mirror of
https://github.com/MervinPraison/PraisonAIUI.git
synced 2026-11-07 05:56:17 -10:00
Compare commits
64
Commits
No files matched your search
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"templateRepo": "MervinPraison/github-automation-template",
|
||||
"stack": "claude",
|
||||
"profile": "docs",
|
||||
"mode": "hub",
|
||||
"hubRef": "v1.1.3",
|
||||
"templateVersion": "1.1.3",
|
||||
"installedAt": "2026-09-07T09:52:44Z"
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
trigger-claude:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'issues' && !contains(toJSON(github.event.issue.labels), 'claude'))
|
||||
github.event_name == 'issues'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
@@ -52,7 +52,8 @@ jobs:
|
||||
});
|
||||
|
||||
const labels = (issue.labels || []).map(l => typeof l === 'string' ? l : l.name);
|
||||
if (!labels.includes('claude')) {
|
||||
const hadClaude = labels.includes('claude');
|
||||
if (!hadClaude) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -60,7 +61,7 @@ jobs:
|
||||
labels: ['claude'],
|
||||
});
|
||||
} else {
|
||||
console.log(`Issue #${issueNumber} already has 'claude' label.`);
|
||||
console.log(`Issue #${issueNumber} already has 'claude' label — dispatching Claude only.`);
|
||||
}
|
||||
|
||||
const body = [
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
name: Auto PR Comment
|
||||
|
||||
# Review chain: CodeRabbit/Qodo/Gemini review first → Copilot → Claude (final)
|
||||
# For human PRs: CodeRabbit/Qodo auto-review → this workflow triggers Copilot → then Claude
|
||||
# For bot PRs: CodeRabbit/Qodo skip bots, so we trigger them explicitly first
|
||||
#
|
||||
# IMPORTANT: @copilot ignores bot comments. All @copilot mentions must use
|
||||
# PAT_TOKEN so the comment posts as MervinPraison (a real user).
|
||||
#
|
||||
# Claude trigger runs IN-BAND (same workflow) to avoid GitHub Actions
|
||||
# blocking bot-triggered workflows with action_required approval gates.
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
@@ -17,793 +8,21 @@ on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
types: [opened, synchronize]
|
||||
schedule:
|
||||
- cron: '30 3,9,15,21 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number for manual FINAL (leave empty to sweep all open PRs)'
|
||||
required: false
|
||||
type: number
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
MAX_POLL_ATTEMPTS: 20
|
||||
POLL_DELAY_MS: 30000
|
||||
CLAUDE_TRIGGER_LOGINS: '["MervinPraison","github-actions[bot]"]'
|
||||
# GH_TOKEN (user PAT) posts as MervinPraison for @copilot; fallback to github.token for @claude
|
||||
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"
|
||||
REVIEW_CHAIN_SKIP_COPILOT: '1'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
# ================================================================
|
||||
# HUMAN PRs: Trigger @copilot AFTER CodeRabbit posts its summary
|
||||
# ================================================================
|
||||
copilot-after-coderabbit:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
github.event.comment.user.login == 'coderabbitai[bot]' &&
|
||||
contains(github.event.comment.body, 'summarize by coderabbit')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
outputs:
|
||||
pr_number: ${{ steps.trigger.outputs.pr_number }}
|
||||
triggered: ${{ steps.trigger.outputs.triggered }}
|
||||
steps:
|
||||
- name: Post Copilot review request (as user via PAT)
|
||||
id: trigger
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
// Check for duplicates — posted by MervinPraison (PAT) or github-actions[bot] (legacy)
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@copilot') &&
|
||||
(c.user.login === 'MervinPraison' || c.user.login === 'github-actions[bot]')
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Copilot already triggered, skipping');
|
||||
core.setOutput('triggered', 'false');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first — incorporate their findings.\n\nReview areas:\n1. **Bloat check**: Are changes minimal and focused? Any unnecessary code or scope creep?\n2. **Security**: Any hardcoded secrets, unsafe eval/exec, missing input validation?\n3. **Performance**: Any module-level heavy imports? Hot-path regressions?\n4. **Tests**: Are tests included? Do they cover the changes adequately?\n5. **Backward compat**: Any public API changes without deprecation?\n6. **Code quality**: DRY violations, naming conventions, error handling?\n7. **Address reviewer feedback**: If Qodo, Coderabbit, or Gemini flagged valid issues, include them in your review\n8. Suggest specific improvements with code examples where possible'
|
||||
});
|
||||
core.setOutput('triggered', 'true');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
|
||||
# Fallback: Trigger @copilot after Qodo review (if Coderabbit doesn't fire)
|
||||
copilot-after-qodo:
|
||||
if: |
|
||||
github.event_name == 'pull_request_review' &&
|
||||
github.event.review.user.login == 'qodo-code-review[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
outputs:
|
||||
pr_number: ${{ steps.trigger.outputs.pr_number }}
|
||||
triggered: ${{ steps.trigger.outputs.triggered }}
|
||||
steps:
|
||||
- name: Post Copilot review request (as user via PAT)
|
||||
id: trigger
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@copilot') &&
|
||||
(c.user.login === 'MervinPraison' || c.user.login === 'github-actions[bot]')
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Copilot already triggered, skipping');
|
||||
core.setOutput('triggered', 'false');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above first.\n\nReview areas:\n1. **Bloat check**: Are changes minimal and focused?\n2. **Security**: Any hardcoded secrets, unsafe eval/exec, missing input validation?\n3. **Performance**: Any module-level heavy imports? Hot-path regressions?\n4. **Tests**: Are tests included? Do they cover the changes adequately?\n5. **Backward compat**: Any public API changes without deprecation?\n6. **Code quality**: DRY violations, naming conventions, error handling?\n7. Suggest specific improvements with code examples where possible'
|
||||
});
|
||||
core.setOutput('triggered', 'true');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
|
||||
# ================================================================
|
||||
# FALLBACK: Claude trigger for cases where Copilot chain fails/skips
|
||||
# Ensures Claude always runs after sufficient review time
|
||||
# ================================================================
|
||||
claude-fallback-timeout:
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'opened'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Wait for review window (15 min)
|
||||
run: sleep 900
|
||||
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
|
||||
core.setOutput('comments_count', comments.data.length.toString());
|
||||
|
||||
const botReviews = comments.data.filter(c =>
|
||||
['coderabbitai[bot]', 'qodo-code-review[bot]', 'gemini-code-assist[bot]',
|
||||
'copilot-swe-agent', 'Copilot', 'greptile-apps[bot]'].some(bot =>
|
||||
c.user.login.toLowerCase().includes(bot.toLowerCase())
|
||||
)
|
||||
);
|
||||
core.setOutput('review_count', botReviews.length.toString());
|
||||
|
||||
- name: Aggregate reviews and trigger Claude
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const comments = await github.rest.issues.listComments({issue_number: pr, owner: context.repo.owner, repo: context.repo.repo, per_page: 100});
|
||||
const reviews = {coderabbit: [], qodo: [], gemini: [], copilot: [], greptile: [], others: []};
|
||||
comments.data.forEach(c => {
|
||||
const login = c.user.login.toLowerCase();
|
||||
const body = c.body.substring(0, 500);
|
||||
if (login.includes('coderabbit')) reviews.coderabbit.push(body);
|
||||
else if (login.includes('qodo')) reviews.qodo.push(body);
|
||||
else if (login.includes('gemini')) reviews.gemini.push(body);
|
||||
else if (login.includes('copilot')) reviews.copilot.push(body);
|
||||
else if (login.includes('greptile')) reviews.greptile.push(body);
|
||||
else if (c.user.type === 'Bot') reviews.others.push(body);
|
||||
});
|
||||
const summaryParts = [];
|
||||
if (reviews.coderabbit.length) summaryParts.push('CodeRabbit: ' + reviews.coderabbit.length + ' comments');
|
||||
if (reviews.qodo.length) summaryParts.push('Qodo: ' + reviews.qodo.length + ' comments');
|
||||
if (reviews.gemini.length) summaryParts.push('Gemini: ' + reviews.gemini.length + ' comments');
|
||||
if (reviews.copilot.length) summaryParts.push('Copilot: ' + reviews.copilot.length + ' comments');
|
||||
if (reviews.greptile.length) summaryParts.push('Greptile: ' + reviews.greptile.length + ' comments');
|
||||
const reviewSummary = summaryParts.length ? summaryParts.join(' | ') : 'No bot reviews detected';
|
||||
const scope = process.env.CLAUDE_UI_SCOPE;
|
||||
const phases = process.env.CLAUDE_FINAL_PHASES_COMPACT;
|
||||
const claudeBody = '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. ' + scope + ' Read ALL prior reviewer comments. ' + reviewSummary + '.\n\n' +
|
||||
'Review Context: ' + comments.data.length + ' total comments, ' +
|
||||
(reviews.coderabbit.length + reviews.qodo.length + reviews.gemini.length + reviews.copilot.length + reviews.greptile.length) + ' bot reviews.\n\n' +
|
||||
phases;
|
||||
await github.rest.issues.createComment({issue_number: pr, owner: context.repo.owner, repo: context.repo.repo, body: claudeBody});
|
||||
console.log('Claude fallback triggered on PR #' + pr + ' with ' + reviewSummary);
|
||||
|
||||
# ================================================================
|
||||
# Claude FINAL review — polls for Copilot's response, then triggers
|
||||
# This runs IN-BAND under the PAT context (MervinPraison), avoiding
|
||||
# the action_required approval gate that blocks bot-triggered workflows.
|
||||
# ================================================================
|
||||
claude-after-copilot:
|
||||
needs: [copilot-after-coderabbit]
|
||||
if: |
|
||||
always() &&
|
||||
needs.copilot-after-coderabbit.result == 'success' &&
|
||||
needs.copilot-after-coderabbit.outputs.triggered == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Wait for Copilot to respond (poll)
|
||||
id: poll
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
MAX_POLL_ATTEMPTS: ${{ env.MAX_POLL_ATTEMPTS }}
|
||||
POLL_DELAY_MS: ${{ env.POLL_DELAY_MS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// Poll for Copilot's response — check every 30s for up to 10 minutes
|
||||
const maxAttempts = parseInt(process.env.MAX_POLL_ATTEMPTS, 10);
|
||||
const delayMs = parseInt(process.env.POLL_DELAY_MS, 10);
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
console.log(`Poll attempt ${i + 1}/${maxAttempts}...`);
|
||||
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner,
|
||||
repo,
|
||||
per_page: 50,
|
||||
sort: 'created',
|
||||
direction: 'desc'
|
||||
});
|
||||
|
||||
// Look for Copilot's response (copilot-swe-agent or Copilot)
|
||||
const copilotResponse = comments.data.find(c =>
|
||||
(c.user.login === 'copilot-swe-agent' ||
|
||||
c.user.login === 'Copilot' ||
|
||||
c.user.login.toLowerCase().includes('copilot')) &&
|
||||
c.user.type === 'Bot'
|
||||
);
|
||||
|
||||
if (copilotResponse) {
|
||||
console.log(`Copilot responded at ${copilotResponse.created_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
// Also check PR reviews for Copilot
|
||||
const reviews = await github.rest.pulls.listReviews({
|
||||
pull_number: pr,
|
||||
owner,
|
||||
repo
|
||||
});
|
||||
|
||||
const copilotReview = reviews.data.find(r =>
|
||||
r.user.login.toLowerCase().includes('copilot')
|
||||
);
|
||||
|
||||
if (copilotReview) {
|
||||
console.log(`Copilot review submitted at ${copilotReview.submitted_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
if (i < maxAttempts - 1) {
|
||||
console.log(`Copilot hasn't responded yet, waiting ${delayMs/1000}s...`);
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Copilot did not respond within timeout, triggering Claude anyway');
|
||||
core.setOutput('copilot_responded', 'timeout');
|
||||
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
|
||||
|
||||
- name: Trigger Claude final review
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const scope = process.env.CLAUDE_UI_SCOPE;
|
||||
const phases = process.env.CLAUDE_FINAL_PHASES_DETAILED;
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. ' + scope + ' Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n' + phases
|
||||
});
|
||||
console.log(`Claude triggered on PR #${pr}`);
|
||||
|
||||
- name: Sync pipeline status labels
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
await ps.ensurePipelineLabels(github, context.repo.owner, context.repo.repo, core);
|
||||
await ps.syncPipelineLabels(github, context.repo.owner, context.repo.repo, pr, core);
|
||||
|
||||
# ================================================================
|
||||
# Claude after Qodo fallback path (same polling approach)
|
||||
# ================================================================
|
||||
claude-after-copilot-qodo:
|
||||
needs: [copilot-after-qodo]
|
||||
if: |
|
||||
always() &&
|
||||
needs.copilot-after-qodo.result == 'success' &&
|
||||
needs.copilot-after-qodo.outputs.triggered == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Wait for Copilot to respond (poll)
|
||||
id: poll
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
MAX_POLL_ATTEMPTS: ${{ env.MAX_POLL_ATTEMPTS }}
|
||||
POLL_DELAY_MS: ${{ env.POLL_DELAY_MS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const maxAttempts = parseInt(process.env.MAX_POLL_ATTEMPTS, 10);
|
||||
const delayMs = parseInt(process.env.POLL_DELAY_MS, 10);
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
console.log(`Poll attempt ${i + 1}/${maxAttempts}...`);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr, owner, repo,
|
||||
per_page: 50, sort: 'created', direction: 'desc'
|
||||
});
|
||||
const copilotResponse = comments.data.find(c =>
|
||||
(c.user.login === 'copilot-swe-agent' ||
|
||||
c.user.login === 'Copilot' ||
|
||||
c.user.login.toLowerCase().includes('copilot')) &&
|
||||
c.user.type === 'Bot'
|
||||
);
|
||||
if (copilotResponse) {
|
||||
console.log(`Copilot responded at ${copilotResponse.created_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
const reviews = await github.rest.pulls.listReviews({
|
||||
pull_number: pr, owner, repo
|
||||
});
|
||||
const copilotReview = reviews.data.find(r =>
|
||||
r.user.login.toLowerCase().includes('copilot')
|
||||
);
|
||||
if (copilotReview) {
|
||||
console.log(`Copilot review submitted at ${copilotReview.submitted_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
if (i < maxAttempts - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
console.log('Copilot did not respond within timeout, triggering Claude anyway');
|
||||
core.setOutput('copilot_responded', 'timeout');
|
||||
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
|
||||
|
||||
- name: Trigger Claude final review
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const scope = process.env.CLAUDE_UI_SCOPE;
|
||||
const phases = process.env.CLAUDE_FINAL_PHASES_DETAILED;
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. ' + scope + ' Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n' + phases
|
||||
});
|
||||
console.log(`Claude triggered on PR #${pr}`);
|
||||
|
||||
# ================================================================
|
||||
# Claude after Gemini — restores the trigger lost from the deleted
|
||||
# chain-claude-after-copilot.yml workflow.
|
||||
# Fires when Gemini posts a review comment on a PR or issue.
|
||||
# ================================================================
|
||||
claude-after-gemini:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.user.login == 'gemini-code-assist[bot]' ||
|
||||
contains(github.event.comment.body, 'Review completed by Gemini CLI') ||
|
||||
contains(github.event.comment.body, 'The fix is ready for review. Please test')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
|
||||
|
||||
- name: Post Claude analysis request (Lead Engineer)
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const scope = process.env.CLAUDE_UI_SCOPE;
|
||||
const phases = process.env.CLAUDE_FINAL_PHASES_DETAILED;
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude You are the Lead Engineer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. ' + scope + ' Read ALL analysis and reviews above carefully (Gemini, CodeRabbit, Qodo, Copilot, etc).\n\n' + phases
|
||||
});
|
||||
|
||||
# ================================================================
|
||||
# Recovery: re-post FINAL @claude after push or when chain missed
|
||||
# ================================================================
|
||||
claude-review-recovery:
|
||||
if: |
|
||||
github.event_name == 'pull_request_target' &&
|
||||
github.event.action == 'synchronize'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Re-post FINAL Claude review when stale or missing
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js'));
|
||||
const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js'));
|
||||
const pr = context.payload.pull_request.number;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
const comments = await mergeGate.listAllComments(github, owner, repo, pr);
|
||||
const headPushedAt = (await mergeGate.getHeadCommitDate(github, owner, repo, pr))
|
||||
|| context.payload.pull_request.updated_at;
|
||||
const headPusher = context.payload.sender?.login || null;
|
||||
|
||||
const hasFinal = mergeGate.hasFinalClaudeReviewTrigger(comments);
|
||||
const isStale = mergeGate.isStaleFinalAfterPush(comments, headPushedAt);
|
||||
|
||||
if (mergeGate.shouldSkipFinalRecovery(comments, headPushedAt)) {
|
||||
core.info('Recent @claude within 35min and FINAL not stale — skip recovery');
|
||||
return;
|
||||
}
|
||||
|
||||
if (await mergeGate.hasInProgressClaudeAssistant(github, owner, repo, pr)) {
|
||||
core.info('Claude workflow in progress — skip recovery');
|
||||
return;
|
||||
}
|
||||
|
||||
const finalBody = mergeGate.FINAL_CLAUDE_REVIEW_BODY;
|
||||
const staleOpts = { allowStaleRepost: true, skipCopilot: true, optionalWaitMs: 0 };
|
||||
const missingOpts = { allowCopilotTimeout: true, optionalWaitMs: 0 };
|
||||
|
||||
if (hasFinal && isStale) {
|
||||
const gate = mergeGate.shouldSkipStaleFinalRecovery(comments, headPushedAt, headPusher);
|
||||
if (gate.skip) {
|
||||
core.info(`Skip stale-FINAL recovery: ${gate.reason}`);
|
||||
return;
|
||||
}
|
||||
const result = await chain.maybeTriggerClaudeFinal(
|
||||
github, owner, repo, pr, finalBody, core, staleOpts
|
||||
);
|
||||
if (result.posted) core.info(`Posted stale-FINAL recovery @claude on PR #${pr}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasFinal) {
|
||||
core.info('FINAL Claude review current on HEAD — skip recovery');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await chain.maybeTriggerClaudeFinal(
|
||||
github, owner, repo, pr, finalBody, core, missingOpts
|
||||
);
|
||||
if (result.posted) core.info(`Posted missing FINAL Claude review on PR #${pr}`);
|
||||
|
||||
- name: Sync pipeline status labels
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
const pr = context.payload.pull_request.number;
|
||||
await ps.ensurePipelineLabels(github, context.repo.owner, context.repo.repo, core);
|
||||
await ps.syncPipelineLabels(github, context.repo.owner, context.repo.repo, pr, core);
|
||||
|
||||
# Scheduled + manual: post missing or stale FINAL @claude on all open PRs
|
||||
post-missing-claude-final:
|
||||
if: |
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && !inputs.pr_number)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Post missing/stale FINAL @claude on open PRs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js'));
|
||||
const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js'));
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const prs = await github.paginate(github.rest.pulls.list, {
|
||||
owner, repo, state: 'open', per_page: 100,
|
||||
});
|
||||
let posted = 0;
|
||||
for (const pr of prs) {
|
||||
if (pr.draft) continue;
|
||||
const comments = await mergeGate.listAllComments(github, owner, repo, pr.number);
|
||||
const headPushedAt = (await mergeGate.getHeadCommitDate(github, owner, repo, pr.number)) || pr.updated_at;
|
||||
const hasFinal = mergeGate.hasFinalClaudeReviewTrigger(comments);
|
||||
const isStale = mergeGate.isStaleFinalAfterPush(comments, headPushedAt);
|
||||
|
||||
if (hasFinal && !isStale) continue;
|
||||
if (mergeGate.hasRecentClaudeTrigger(comments, 10)) {
|
||||
core.info(`PR #${pr.number}: @claude within 10min — skip`);
|
||||
continue;
|
||||
}
|
||||
if (await mergeGate.hasInProgressClaudeAssistant(github, owner, repo, pr.number)) {
|
||||
core.info(`PR #${pr.number}: Claude in progress — skip`);
|
||||
continue;
|
||||
}
|
||||
if (hasFinal && isStale) {
|
||||
const gate = mergeGate.shouldSkipStaleFinalRecovery(comments, headPushedAt);
|
||||
if (gate.skip) {
|
||||
core.info(`PR #${pr.number}: skip stale — ${gate.reason}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const opts = hasFinal && isStale
|
||||
? { allowStaleRepost: true, skipCopilot: true, optionalWaitMs: 0 }
|
||||
: { allowCopilotTimeout: true, optionalWaitMs: 0 };
|
||||
const result = await chain.maybeTriggerClaudeFinal(
|
||||
github, owner, repo, pr.number,
|
||||
mergeGate.FINAL_CLAUDE_REVIEW_BODY, core, opts
|
||||
);
|
||||
if (result.posted) {
|
||||
posted++;
|
||||
core.info(`Posted Claude FINAL on PR #${pr.number}`);
|
||||
}
|
||||
await ps.syncPipelineLabels(github, owner, repo, pr.number, core);
|
||||
if (posted >= 10) break;
|
||||
}
|
||||
core.info(`Missing/stale Claude FINAL sweep complete (${posted} posted)`);
|
||||
|
||||
# ================================================================
|
||||
# BOT PRs: CodeRabbit → Claude directly (skip Copilot when no user PAT)
|
||||
# Copilot ignores github-actions[bot] comments; bot PRs use this path.
|
||||
# ================================================================
|
||||
claude-after-coderabbit-bot-pr:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
github.event.comment.user.login == 'coderabbitai[bot]' &&
|
||||
contains(github.event.comment.body, 'summarize by coderabbit')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Trigger Claude final review on bot PR
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = context.issue.number;
|
||||
const { data: pull } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr,
|
||||
});
|
||||
const author = pull.user.login;
|
||||
const isBot = author === 'github-actions[bot]' ||
|
||||
author === 'praisonai-triage-agent[bot]' ||
|
||||
pull.user.type === 'Bot';
|
||||
if (!isBot) {
|
||||
console.log('Human PR — Copilot chain handles Claude trigger');
|
||||
return;
|
||||
}
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Claude already triggered on PR #' + pr);
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude You are the FINAL architecture reviewer on this bot-authored PR. Push fixes directly to THIS branch (do NOT open a new PR). ' + process.env.CLAUDE_UI_SCOPE + ' Read ALL CodeRabbit/Qodo comments above.\n\n' + process.env.CLAUDE_FINAL_PHASES_COMPACT
|
||||
});
|
||||
console.log('Claude final review triggered on bot PR #' + pr);
|
||||
|
||||
# ================================================================
|
||||
# Manual: trigger Claude final review on any PR
|
||||
# ================================================================
|
||||
claude-manual-dispatch:
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.pr_number
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Trigger Claude final review
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ inputs.pr_number }}
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
});
|
||||
const alreadyPosted = comments.data.some(c =>
|
||||
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Claude already triggered on PR #' + pr);
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: pr,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. ' + process.env.CLAUDE_UI_SCOPE + ' Read all prior reviewer comments.\n\n' + process.env.CLAUDE_FINAL_PHASES_COMPACT
|
||||
});
|
||||
console.log('Claude manually triggered on PR #' + pr);
|
||||
|
||||
# ================================================================
|
||||
# BOT PRs: kick CodeRabbit + Qodo
|
||||
# ================================================================
|
||||
bot-pr-trigger-reviews:
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action == 'opened' &&
|
||||
(
|
||||
github.event.pull_request.user.login == 'github-actions[bot]' ||
|
||||
github.event.pull_request.user.login == 'praisonai-triage-agent[bot]' ||
|
||||
github.event.pull_request.user.type == 'Bot'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Trigger CodeRabbit and Qodo reviews
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/bot-pr-review-chain.js'));
|
||||
const pr = context.payload.pull_request.number;
|
||||
await chain.kickReviewChain(github, context.repo.owner, context.repo.repo, pr, core);
|
||||
|
||||
- name: Sync pipeline status labels
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
const pr = context.payload.pull_request.number;
|
||||
await ps.ensurePipelineLabels(github, context.repo.owner, context.repo.repo, core);
|
||||
await ps.syncPipelineLabels(github, context.repo.owner, context.repo.repo, pr, core);
|
||||
review-chain:
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Bot PR Recovery
|
||||
|
||||
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:
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
pr_number: ${{ github.event.inputs.pr_number || '' }}
|
||||
@@ -1,7 +1,5 @@
|
||||
name: Auto CI Failure → Claude
|
||||
|
||||
# Detects internal PRs with failing CI and posts @claude with extracted failure details.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
@@ -11,7 +9,6 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'Optional PR number to process (default: scan blocked PRs)'
|
||||
required: false
|
||||
type: string
|
||||
schedule:
|
||||
@@ -21,10 +18,6 @@ concurrency:
|
||||
group: ci-failure-scan-${{ github.repository }}-${{ github.event.pull_request.number || github.event.workflow_run.id || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# PAT posts as MervinPraison so issue_comment triggers claude.yml (GITHUB_TOKEN cannot chain workflows)
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
issues: write
|
||||
@@ -32,71 +25,8 @@ permissions:
|
||||
|
||||
jobs:
|
||||
detect-and-trigger:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Detect CI failures and trigger Claude
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const ciFix = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/ci-failure-claude.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const baseRepo = `${owner}/${repo}`;
|
||||
|
||||
async function processPR(prNumber) {
|
||||
return ciFix.maybeTriggerCiFixClaude(github, owner, repo, prNumber, core);
|
||||
}
|
||||
|
||||
let results = [];
|
||||
|
||||
if (context.eventName === 'workflow_run') {
|
||||
const wr = context.payload.workflow_run;
|
||||
const result = await ciFix.processWorkflowRunFailure(
|
||||
github, owner, repo, wr, core
|
||||
);
|
||||
results.push({ event: 'workflow_run', workflow: wr.name, ...result });
|
||||
} else if (context.eventName === 'pull_request') {
|
||||
results.push({
|
||||
pr: context.payload.pull_request.number,
|
||||
...(await processPR(context.payload.pull_request.number)),
|
||||
});
|
||||
} else {
|
||||
let prNumbers = [];
|
||||
if (context.eventName === 'workflow_dispatch' && context.payload.inputs?.pr_number) {
|
||||
prNumbers = [Number(context.payload.inputs.pr_number)];
|
||||
} else {
|
||||
const prs = await github.paginate(github.rest.pulls.list, {
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
});
|
||||
for (const pr of prs) {
|
||||
if (pr.draft) continue;
|
||||
const headRepo = pr.head?.repo?.full_name;
|
||||
if (headRepo && headRepo !== baseRepo) continue;
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
});
|
||||
const labels = issue.labels.map((l) => l.name);
|
||||
if (labels.includes('pipeline/blocked:ci') || labels.includes(ciFix.CI_FIX_LABEL)) {
|
||||
prNumbers.push(pr.number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const num of prNumbers) {
|
||||
results.push({ pr: num, ...(await processPR(num)) });
|
||||
}
|
||||
}
|
||||
|
||||
core.summary.addRaw('```json\n' + JSON.stringify(results, null, 2) + '\n```');
|
||||
await core.summary.write();
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
pr_number: ${{ github.event.inputs.pr_number || '' }}
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
python:
|
||||
@@ -65,3 +66,4 @@ jobs:
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./site
|
||||
force_orphan: true
|
||||
@@ -1,46 +1,30 @@
|
||||
name: Claude PR Merge Gate
|
||||
|
||||
# Read-only Claude assesses merge readiness; GH_TOKEN merges (no @claude, no gh pr review).
|
||||
# Does not overlap with merge-conflict-claude.yml or auto-pr-comment @claude chains.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Scan for merge-ready PRs every 10 minutes (assess runs only when candidates exist).
|
||||
- cron: '*/10 * * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'Optional PR number (scan all open internal PRs if empty)'
|
||||
required: false
|
||||
type: string
|
||||
merge_method:
|
||||
description: 'Merge method'
|
||||
required: false
|
||||
type: choice
|
||||
default: merge
|
||||
options:
|
||||
- merge
|
||||
- squash
|
||||
- rebase
|
||||
options: [merge, squash, rebase]
|
||||
workflow_run:
|
||||
workflows:
|
||||
- Claude Assistant
|
||||
- CI
|
||||
types:
|
||||
- completed
|
||||
types: [completed]
|
||||
repository_dispatch:
|
||||
types:
|
||||
- claude-merge-gate
|
||||
types: [claude-merge-gate]
|
||||
|
||||
concurrency:
|
||||
group: claude-merge-gate-${{ github.repository }}-${{ github.event.inputs.pr_number || github.event.client_payload.pr_number || github.event.workflow_run.id || 'scan' }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
MAX_CANDIDATES: 3
|
||||
DISPATCH_PR_NUMBER: ${{ github.event.inputs.pr_number || github.event.client_payload.pr_number || '' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
@@ -59,7 +43,9 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: MervinPraison/github-automation-template/.github/actions/[email protected]
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
@@ -67,472 +53,37 @@ jobs:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Dispatch merge gate when PR pipeline is ready
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js'));
|
||||
const mergeGate = require(path.join(process.env.HUB_SCRIPTS, 'merge-gate.js'));
|
||||
const wr = context.payload.workflow_run;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
if (wr.conclusion !== 'success') {
|
||||
core.info(`Skip dispatch: ${wr.name} conclusion=${wr.conclusion}`);
|
||||
return;
|
||||
}
|
||||
if (wr.conclusion !== 'success') return;
|
||||
const allowed = ['Claude Assistant', 'CI'];
|
||||
if (!allowed.includes(wr.name)) {
|
||||
core.info(`Skip dispatch: workflow ${wr.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allowed.includes(wr.name)) return;
|
||||
const prNumber = await mergeGate.resolvePrNumberFromWorkflowRun(github, owner, repo, wr);
|
||||
if (!prNumber) {
|
||||
core.info('Skip dispatch: could not resolve PR from workflow run');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prNumber) return;
|
||||
const fromClaude = wr.name === 'Claude Assistant';
|
||||
const check = await mergeGate.evaluatePipelineQuiescent(
|
||||
github, owner, repo, prNumber, core, {
|
||||
skipGlobalClaudeRunCheck: fromClaude,
|
||||
skipRecentClaudeCooldown: fromClaude,
|
||||
}
|
||||
);
|
||||
if (!check.ready) {
|
||||
core.info(`PR #${prNumber} not ready: ${check.reasons.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const check = await mergeGate.evaluatePipelineQuiescent(github, owner, repo, prNumber, core, {
|
||||
skipGlobalClaudeRunCheck: fromClaude,
|
||||
skipRecentClaudeCooldown: fromClaude,
|
||||
});
|
||||
if (!check.ready) return;
|
||||
await github.rest.repos.createDispatchEvent({
|
||||
owner,
|
||||
repo,
|
||||
event_type: 'claude-merge-gate',
|
||||
owner, repo, event_type: 'claude-merge-gate',
|
||||
client_payload: { pr_number: prNumber },
|
||||
});
|
||||
core.info(`Dispatched merge gate for PR #${prNumber} after ${wr.name}`);
|
||||
|
||||
scan-candidates:
|
||||
merge-gate:
|
||||
if: github.event_name != 'workflow_run'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: write
|
||||
actions: read
|
||||
id-token: write
|
||||
outputs:
|
||||
matrix: ${{ steps.scan.outputs.matrix }}
|
||||
has_candidates: ${{ steps.scan.outputs.has_candidates }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Scan open PRs for merge gate eligibility
|
||||
id: scan
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js'));
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const maxCandidates = parseInt(process.env.MAX_CANDIDATES || '5', 10);
|
||||
const inputPr = String(process.env.DISPATCH_PR_NUMBER || '').trim();
|
||||
|
||||
const candidates = [];
|
||||
const skipped = [];
|
||||
|
||||
if (inputPr) {
|
||||
const n = parseInt(inputPr, 10);
|
||||
if (Number.isNaN(n)) {
|
||||
throw new Error(`Invalid pr_number input: ${inputPr}`);
|
||||
}
|
||||
const result = await mergeGate.evaluatePipelineQuiescent(github, owner, repo, n, core);
|
||||
if (result.ready) {
|
||||
candidates.push({ pr_number: n, head_sha: result.headSha });
|
||||
} else {
|
||||
skipped.push({ pr: n, reasons: result.reasons });
|
||||
}
|
||||
} else {
|
||||
const prNumbers = await mergeGate.listPrNumbersForMergeGateScan(
|
||||
github, owner, repo, core
|
||||
);
|
||||
const selected = await mergeGate.selectMergeGateCandidates(
|
||||
github, owner, repo, prNumbers, maxCandidates, core
|
||||
);
|
||||
candidates.push(...selected.candidates);
|
||||
skipped.push(...selected.skipped);
|
||||
}
|
||||
|
||||
core.info('Skipped: ' + JSON.stringify(skipped));
|
||||
core.setOutput('matrix', JSON.stringify({ include: candidates }));
|
||||
core.setOutput('has_candidates', candidates.length > 0 ? 'true' : 'false');
|
||||
|
||||
core.summary.addRaw('## Merge gate scan\n');
|
||||
core.summary.addRaw(`Candidates: ${candidates.length}\n`);
|
||||
core.summary.addRaw('```json\n' + JSON.stringify({ candidates, skipped }, null, 2) + '\n```');
|
||||
await core.summary.write();
|
||||
|
||||
if (inputPr && skipped.length === 1 && skipped[0].pr === parseInt(inputPr, 10)) {
|
||||
const reasons = skipped[0].reasons;
|
||||
const body = [
|
||||
'**Merge gate scan** — not eligible for auto-merge.',
|
||||
'',
|
||||
...reasons.map((r) => `- \`${r}\``),
|
||||
'',
|
||||
'**Actions:** wait for CI and the Claude review chain, or add label `needs-manual-review` and merge manually.',
|
||||
'**Opt out:** label `no-auto-merge`.',
|
||||
].join('\n');
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: skipped[0].pr, body,
|
||||
});
|
||||
} else if (inputPr && candidates.length === 1) {
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: candidates[0].pr_number,
|
||||
body: '**Merge gate scan** — eligible for assessment. Claude merge gate will assess and may auto-merge if `MERGE_GATE_VERDICT: APPROVE`.',
|
||||
});
|
||||
}
|
||||
|
||||
await ps.syncOpenPullRequests(github, owner, repo, { maxPrs: 10 }, core);
|
||||
|
||||
claude-assess:
|
||||
needs: scan-candidates
|
||||
if: needs.scan-candidates.outputs.has_candidates == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.scan-candidates.outputs.matrix) }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: write
|
||||
actions: read
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Mark merge gate active
|
||||
id: mark_active
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
core.setOutput('started_at', new Date().toISOString());
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ matrix.pr_number }},
|
||||
labels: ['claude-merge-gate-active'],
|
||||
});
|
||||
|
||||
- name: Stash merge gate helpers
|
||||
run: |
|
||||
# Keep scripts together so require('./gate-config.js') works from /tmp
|
||||
mkdir -p /tmp/merge-gate-scripts /tmp/gh-actions
|
||||
cp .github/scripts/merge-gate.js .github/scripts/gate-config.js /tmp/merge-gate-scripts/
|
||||
cp -R .github/actions/claude-code-action /tmp/gh-actions/
|
||||
|
||||
- name: Fetch PR branch
|
||||
env:
|
||||
PR_NUMBER: ${{ matrix.pr_number }}
|
||||
run: |
|
||||
git fetch origin "pull/${PR_NUMBER}/head:pr-${PR_NUMBER}"
|
||||
git checkout "pr-${PR_NUMBER}"
|
||||
mkdir -p .github/actions
|
||||
rm -rf .github/actions/claude-code-action
|
||||
cp -R /tmp/gh-actions/claude-code-action .github/actions/
|
||||
|
||||
- name: Claude merge gate assessment (read-only)
|
||||
id: assess
|
||||
uses: ./.github/actions/claude-code-action
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
CLAUDE_CODE_SUBAGENT_MODEL: inherit
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: claude-sonnet-4-6
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
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 }}.
|
||||
READ-ONLY: do not modify code, commit, push, rebase, or create branches.
|
||||
|
||||
CRITICAL OUTPUT RULE:
|
||||
Post exactly one PR comment via GitHub MCP containing either:
|
||||
MERGE_GATE_VERDICT: APPROVE
|
||||
or
|
||||
MERGE_GATE_VERDICT: BLOCK
|
||||
followed by a short rationale on the next lines.
|
||||
|
||||
NEVER include @claude in any comment. NEVER mention merge conflict resolution.
|
||||
|
||||
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.
|
||||
|
||||
Do not approve or merge via GitHub UI/API — only post the verdict comment.
|
||||
disallowed_tools: |
|
||||
Bash
|
||||
Edit
|
||||
Write
|
||||
Replace
|
||||
allowed_tools: |
|
||||
View
|
||||
GlobTool
|
||||
GrepTool
|
||||
Read
|
||||
timeout_minutes: 15
|
||||
|
||||
- name: Post fallback BLOCK if Opus did not comment
|
||||
id: fallback_verdict
|
||||
if: steps.assess.outcome != 'success'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const mergeGate = require('/tmp/merge-gate-scripts/merge-gate.js');
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const prNumber = ${{ matrix.pr_number }};
|
||||
|
||||
const ctx = await mergeGate.loadPrContext(github, owner, repo, prNumber);
|
||||
const minCreatedAt = new Date(Date.now() - 25 * 60 * 1000).toISOString();
|
||||
const existing = mergeGate.findMergeGateVerdict(
|
||||
ctx.comments, minCreatedAt, ctx.headPushedAt, { excludeAutomatedFallback: true }
|
||||
);
|
||||
if (existing) {
|
||||
core.info(`Opus verdict already posted: ${existing}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const check = await mergeGate.evaluatePipelineQuiescent(
|
||||
github, owner, repo, prNumber, core, { forMergeStep: true }
|
||||
);
|
||||
const lines = [
|
||||
'MERGE_GATE_VERDICT: BLOCK',
|
||||
'',
|
||||
'Automated fallback — Opus merge gate assessment did not complete.',
|
||||
];
|
||||
if (check.ready) {
|
||||
lines.push('Deterministic gates passed, but Opus MERGE_GATE_VERDICT is required before merge.');
|
||||
} else {
|
||||
lines.push('Blockers:', ...check.reasons.map((r) => `- ${r}`));
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: prNumber,
|
||||
body: lines.join('\n'),
|
||||
});
|
||||
core.info('Posted fallback BLOCK — Opus verdict required');
|
||||
|
||||
- name: Require Opus MERGE_GATE_VERDICT
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const mergeGate = require('/tmp/merge-gate-scripts/merge-gate.js');
|
||||
const comments = await mergeGate.listAllComments(
|
||||
github, context.repo.owner, context.repo.repo, ${{ matrix.pr_number }}
|
||||
);
|
||||
const ctx = await mergeGate.loadPrContext(
|
||||
github, context.repo.owner, context.repo.repo, ${{ matrix.pr_number }}
|
||||
);
|
||||
const minCreatedAt = new Date(Date.now() - 25 * 60 * 1000).toISOString();
|
||||
const verdict = mergeGate.findMergeGateVerdict(
|
||||
comments, minCreatedAt, ctx.headPushedAt, { excludeAutomatedFallback: true }
|
||||
);
|
||||
if (!verdict) {
|
||||
core.setFailed(
|
||||
'Opus merge gate assessment must post MERGE_GATE_VERDICT on this PR (automated fallback APPROVE is not accepted).'
|
||||
);
|
||||
return;
|
||||
}
|
||||
core.info(`Opus merge gate verdict: ${verdict}`);
|
||||
core.setOutput('verdict', verdict);
|
||||
|
||||
- name: Remove merge gate active label
|
||||
if: always()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const prNumber = ${{ matrix.pr_number }};
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'claude-merge-gate-active',
|
||||
});
|
||||
core.info(`Removed claude-merge-gate-active from PR #${prNumber}`);
|
||||
} catch (e) {
|
||||
core.info(`Label claude-merge-gate-active already removed or missing on PR #${prNumber}`);
|
||||
}
|
||||
|
||||
- name: Fail job if assessment failed
|
||||
if: steps.assess.outcome == 'failure'
|
||||
run: exit 1
|
||||
|
||||
merge-only:
|
||||
needs: [scan-candidates, claude-assess]
|
||||
if: |
|
||||
always() &&
|
||||
needs.scan-candidates.outputs.has_candidates == 'true' &&
|
||||
needs.claude-assess.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.scan-candidates.outputs.matrix) }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
actions: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Re-check gates and merge
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
MERGE_METHOD: ${{ github.event.inputs.merge_method || 'merge' }}
|
||||
SCAN_HEAD_SHA: ${{ matrix.head_sha }}
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const prNumber = ${{ matrix.pr_number }};
|
||||
const scanHeadSha = process.env.SCAN_HEAD_SHA;
|
||||
|
||||
const removeLabels = async () => {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner, repo, issue_number: prNumber, name: 'claude-merge-gate-active',
|
||||
});
|
||||
} catch (e) {
|
||||
core.info('Label claude-merge-gate-active already removed or missing');
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const ctx = await mergeGate.loadPrContext(github, owner, repo, prNumber);
|
||||
const comments = ctx.comments;
|
||||
const verdictMin = new Date(Date.now() - 25 * 60 * 1000).toISOString();
|
||||
const verdict = mergeGate.findMergeGateVerdict(
|
||||
comments, verdictMin, ctx.headPushedAt, { excludeAutomatedFallback: true }
|
||||
);
|
||||
if (verdict !== 'APPROVE') {
|
||||
core.info(`Skip merge: verdict=${verdict || 'NONE'}`);
|
||||
await removeLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
const check = await mergeGate.evaluatePipelineQuiescent(
|
||||
github, owner, repo, prNumber, core, { forMergeStep: true }
|
||||
);
|
||||
if (!check.ready) {
|
||||
core.info(`Skip merge: ${check.reasons.join(', ')}`);
|
||||
await removeLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
if (check.headSha !== scanHeadSha) {
|
||||
core.info(`Skip merge: HEAD moved ${scanHeadSha.slice(0, 7)} → ${check.headSha.slice(0, 7)}`);
|
||||
await removeLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
const method = process.env.MERGE_METHOD || 'merge';
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
let merged = false;
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
await github.rest.pulls.merge({
|
||||
owner, repo, pull_number: prNumber, merge_method: method,
|
||||
});
|
||||
merged = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
const msg = (e.message || '').toLowerCase();
|
||||
if (attempt < 3 && msg.includes('base branch was modified')) {
|
||||
core.info(`Merge attempt ${attempt} failed (base moved), retrying in 8s...`);
|
||||
await sleep(8000);
|
||||
const recheck = await mergeGate.evaluatePipelineQuiescent(
|
||||
github, owner, repo, prNumber, core, { forMergeStep: true }
|
||||
);
|
||||
if (!recheck.ready) {
|
||||
core.info(`Skip merge retry: ${recheck.reasons.join(', ')}`);
|
||||
await removeLabels();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
core.info('Skip merge: exhausted retries');
|
||||
await removeLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner, repo, issue_number: prNumber, labels: ['auto-merged-by-gate'],
|
||||
});
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number: prNumber,
|
||||
body: [
|
||||
'Merged by **Claude PR merge gate** (`claude-merge-gate.yml`).',
|
||||
`Verdict: MERGE_GATE_VERDICT: APPROVE`,
|
||||
`SHA: \`${check.headSha.slice(0, 7)}\``,
|
||||
`Method: ${method}`,
|
||||
].join('\n'),
|
||||
});
|
||||
|
||||
core.info(`Merged PR #${prNumber}`);
|
||||
} finally {
|
||||
await removeLabels();
|
||||
}
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
pr_number: ${{ github.event.inputs.pr_number || github.event.client_payload.pr_number || '' }}
|
||||
merge_method: ${{ github.event.inputs.merge_method || 'merge' }}
|
||||
@@ -1,543 +1,18 @@
|
||||
name: Claude Assistant
|
||||
|
||||
# Triggers:
|
||||
# - issues:opened → lightweight triage (labels + auto-claude for owner)
|
||||
# - issues:labeled → full implementation when `claude` label applied
|
||||
# - issues:assigned → full implementation when issue assigned to Claude
|
||||
# - issue_comment → full implementation when @claude mentioned (issues + PRs)
|
||||
# - pull_request_review → full implementation when @claude mentioned in review
|
||||
# - pull_request_review_comment → full implementation when @claude mentioned inline
|
||||
#
|
||||
# Escalate an issue with the `claude` label OR an @claude comment — not both.
|
||||
# If both fire within 3 minutes, the comment trigger is skipped (dedupe job).
|
||||
#
|
||||
# Focus: CLAUDE only. Copilot and CodeRabbit are GitHub Apps that react to PRs
|
||||
# automatically — no workflow file needed. auto-pr-comment.yml orchestrates the
|
||||
# CodeRabbit → Copilot → Claude review chain for PRs.
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, labeled, assigned]
|
||||
types: [assigned, labeled, opened]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'Issue number for Claude implementation'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
# ================================================================
|
||||
# Job 1 — Issue triage (runs on ANY new issue, including external users)
|
||||
# Posts welcome comment + triage labels. Auto-adds `claude` for owner issues.
|
||||
# ================================================================
|
||||
issue-triage:
|
||||
if: |
|
||||
github.event_name == 'issues' &&
|
||||
github.event.action == 'opened' &&
|
||||
!contains(toJSON(github.event.issue.labels), 'claude')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Resolve GitHub token
|
||||
id: github-token
|
||||
run: |
|
||||
if [ -n "${{ steps.app-token.outputs.token }}" ]; then
|
||||
echo "token=${{ steps.app-token.outputs.token }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "token=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Triage and acknowledge issue
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.github-token.outputs.token }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const title = (issue.title || '').toLowerCase();
|
||||
const body = (issue.body || '').toLowerCase();
|
||||
const content = title + ' ' + body;
|
||||
const isOwner = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(
|
||||
issue.author_association
|
||||
);
|
||||
|
||||
const labels = [];
|
||||
|
||||
if (content.includes('feature request') || content.includes('[feature')) {
|
||||
labels.push('enhancement');
|
||||
} else if (content.includes('bug') || content.includes('error') || content.includes('crash') || content.includes('traceback')) {
|
||||
labels.push('bug');
|
||||
} else if (content.includes('question') || content.includes('how do i') || content.includes('how to')) {
|
||||
labels.push('question');
|
||||
}
|
||||
|
||||
if (content.includes('security') || content.includes('cve') || content.includes('vulnerability')) {
|
||||
labels.push('security');
|
||||
}
|
||||
if (content.includes('performance') || content.includes('slow') || content.includes('memory leak')) {
|
||||
labels.push('performance');
|
||||
}
|
||||
if (content.includes('documentation') || content.includes('docs')) {
|
||||
labels.push('documentation');
|
||||
}
|
||||
if (content.includes('typescript') || content.includes('javascript') || content.includes('npm')) {
|
||||
labels.push('javascript');
|
||||
}
|
||||
if (content.includes('frontend') || content.includes('react') || content.includes('vite')) {
|
||||
labels.push('frontend');
|
||||
}
|
||||
if (content.includes('example') || content.includes('yaml')) {
|
||||
labels.push('examples');
|
||||
}
|
||||
|
||||
if (labels.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels
|
||||
});
|
||||
}
|
||||
|
||||
if (!isOwner) {
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: [
|
||||
`👋 Thanks for opening this issue, @${issue.user.login}!`,
|
||||
'',
|
||||
'A maintainer will review this shortly. In the meantime:',
|
||||
'- Make sure you\'ve included steps to reproduce (for bugs)',
|
||||
'- Check [existing issues](https://github.com/MervinPraison/PraisonAIUI/issues) for duplicates',
|
||||
'- Review [AGENTS.md](https://github.com/MervinPraison/PraisonAIUI/blob/main/AGENTS.md) for project conventions',
|
||||
'',
|
||||
'_A maintainer can trigger deeper analysis by commenting `@claude` on this issue._'
|
||||
].join('\n')
|
||||
});
|
||||
}
|
||||
|
||||
if (isOwner) {
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['claude']
|
||||
});
|
||||
}
|
||||
|
||||
# ================================================================
|
||||
# Job 2 — Full Claude Code implementation
|
||||
# Triggers on: labeled `claude`, @claude comments, assignments
|
||||
#
|
||||
# Dedup: label + @claude comment on the same issue fires two events.
|
||||
# Skip the comment trigger when `claude` was labelled within 3 minutes.
|
||||
# ================================================================
|
||||
claude-dedupe:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(
|
||||
github.event.action != 'opened' &&
|
||||
(github.event.action != 'labeled' || github.event.label.name == 'claude') &&
|
||||
(github.event_name != 'issue_comment' || contains(github.event.comment.body, '@claude')) &&
|
||||
(
|
||||
!contains(github.actor, '[bot]') ||
|
||||
github.actor == 'github-actions[bot]' ||
|
||||
github.actor == 'praisonai-triage-agent[bot]'
|
||||
) &&
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
github.actor != 'cursor[bot]' &&
|
||||
github.actor != 'renovate[bot]'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
proceed: ${{ steps.check.outputs.proceed }}
|
||||
permissions:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Skip duplicate comment after fresh claude label
|
||||
id: check
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
if (context.eventName === 'workflow_dispatch') {
|
||||
core.setOutput('proceed', 'true');
|
||||
return;
|
||||
}
|
||||
if (context.eventName !== 'issue_comment' || context.payload.issue?.pull_request) {
|
||||
core.setOutput('proceed', 'true');
|
||||
return;
|
||||
}
|
||||
const issue = context.payload.issue;
|
||||
const hasClaude = (issue.labels || []).some(l => l.name === 'claude');
|
||||
if (!hasClaude) {
|
||||
core.setOutput('proceed', 'true');
|
||||
return;
|
||||
}
|
||||
const events = await github.rest.issues.listEvents({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 30,
|
||||
});
|
||||
const windowMs = 3 * 60 * 1000;
|
||||
const recentLabel = events.data.find(e =>
|
||||
e.event === 'labeled' &&
|
||||
e.label?.name === 'claude' &&
|
||||
(Date.now() - new Date(e.created_at).getTime()) < windowMs
|
||||
);
|
||||
if (recentLabel) {
|
||||
core.info('Skipping: claude label was added within 3 minutes — label trigger already handles this issue.');
|
||||
core.setOutput('proceed', 'false');
|
||||
return;
|
||||
}
|
||||
core.setOutput('proceed', 'true');
|
||||
|
||||
claude-response:
|
||||
needs: claude-dedupe
|
||||
env:
|
||||
ISSUE_NUMBER_RESOLVED: ${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
|
||||
if: needs.claude-dedupe.outputs.proceed == 'true'
|
||||
concurrency:
|
||||
group: claude-${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
actions: read
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Resolve GitHub token
|
||||
id: github-token
|
||||
run: |
|
||||
if [ -n "${{ steps.app-token.outputs.token }}" ]; then
|
||||
echo "token=${{ steps.app-token.outputs.token }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "token=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Check for Fork PR
|
||||
id: check_fork
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.github-token.outputs.token }}
|
||||
script: |
|
||||
const isPR = context.eventName === 'pull_request' ||
|
||||
context.eventName === 'pull_request_review' ||
|
||||
context.eventName === 'pull_request_review_comment' ||
|
||||
(context.payload.issue && context.payload.issue.pull_request);
|
||||
if (isPR) {
|
||||
core.setOutput('is_pr_review', 'true');
|
||||
const prNumber = context.payload.pull_request
|
||||
? context.payload.pull_request.number
|
||||
: context.payload.issue.number;
|
||||
core.setOutput('pr_number', String(prNumber));
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
core.setOutput('pr_branch', pr.data.head.ref);
|
||||
const headRepo = pr.data.head.repo?.full_name || context.repo.full_name;
|
||||
core.setOutput('head_repo', headRepo);
|
||||
|
||||
let mergeState = '';
|
||||
try {
|
||||
const gql = await github.graphql(
|
||||
`query($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $number) { mergeStateStatus }
|
||||
}
|
||||
}`,
|
||||
{ owner: context.repo.owner, repo: context.repo.repo, number: prNumber }
|
||||
);
|
||||
mergeState = (gql.repository.pullRequest.mergeStateStatus || '').toUpperCase();
|
||||
} catch (e) {
|
||||
core.warning(`GraphQL mergeStateStatus failed: ${e.message}`);
|
||||
}
|
||||
if (!mergeState) {
|
||||
mergeState = (pr.data.mergeable_state || pr.data.merge_state_status || '').toUpperCase();
|
||||
}
|
||||
core.setOutput('merge_state', mergeState);
|
||||
|
||||
if (headRepo !== `${context.repo.owner}/${context.repo.repo}`) {
|
||||
core.setOutput('is_fork', 'true');
|
||||
core.setOutput('clone_url', pr.data.head.repo.clone_url);
|
||||
core.setOutput('branch', pr.data.head.ref);
|
||||
return;
|
||||
}
|
||||
core.setOutput('is_fork', 'false');
|
||||
return;
|
||||
}
|
||||
core.setOutput('is_pr_review', 'false');
|
||||
core.setOutput('is_fork', 'false');
|
||||
core.setOutput('merge_state', '');
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.github-token.outputs.token }}
|
||||
ref: ${{ steps.check_fork.outputs.is_pr_review == 'true' && format('refs/pull/{0}/head', steps.check_fork.outputs.pr_number) || github.ref }}
|
||||
|
||||
- name: Checkout PR branch for edits
|
||||
if: steps.check_fork.outputs.is_pr_review == 'true' && steps.check_fork.outputs.is_fork != 'true'
|
||||
env:
|
||||
PR_BRANCH: ${{ steps.check_fork.outputs.pr_branch }}
|
||||
run: |
|
||||
git checkout "${PR_BRANCH}"
|
||||
git config --local user.name "MervinPraison"
|
||||
git config --local user.email "[email protected]"
|
||||
echo "On branch: $(git branch --show-current)"
|
||||
|
||||
- name: Fetch PR branch and Setup Remote
|
||||
if: steps.check_fork.outputs.is_pr_review == 'true' && steps.check_fork.outputs.is_fork == 'true'
|
||||
env:
|
||||
PR_BRANCH: ${{ steps.check_fork.outputs.pr_branch }}
|
||||
run: |
|
||||
git fetch origin pull/${{ steps.check_fork.outputs.pr_number }}/head:"${PR_BRANCH}"
|
||||
git checkout "${PR_BRANCH}"
|
||||
git remote set-url origin file://$(pwd)
|
||||
|
||||
- name: Run Claude PR final review
|
||||
if: steps.check_fork.outputs.is_pr_review == 'true'
|
||||
uses: anthropics/claude-code-action@beta
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.github-token.outputs.token }}
|
||||
with:
|
||||
allowed_bots: 'praisonai-triage-agent[bot]'
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ steps.github-token.outputs.token }}
|
||||
model: claude-opus-4-8
|
||||
trigger_phrase: "@claude"
|
||||
timeout_minutes: "30"
|
||||
direct_prompt: |
|
||||
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: FORK PR — comment only, do not push.\n\n' || 'PR on MervinPraison/PraisonAIUI — SAME REPO, NOT A FORK. You HAVE push permission to this branch. NEVER say this is a fork.\n\n' }}
|
||||
${{ steps.check_fork.outputs.is_fork != 'true' && steps.check_fork.outputs.merge_state == 'DIRTY' && format('PRIORITY — RESOLVE MERGE CONFLICTS (branch: {0}, PR #{1}):\n- DO NOT create a new branch or PR\n- git fetch origin main && git rebase origin/main\n- Resolve conflicts: keep this PR intent, merge in main newer logic\n- git add -A && git rebase --continue (repeat until done)\n- git push --force-with-lease origin {0}\n- Run targeted pytest/ruff on changed paths\n- Comment files resolved and what you skipped\n- Only after conflicts are resolved, proceed with review phases below if still needed\n\n', steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.pr_number) || '' }}
|
||||
${{ steps.check_fork.outputs.merge_state != 'DIRTY' && format('CONTEXT: PR #{0} on branch {1}. merge_state={2}.\n\n', steps.check_fork.outputs.pr_number, steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.merge_state) || '' }}
|
||||
You are the Lead Engineer doing FINAL review on this open PR in MervinPraison/PraisonAIUI.
|
||||
Read ALL reviewer comments (CodeRabbit, Gemini, Qodo, Copilot, human). Follow AGENTS.md.
|
||||
SCOPE: src/praisonaiui, src/frontend, tests, docs only.
|
||||
|
||||
**Phase 1 — Review** architecture, security, tests, backward compat, import-time budget.
|
||||
**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.
|
||||
|
||||
git config --global user.name "MervinPraison"
|
||||
git config --global user.email "[email protected]"
|
||||
gh auth setup-git
|
||||
git status
|
||||
git branch --show-current
|
||||
allowed_tools: |
|
||||
Bash(git:*)
|
||||
Bash(python:*)
|
||||
Bash(pip:*)
|
||||
Bash(pytest:*)
|
||||
Bash(ruff:*)
|
||||
Bash(gh:*)
|
||||
Bash(npm:*)
|
||||
View
|
||||
GlobTool
|
||||
GrepTool
|
||||
BatchTool
|
||||
Edit
|
||||
Replace
|
||||
|
||||
- name: Run Claude Code Action
|
||||
if: steps.check_fork.outputs.is_pr_review != 'true'
|
||||
uses: anthropics/claude-code-action@beta
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.github-token.outputs.token }}
|
||||
with:
|
||||
allowed_bots: 'praisonai-triage-agent[bot]'
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ steps.github-token.outputs.token }}
|
||||
model: claude-opus-4-8
|
||||
mode: ${{ github.event_name == 'workflow_dispatch' && 'agent' || 'tag' }}
|
||||
trigger_phrase: "@claude"
|
||||
label_trigger: "claude"
|
||||
timeout_minutes: "30"
|
||||
direct_prompt: |
|
||||
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || 'NOTE: The branch is under MervinPraison/PraisonAIUI (not a fork). You are able to make modifications and push directly to this branch.\n\n' }}
|
||||
You are implementing changes in **MervinPraison/PraisonAIUI** (YAML-driven server-driven UI with agentic chat).
|
||||
Follow `AGENTS.md` at the repository root. If a conflict arises between this prompt and `AGENTS.md`, `AGENTS.md` wins.
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 0 — GIT & GITHUB CLI
|
||||
═══════════════════════════════════════════════════════════════
|
||||
git config --global user.name "MervinPraison"
|
||||
git config --global user.email "[email protected]"
|
||||
gh auth setup-git
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 1 — READ GUIDELINES
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Read `AGENTS.md` and any `docs/principles.md`. Key invariants:
|
||||
• 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`,
|
||||
`google.generativeai`) must be **lazy-imported** — never eager.
|
||||
• Public API is verb-first. New classes in `src/praisonaiui/message.py` ending
|
||||
in `Message` with a verb prefix (Ask*, Do*, Request*, …) are forbidden —
|
||||
use a module-level `async def` verb helper instead (see `prompt`, `error`).
|
||||
• Deprecations must emit a runtime `warnings.warn(..., DeprecationWarning, stacklevel=2)`
|
||||
on first access, not only in the docstring.
|
||||
• Deterministic serialisation in anything reaching the wire (sorted keys, stable field
|
||||
order). See existing `to_dict` patterns.
|
||||
• Multi-agent + async safe: no global mutable state without a lock.
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 2 — ARCHITECTURE VALIDATION & ROUTING (MANDATORY before any code)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Answer these questions inline in your response, then proceed only if you pass:
|
||||
|
||||
ROUTING:
|
||||
1. **Core aiui** (`src/praisonaiui/`) — protocols, message types, server, features.
|
||||
2. **Features** (`src/praisonaiui/features/`) — opt-in capabilities (MCP, platforms, lifecycle, audio, auth, sharing, etc.).
|
||||
3. **UI builders** (`src/praisonaiui/ui.py`) — declarative component helpers.
|
||||
4. **Frontend** (`src/frontend/`) — React components / stores / SSE handlers.
|
||||
5. **Tests** (`tests/unit/`, `tests/integration/`) — new features require both smoke and real-agentic tests.
|
||||
6. **Docs** (`docs/`) — canonical name per feature, migration table for renames.
|
||||
|
||||
EXTERNAL REPOS (do NOT edit from within this repo — route the work to the right place and stop):
|
||||
• Core SDK: `MervinPraison/PraisonAI` (`praisonaiagents`, `praisonai`) — agent runtime.
|
||||
• Tools: `MervinPraison/PraisonAI-Tools` — tool implementations.
|
||||
• Docs site: `MervinPraison/PraisonAIDocs` — product docs.
|
||||
If the requested change belongs in an external repo, comment on the issue explaining
|
||||
where it should live and DO NOT open a PR in this repo.
|
||||
|
||||
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 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`?
|
||||
→ Move to `[project.optional-dependencies]` and wrap imports lazily.
|
||||
• Does it break backward compat (rename, signature change, removal)? → Add
|
||||
a deprecation alias that emits `DeprecationWarning`; never hard-remove before the
|
||||
documented removal version (e.g. `0.5.0`).
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 3 — IMPLEMENT
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Single branch for the whole flow (reuse the same name in STEP 5):
|
||||
BRANCH="claude/issue-${{ env.ISSUE_NUMBER_RESOLVED }}-$(date +%Y%m%d-%H%M%S)"
|
||||
git checkout -b "$BRANCH"
|
||||
|
||||
Make minimal, focused changes:
|
||||
• Stay within the files listed in the issue's "Scope" / "Files to change" section.
|
||||
• Prefer extending existing modules over adding new ones.
|
||||
• No emojis, no comments, no markdown except where the user asked for it.
|
||||
• Python ≥ 3.10 style (`list[...]` not `List[...]`, `X | None` not `Optional[X]`).
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 4 — TEST / VERIFY
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Run these targeted checks (NOT the full suite):
|
||||
|
||||
# 1. Lint on only the files you changed:
|
||||
CHANGED=$(git diff --name-only origin/main...HEAD | grep -E '\.py$' | tr '\n' ' ')
|
||||
[ -n "$CHANGED" ] && ruff check $CHANGED
|
||||
|
||||
# 2. Unit tests for the module(s) you touched:
|
||||
pytest tests/unit/<your_new_test_file>.py -v --tb=short
|
||||
|
||||
# 3. Import-time invariant (MUST stay under 200 ms cold):
|
||||
python -c "import time, sys; t=time.time(); import praisonaiui; dt=(time.time()-t)*1000; heavy=[m for m in sys.modules if any(h in m for h in ['langchain','llama_index','mcp','slack','discord','botbuilder','openai.','anthropic.','mistralai','google.generativeai'])]; print(f'{dt:.1f}ms modules={len(sys.modules)} leaked={heavy}'); assert heavy == [], f'Heavy deps leaked: {heavy}'"
|
||||
|
||||
If anything fails, fix the root cause (never skip, xfail, or delete the test).
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 5 — COMMIT, PUSH, OPEN PR (MANDATORY if you produced commits)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
git add -A
|
||||
git commit -m "feat: <short summary> (fixes #${{ env.ISSUE_NUMBER_RESOLVED }})"
|
||||
git push -u origin "$BRANCH"
|
||||
REPO="${{ github.repository }}"
|
||||
COUNT=$(gh pr list --repo "$REPO" --head "$BRANCH" --json number --jq 'length')
|
||||
if [ "$COUNT" -gt 0 ]; then
|
||||
gh pr list --repo "$REPO" --head "$BRANCH" --json url --jq '.[0].url'
|
||||
else
|
||||
gh pr create --repo "$REPO" --base main --head "$BRANCH" \
|
||||
--title "feat: <short title>" \
|
||||
--body "Fixes #${{ env.ISSUE_NUMBER_RESOLVED }}
|
||||
|
||||
## Summary
|
||||
|
||||
<what changed in one paragraph>
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
<copy from the issue; tick boxes only with commit SHA + file path evidence>
|
||||
|
||||
## Test evidence
|
||||
|
||||
\`\`\`
|
||||
<paste pytest output>
|
||||
\`\`\`
|
||||
"
|
||||
fi
|
||||
|
||||
CRITICAL: Do NOT stop after `git push`. You MUST complete `gh pr create` (or print the
|
||||
existing PR URL from the step above) and post that URL on the issue. Never ask a
|
||||
human to open the PR manually.
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
STEP 6 — RESPOND TO REVIEW FEEDBACK (if this run was triggered by a review comment)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
If you were invoked by a PR review comment, you are the Lead Engineer for this PR:
|
||||
• Read ALL reviewer comments in chronological order (CodeRabbit → Copilot → human).
|
||||
• Fix every VALID issue by pushing commits to THIS branch (do NOT open a new PR).
|
||||
• Reply with a summary of which comments you addressed, which you disagreed with (and why).
|
||||
• Never ship a fix that removes/weakens a test.
|
||||
|
||||
allowed_tools: |
|
||||
Bash(git:*)
|
||||
Bash(python:*)
|
||||
Bash(pip:*)
|
||||
Bash(pytest:*)
|
||||
Bash(ruff:*)
|
||||
Bash(gh:*)
|
||||
Bash(npm:*)
|
||||
Bash(npx:*)
|
||||
Bash(python -m pytest:*)
|
||||
Bash(python -m pip:*)
|
||||
Bash(python -m ruff:*)
|
||||
View
|
||||
GlobTool
|
||||
GrepTool
|
||||
BatchTool
|
||||
Edit
|
||||
Replace
|
||||
mcp__github__get_issue
|
||||
mcp__github__get_issue_comments
|
||||
mcp__github__update_issue
|
||||
claude:
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/claud[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: deploy-docs
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install AIUI
|
||||
run: pip install -e .
|
||||
|
||||
- name: Build docs with AIUI
|
||||
run: |
|
||||
aiui build --output site --minify
|
||||
cp CNAME site/CNAME
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./site
|
||||
force_orphan: true
|
||||
@@ -1,7 +1,5 @@
|
||||
name: Auto Merge Conflict → Claude
|
||||
|
||||
# Detects internal PRs with mergeStateStatus=DIRTY and posts @claude to trigger claude.yml.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
@@ -12,11 +10,10 @@ 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:
|
||||
# PAT posts as MervinPraison so issue_comment triggers claude.yml (GITHUB_TOKEN cannot chain workflows)
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
|
||||
permissions:
|
||||
@@ -27,157 +24,29 @@ jobs:
|
||||
detect-and-trigger:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: MervinPraison/github-automation-template/.github/actions/[email protected]
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
- name: Detect conflicting PRs and trigger Claude
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ env.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const mg = require(path.join(process.env.HUB_SCRIPTS, 'merge-gate.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const baseRepo = `${owner}/${repo}`;
|
||||
const COOLDOWN_MS = 12 * 60 * 60 * 1000;
|
||||
const AUTO_ACTORS = ['github-actions[bot]', 'MervinPraison'];
|
||||
const LABEL = 'claude-conflict-pending';
|
||||
const COMMENT_BODY = [
|
||||
'@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.'
|
||||
].join(' ');
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
async function getMergeState(prNumber) {
|
||||
const query = `
|
||||
query($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $number) {
|
||||
mergeStateStatus
|
||||
maintainerCanModify
|
||||
isDraft
|
||||
headRef { repository { nameWithOwner } }
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
const result = await github.graphql(query, { owner, repo, number: prNumber });
|
||||
const prGql = result.repository.pullRequest;
|
||||
const status = (prGql?.mergeStateStatus || '').toUpperCase();
|
||||
if (status && status !== 'UNKNOWN') {
|
||||
return {
|
||||
status,
|
||||
isDraft: prGql.isDraft,
|
||||
headRepo: prGql.headRef?.repository?.nameWithOwner,
|
||||
maintainerCanModify: prGql.maintainerCanModify === true,
|
||||
};
|
||||
}
|
||||
if (attempt < 2) await sleep(10000);
|
||||
}
|
||||
const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
||||
return {
|
||||
status: (pr.data.mergeable_state || '').toUpperCase(),
|
||||
isDraft: pr.data.draft,
|
||||
headRepo: pr.data.head.repo?.full_name,
|
||||
maintainerCanModify: pr.data.maintainer_can_modify === true,
|
||||
};
|
||||
const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: 'open', per_page: 50 });
|
||||
for (const pr of prs) {
|
||||
if (pr.draft) continue;
|
||||
if (pr.head?.repo?.full_name !== baseRepo) continue;
|
||||
const state = await mg.getMergeState(github, owner, repo, pr.number);
|
||||
if (state.status !== 'DIRTY') continue;
|
||||
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: pr.number, per_page: 100 });
|
||||
if (mg.hasRecentConflictComment(comments)) continue;
|
||||
const body = '@claude this PR has merge conflicts with `main`. Rebase onto latest main, resolve conflicts, run tests, force-push with --force-with-lease.';
|
||||
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body });
|
||||
await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: ['claude-conflict-pending'] });
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (!AUTO_ACTORS.includes(c.user.login)) return false;
|
||||
const body = (c.body || '').toLowerCase();
|
||||
if (!body.includes('@claude') || !body.includes('merge conflict')) return false;
|
||||
return new Date(c.created_at).getTime() > cutoff;
|
||||
});
|
||||
}
|
||||
|
||||
async function processPR(prNumber) {
|
||||
const { status, isDraft, headRepo, maintainerCanModify } = await getMergeState(prNumber);
|
||||
core.info(`PR #${prNumber}: mergeState=${status}, head=${headRepo}, draft=${isDraft}, maintainerCanModify=${maintainerCanModify}`);
|
||||
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
const labels = issue.labels.map((l) => l.name);
|
||||
|
||||
if (status !== 'DIRTY' && labels.includes(LABEL)) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: LABEL,
|
||||
});
|
||||
core.info(`Removed ${LABEL} from PR #${prNumber}`);
|
||||
}
|
||||
|
||||
if (status !== 'DIRTY') return { skipped: true, reason: 'not dirty' };
|
||||
if (isDraft) return { skipped: true, reason: 'draft' };
|
||||
if (headRepo && headRepo !== baseRepo && !maintainerCanModify) {
|
||||
return { skipped: true, reason: 'fork without maintainer edits' };
|
||||
}
|
||||
if (labels.includes(LABEL)) {
|
||||
return { skipped: true, reason: 'conflict resolution pending' };
|
||||
}
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
if (hasRecentAutoComment(comments)) {
|
||||
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,
|
||||
issue_number: prNumber,
|
||||
labels: [LABEL],
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
body: COMMENT_BODY,
|
||||
});
|
||||
return { triggered: true };
|
||||
}
|
||||
|
||||
let prNumbers = [];
|
||||
if (context.eventName === 'pull_request') {
|
||||
prNumbers = [context.payload.pull_request.number];
|
||||
} else {
|
||||
const prs = await github.paginate(github.rest.pulls.list, {
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
});
|
||||
prNumbers = prs.map((p) => p.number);
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const num of prNumbers) {
|
||||
results.push({ pr: num, ...(await processPR(num)) });
|
||||
}
|
||||
core.summary.addRaw('```json\n' + JSON.stringify(results, null, 2) + '\n```');
|
||||
await core.summary.write();
|
||||
@@ -1,7 +1,5 @@
|
||||
name: Pipeline Status Sync
|
||||
|
||||
# Sync pipeline/* stage and blocker labels on open internal PRs.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/10 * * * *'
|
||||
@@ -15,28 +13,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Sync pipeline labels on open PRs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js'));
|
||||
await ps.syncOpenPullRequests(
|
||||
github, context.repo.owner, context.repo.repo,
|
||||
{ maxPrs: 30, dispatchMergeGate: true }, core
|
||||
);
|
||||
uses: MervinPraison/github-automation-template/.github/workflows/[email protected]
|
||||
secrets: inherit
|
||||
with:
|
||||
hub_ref: v1.1.3
|
||||
+13
-4
@@ -9,6 +9,14 @@ site:
|
||||
radius: "md"
|
||||
darkMode: true
|
||||
|
||||
seo:
|
||||
siteUrl: "https://ui.praison.ai"
|
||||
titleTemplate: "%s | %s"
|
||||
defaultImage: "/icon.svg"
|
||||
robots:
|
||||
index: true
|
||||
follow: true
|
||||
|
||||
# Mintlify-style navigation: few tabs, many groups per tab
|
||||
navigation:
|
||||
tabs:
|
||||
@@ -73,11 +81,12 @@ navigation:
|
||||
# Navbar actions
|
||||
navbar:
|
||||
primary:
|
||||
type: "github"
|
||||
href: "https://github.com/MervinPraison/PraisonAIUI"
|
||||
type: "button"
|
||||
label: "Get started"
|
||||
href: "/docs/getting-started/quickstart"
|
||||
links:
|
||||
- label: "Submit Issues"
|
||||
href: "https://github.com/MervinPraison/PraisonAIUI/issues"
|
||||
- label: "GitHub"
|
||||
href: "https://github.com/MervinPraison/PraisonAIUI"
|
||||
|
||||
# Footer
|
||||
footer:
|
||||
|
||||
+54
-51
@@ -1,73 +1,76 @@
|
||||
# SEO
|
||||
|
||||
Built-in SEO features for better search engine visibility.
|
||||
Built-in SEO for documentation sites — sitemap, robots, llms.txt, per-page meta tags, and crawlable static HTML.
|
||||
|
||||
## Automatic Meta Tags
|
||||
## Configuration
|
||||
|
||||
PraisonAIUI generates meta tags from your configuration:
|
||||
Top-level `seo` block in `aiui.template.yaml`:
|
||||
|
||||
```yaml
|
||||
site:
|
||||
title: "My Documentation"
|
||||
description: "Comprehensive guides for my product"
|
||||
seo:
|
||||
siteUrl: "https://ui.praison.ai"
|
||||
titleTemplate: "%s | %s"
|
||||
defaultImage: "/icon.svg"
|
||||
robots:
|
||||
index: true
|
||||
follow: true
|
||||
twitter:
|
||||
handle: "@yourhandle"
|
||||
```
|
||||
|
||||
Generates:
|
||||
```html
|
||||
<title>My Documentation</title>
|
||||
<meta name="description" content="Comprehensive guides for my product">
|
||||
```
|
||||
`siteUrl` is required for absolute canonical URLs, sitemap, and social tags.
|
||||
|
||||
## Page-Level Overrides
|
||||
## Build outputs
|
||||
|
||||
Use frontmatter to override per-page:
|
||||
`aiui build` automatically generates:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `sitemap.xml` | All doc URLs with `lastmod` dates |
|
||||
| `robots.txt` | Crawl rules + sitemap pointer |
|
||||
| `llms.txt` | AI crawler index ([llmstxt.org](https://llmstxt.org)) |
|
||||
| `llms-full.txt` | Full markdown export for AI tools |
|
||||
| `docs/**/*.html` | Per-route HTML with meta tags + crawl nav |
|
||||
| `.nojekyll` | GitHub Pages Jekyll bypass |
|
||||
|
||||
No extra flags are needed — SEO files are emitted whenever `content.docs` is configured.
|
||||
|
||||
## Page-level overrides
|
||||
|
||||
Use frontmatter on any markdown page:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Custom Page Title"
|
||||
description: "Custom meta description for this page"
|
||||
description: "Custom meta description for search snippets"
|
||||
noindex: true
|
||||
---
|
||||
|
||||
# Page Content
|
||||
```
|
||||
|
||||
## SEO Configuration
|
||||
If `description` is omitted, the first paragraph is used automatically.
|
||||
|
||||
## Per-page HTML includes
|
||||
|
||||
Each route HTML file contains:
|
||||
|
||||
- `<link rel="canonical">` (absolute HTTPS)
|
||||
- `<meta name="description">`
|
||||
- Open Graph tags (`og:title`, `og:description`, `og:url`, `og:type`, `og:image`)
|
||||
- Twitter Card tags
|
||||
- JSON-LD `TechArticle` schema
|
||||
- Static `<nav>` with links to every doc page (for non-JS crawlers)
|
||||
- `<noscript>` pre-rendered page content
|
||||
|
||||
## Runtime (SPA navigation)
|
||||
|
||||
When users navigate via the sidebar, React updates title, canonical, OG, Twitter, robots, and JSON-LD to match the current page.
|
||||
|
||||
## Deploy
|
||||
|
||||
GitHub Pages deploy runs via the **Deploy Docs** workflow on every push to `main`. Submit your sitemap in [Google Search Console](https://search.google.com/search-console):
|
||||
|
||||
```yaml
|
||||
site:
|
||||
seo:
|
||||
titleTemplate: "%s | My Site"
|
||||
defaultImage: "/og-image.png"
|
||||
twitterHandle: "@myhandle"
|
||||
```
|
||||
|
||||
## Open Graph
|
||||
|
||||
Automatic Open Graph tags for social sharing:
|
||||
|
||||
```html
|
||||
<meta property="og:title" content="Page Title">
|
||||
<meta property="og:description" content="Page description">
|
||||
<meta property="og:image" content="/og-image.png">
|
||||
<meta property="og:type" content="website">
|
||||
```
|
||||
|
||||
## Sitemap
|
||||
|
||||
Generate a sitemap during build:
|
||||
|
||||
```bash
|
||||
aiui build --sitemap
|
||||
```
|
||||
|
||||
Creates `sitemap.xml` with all pages.
|
||||
|
||||
## Robots.txt
|
||||
|
||||
```yaml
|
||||
site:
|
||||
seo:
|
||||
robots:
|
||||
index: true
|
||||
follow: true
|
||||
https://your-domain.com/sitemap.xml
|
||||
```
|
||||
@@ -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."""
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Meeting Agent example — copy to .env and fill in. Never commit real secrets.
|
||||
|
||||
# Used by the PraisonAI agent (PAI-MEET-CORE) for summarization/embeddings.
|
||||
OPENAI_API_KEY=
|
||||
|
||||
# Optional: enable Slack summary delivery via the built-in SlackChannelAdapter.
|
||||
# Leave blank to run dashboard-only (no Slack required).
|
||||
SLACK_BOT_TOKEN=
|
||||
SLACK_APP_TOKEN=
|
||||
|
||||
# Local dev server port for `python app.py`.
|
||||
MEETING_AGENT_PORT=8000
|
||||
@@ -0,0 +1,49 @@
|
||||
# Meeting Agent (thin UI example)
|
||||
|
||||
A lightweight PraisonAIUI example that wires **existing** features together via
|
||||
YAML/config to build a meeting assistant dashboard. It adds no agent-runtime
|
||||
logic and no agent-callable tools.
|
||||
|
||||
## What this example includes
|
||||
|
||||
| Page | Built from |
|
||||
|------|-----------|
|
||||
| Upload | `attachments` feature (`POST /api/chat/attachments`) + status copy |
|
||||
| Meetings | dashboard page (`aiui.table` / `aiui.metric`) |
|
||||
| Meeting detail | tabbed dashboard page — Transcript / Summary / Action items |
|
||||
| Chat | `@aiui.reply` with a system prompt enforcing *search meetings first* + citation chips |
|
||||
|
||||
Slack delivery (`/meeting-summary <id>`) uses the existing
|
||||
`SlackChannelAdapter` and is **optional** — the dashboard runs without any Slack
|
||||
tokens (dashboard-only mode). `slack_sdk` is imported lazily, only when tokens
|
||||
are present.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
cp .env.example .env # optional; dashboard works without any tokens
|
||||
aiui run app.py
|
||||
# or
|
||||
python app.py # honours MEETING_AGENT_PORT
|
||||
```
|
||||
|
||||
## Where the rest lives (routed out)
|
||||
|
||||
This repo intentionally hosts only the thin UI slice. The agent and tools live
|
||||
in separate repositories:
|
||||
|
||||
- **Agent core** (`build_agent`, instructions, summarization pipeline, storage,
|
||||
status state machine) → `MervinPraison/PraisonAI` (**PAI-MEET-CORE**).
|
||||
- **Tools** (`search_meetings`, transcription, indexing, etc.) →
|
||||
`MervinPraison/PraisonAI-Tools` (**PAI-MEET-TOOLS**).
|
||||
|
||||
Point this UI at your running agent via the standard PraisonAI provider/jobs
|
||||
configuration; no changes to `praisonaiui` internals are required.
|
||||
|
||||
## Environment
|
||||
|
||||
See `.env.example`:
|
||||
|
||||
- `OPENAI_API_KEY` — used by the agent (PAI-MEET-CORE).
|
||||
- `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN` — optional Slack delivery.
|
||||
- `MEETING_AGENT_PORT` — local dev port.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Meeting Agent — PraisonAIUI example config (PAI-MEET-UI, issue #260)
|
||||
# Composes existing features (attachments, knowledge, chat, Slack) via YAML.
|
||||
# Run with: aiui run app.py
|
||||
|
||||
site:
|
||||
title: Meeting Agent
|
||||
description: Upload meetings, browse summaries and action items, ask questions.
|
||||
logo: "🗓️"
|
||||
theme:
|
||||
preset: slate
|
||||
darkMode: true
|
||||
radius: md
|
||||
|
||||
style: dashboard
|
||||
|
||||
layout:
|
||||
mode: dashboard
|
||||
|
||||
dashboard:
|
||||
sidebar: true
|
||||
pageHeader: true
|
||||
|
||||
pages:
|
||||
- upload
|
||||
- meetings
|
||||
- meeting-detail
|
||||
- chat
|
||||
|
||||
chat:
|
||||
enabled: true
|
||||
name: Meeting Assistant
|
||||
features:
|
||||
streaming: true
|
||||
reasoning: true
|
||||
tools: true
|
||||
fileUpload: true
|
||||
feedback: true
|
||||
input:
|
||||
placeholder: "Ask about a past meeting..."
|
||||
starters:
|
||||
- label: "What did we decide about billing?"
|
||||
message: "What did we decide about billing?"
|
||||
icon: "🔎"
|
||||
- label: "List open action items"
|
||||
message: "List open action items and their owners"
|
||||
icon: "✅"
|
||||
@@ -0,0 +1,252 @@
|
||||
"""Meeting Agent — thin PraisonAIUI example (PAI-MEET-UI, issue #260).
|
||||
|
||||
Composes existing PraisonAIUI features via YAML/config only:
|
||||
|
||||
- Upload page → reuses the ``attachments`` feature (POST /api/chat/attachments)
|
||||
- Meetings list → dashboard page built from ``aiui.ui`` helpers
|
||||
- Meeting detail → tabbed dashboard page (Transcript / Summary / Action items)
|
||||
- Chat → system prompt enforces "search meetings first" + citation chips
|
||||
- Slack delivery → optional, via the existing SlackChannelAdapter (lazy slack_sdk)
|
||||
|
||||
This example adds NO agent-runtime logic and NO agent-callable tools. Those live in
|
||||
external repositories:
|
||||
|
||||
- Agent core → MervinPraison/PraisonAI (PAI-MEET-CORE)
|
||||
- Tools → MervinPraison/PraisonAI-Tools (PAI-MEET-TOOLS)
|
||||
|
||||
The dashboard runs without any Slack tokens (dashboard-only mode). Heavy deps
|
||||
(openai, slack_sdk, chromadb) are never imported eagerly here.
|
||||
|
||||
Run:
|
||||
aiui run app.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import praisonaiui as aiui
|
||||
from praisonaiui.server import create_app
|
||||
|
||||
CHAT_SYSTEM_PROMPT = (
|
||||
"You are a meeting assistant. For any question about past meetings, decisions, "
|
||||
"owners, or action items you MUST call the search_meetings tool first and answer "
|
||||
"only from the retrieved transcripts and summaries. Never answer historical "
|
||||
"questions from memory. Cite every claim with a citation chip showing the meeting "
|
||||
"title linked to its detail page."
|
||||
)
|
||||
|
||||
SAMPLE_MEETINGS: list[dict] = [
|
||||
{
|
||||
"id": "m-1001",
|
||||
"title": "Q3 Planning Kickoff",
|
||||
"date": "2026-07-01",
|
||||
"status": "Ready",
|
||||
"duration": "48m",
|
||||
"transcript": "Alice: Let's align on Q3 goals...\nBob: Priorities are onboarding and billing.",
|
||||
"summary": "Team aligned on Q3 priorities: onboarding revamp and billing reliability.",
|
||||
"actions": [
|
||||
{"task": "Draft onboarding spec", "owner": "Alice", "due": "2026-07-08"},
|
||||
{"task": "Audit billing retries", "owner": "Bob", "due": "2026-07-10"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "m-1002",
|
||||
"title": "Billing Reliability Review",
|
||||
"date": "2026-07-09",
|
||||
"status": "Summarizing",
|
||||
"duration": "32m",
|
||||
"transcript": "Bob: Retry storms caused duplicate charges...\nCara: We need idempotency keys.",
|
||||
"summary": "Root-caused duplicate charges to missing idempotency keys; fix scheduled.",
|
||||
"actions": [
|
||||
{"task": "Add idempotency keys", "owner": "Cara", "due": "2026-07-16"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _meeting(meeting_id: str) -> dict | None:
|
||||
return next((m for m in SAMPLE_MEETINGS if m["id"] == meeting_id), None)
|
||||
|
||||
|
||||
def _status_variant(status: str) -> str:
|
||||
return {
|
||||
"Ready": "default",
|
||||
"Indexing": "secondary",
|
||||
"Summarizing": "secondary",
|
||||
"Transcribing": "secondary",
|
||||
"Uploading": "secondary",
|
||||
"Error": "destructive",
|
||||
}.get(status, "outline")
|
||||
|
||||
|
||||
aiui.set_style("dashboard")
|
||||
aiui.set_branding(title="Meeting Agent", logo="🗓️")
|
||||
aiui.set_pages(["upload", "meetings", "meeting-detail", "chat"])
|
||||
aiui.set_dashboard(sidebar=True, page_header=True)
|
||||
aiui.set_chat_features(file_upload=True, feedback=True)
|
||||
|
||||
|
||||
@aiui.page("upload", title="Upload", icon="⬆️", group="Meetings", order=1)
|
||||
async def upload_page():
|
||||
steps = ["Uploading", "Transcribing", "Summarizing", "Indexing", "Ready"]
|
||||
return aiui.layout(
|
||||
[
|
||||
aiui.text("Drop an audio or transcript file to add a meeting."),
|
||||
aiui.alert(
|
||||
"Files are uploaded via the built-in attachments feature "
|
||||
"(POST /api/chat/attachments). Transcription, summarization and "
|
||||
"indexing run in the PraisonAI agent (PAI-MEET-CORE).",
|
||||
variant="info",
|
||||
title="How it works",
|
||||
),
|
||||
aiui.card(
|
||||
"Processing pipeline",
|
||||
footer=" → ".join(steps),
|
||||
),
|
||||
aiui.text(
|
||||
"On error the pipeline surfaces a retry action via the standard "
|
||||
"chat status events; no bespoke state machine lives in the UI."
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@aiui.page("meetings", title="Meetings", icon="📋", group="Meetings", order=2)
|
||||
async def meetings_page():
|
||||
rows = [
|
||||
[
|
||||
m["title"],
|
||||
m["date"],
|
||||
m["status"],
|
||||
str(len(m["actions"])),
|
||||
m["duration"],
|
||||
]
|
||||
for m in SAMPLE_MEETINGS
|
||||
]
|
||||
return aiui.layout(
|
||||
[
|
||||
aiui.metric("Meetings", value=len(SAMPLE_MEETINGS)),
|
||||
aiui.table(
|
||||
headers=["Title", "Date", "Status", "Actions", "Duration"],
|
||||
rows=rows,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@aiui.page("meeting-detail", title="Meeting detail", icon="📝", group="Meetings", order=3)
|
||||
async def meeting_detail_page():
|
||||
meeting = SAMPLE_MEETINGS[0]
|
||||
action_rows = [[a["task"], a["owner"], a["due"]] for a in meeting["actions"]]
|
||||
return aiui.layout(
|
||||
[
|
||||
aiui.text(meeting["title"]),
|
||||
aiui.badge(meeting["status"], variant=_status_variant(meeting["status"])),
|
||||
aiui.tabs(
|
||||
[
|
||||
{
|
||||
"label": "Transcript",
|
||||
"children": [aiui.code_block(meeting["transcript"], language="text")],
|
||||
},
|
||||
{
|
||||
"label": "Summary",
|
||||
"children": [aiui.text(meeting["summary"])],
|
||||
},
|
||||
{
|
||||
"label": "Action items",
|
||||
"children": [
|
||||
aiui.table(
|
||||
headers=["Task", "Owner", "Due"],
|
||||
rows=action_rows,
|
||||
)
|
||||
],
|
||||
},
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@aiui.reply
|
||||
async def on_message(message: str):
|
||||
await aiui.think("Searching meetings before answering...")
|
||||
hits = [
|
||||
m
|
||||
for m in SAMPLE_MEETINGS
|
||||
if message.lower() in (m["title"] + " " + m["summary"]).lower()
|
||||
]
|
||||
if not hits:
|
||||
hits = SAMPLE_MEETINGS
|
||||
top = hits[0]
|
||||
await aiui.say(top["summary"])
|
||||
await aiui.action_buttons(
|
||||
[
|
||||
{
|
||||
"name": f"open:{m['id']}",
|
||||
"label": f"📎 {m['title']}",
|
||||
}
|
||||
for m in hits
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _slack_adapter():
|
||||
"""Construct a Slack adapter only when tokens are configured (lazy slack_sdk)."""
|
||||
bot = os.environ.get("SLACK_BOT_TOKEN", "")
|
||||
app_token = os.environ.get("SLACK_APP_TOKEN", "")
|
||||
if not bot or not app_token:
|
||||
return None
|
||||
from praisonaiui.features.platform_adapters.slack import SlackChannelAdapter
|
||||
|
||||
return SlackChannelAdapter({"bot_token": bot, "app_token": app_token})
|
||||
|
||||
|
||||
def _summary_blocks(meeting: dict) -> list[dict]:
|
||||
action_lines = "\n".join(
|
||||
f"• {a['task']} — {a['owner']} (due {a['due']})" for a in meeting["actions"]
|
||||
)
|
||||
return [
|
||||
{"type": "header", "text": {"type": "plain_text", "text": meeting["title"]}},
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Date:* {meeting['date']}"}},
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": meeting["summary"]}},
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Action items*\n{action_lines}"}},
|
||||
{
|
||||
"type": "context",
|
||||
"elements": [{"type": "mrkdwn", "text": f"Dashboard: /meeting-detail#{meeting['id']}"}],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def post_meeting_summary(meeting_id: str, channel_id: str) -> bool:
|
||||
"""Deliver a meeting summary to Slack (/meeting-summary <id>).
|
||||
|
||||
Returns False when Slack is not configured — the dashboard still works.
|
||||
"""
|
||||
meeting = _meeting(meeting_id)
|
||||
if meeting is None:
|
||||
return False
|
||||
adapter = _slack_adapter()
|
||||
if adapter is None:
|
||||
return False
|
||||
await adapter.start()
|
||||
try:
|
||||
await adapter.send_message(
|
||||
channel_id,
|
||||
meeting["summary"],
|
||||
blocks=_summary_blocks(meeting),
|
||||
)
|
||||
finally:
|
||||
await adapter.stop()
|
||||
return True
|
||||
|
||||
|
||||
app = create_app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
host = os.environ.get("HOST", "127.0.0.1")
|
||||
port = int(os.environ.get("MEETING_AGENT_PORT", "8000"))
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "aiui"
|
||||
version = "0.3.137"
|
||||
version = "0.3.142"
|
||||
description = "YAML-driven website generator - CLI and compiler"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<title>Documentation</title>
|
||||
<script>
|
||||
// Anti-flicker: apply dark mode class before first paint
|
||||
// Default to dark to prevent flash; React will correct if needed
|
||||
document.documentElement.classList.add('dark');
|
||||
(function(){try{var t=localStorage.getItem('aiui-theme-preference');var d=__AIUI_DARK_DEFAULT__;if(t==='dark'||(t==='system'&&matchMedia('(prefers-color-scheme: dark)').matches)||(!t&&d))document.documentElement.classList.add('dark');else if(t==='light')document.documentElement.classList.remove('dark');}catch(e){}})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="/assets/theme.css">
|
||||
</head>
|
||||
|
||||
Generated
+903
-2
File diff suppressed because it is too large.
Load diff
@@ -89,6 +89,7 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"lucide-react": "^0.563.0",
|
||||
"mermaid": "^11.17.2",
|
||||
"react-day-picker": "^9.13.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resizable-panels": "^4.5.4",
|
||||
|
||||
+274
-119
@@ -1,15 +1,18 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { applyTheme } from './themes'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { applyTheme, applyStoredThemePreference } 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'
|
||||
import { normalizeDocPath } from './pathUtils'
|
||||
import { detectActiveTabIndex, filterNavByTab } from './docs/navTabs'
|
||||
|
||||
function SkipLink({ enabled }: { enabled?: boolean }) {
|
||||
if (!enabled) return null
|
||||
@@ -31,12 +34,30 @@ 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(() => normalizeDocPath(window.location.pathname))
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState(false)
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(0)
|
||||
|
||||
// Helper function to find nav item by path
|
||||
const navTabs = config.navigation?.tabs ?? []
|
||||
const filteredNav = useMemo<DocsNav>(() => {
|
||||
if (!navTabs.length) return nav
|
||||
return { items: filterNavByTab(nav.items ?? [], navTabs[activeTabIndex]) }
|
||||
}, [nav, navTabs, activeTabIndex])
|
||||
|
||||
useEffect(() => {
|
||||
if (navTabs.length) {
|
||||
setActiveTabIndex(detectActiveTabIndex(navTabs, currentPath))
|
||||
}
|
||||
}, [currentPath, navTabs])
|
||||
|
||||
// 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 = normalizeDocPath(path)
|
||||
|
||||
const findItem = (items: NavItem[]): NavItem | null => {
|
||||
for (const item of items) {
|
||||
if (item.path === path || item.path === path.replace(/^\//, '')) {
|
||||
const itemPath = normalizeDocPath(item.path ?? '')
|
||||
if (itemPath === normalizedPath) {
|
||||
return item
|
||||
}
|
||||
if (item.children) {
|
||||
@@ -47,94 +68,190 @@ 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) => {
|
||||
// Update title using SEO titleTemplate if available
|
||||
const titleTemplate = config.seo?.titleTemplate || '%s | %s'
|
||||
const siteName = config.site?.title || 'Documentation'
|
||||
const seoOrigin = (cfg: UIConfig) => (cfg.seo?.siteUrl || window.location.origin).replace(/\/+$/, '')
|
||||
|
||||
const formatSeoTitle = (title: string, cfg: UIConfig) => {
|
||||
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)
|
||||
if (formattedTitle.includes('%s')) {
|
||||
formattedTitle = formattedTitle.replace('%s', siteName)
|
||||
}
|
||||
document.title = formattedTitle
|
||||
} else {
|
||||
document.title = `${title} | ${siteName}`
|
||||
return formattedTitle
|
||||
}
|
||||
return `${title} | ${siteName}`
|
||||
}
|
||||
|
||||
const updateSEO = (
|
||||
title: string,
|
||||
path: string,
|
||||
description?: string,
|
||||
configOverride?: UIConfig,
|
||||
noindex?: boolean,
|
||||
) => {
|
||||
const cfg = configOverride ?? config
|
||||
const canonicalPath = normalizeDocPath(path)
|
||||
const formattedTitle = formatSeoTitle(title, cfg)
|
||||
const metaDescription = description || `${title} - ${cfg.site?.description || ''}`
|
||||
const absoluteUrl = `${seoOrigin(cfg)}${canonicalPath}`
|
||||
|
||||
document.title = formattedTitle
|
||||
|
||||
// Update canonical URL
|
||||
let canonical = document.querySelector('link[rel="canonical"]') as HTMLLinkElement
|
||||
if (!canonical) {
|
||||
canonical = document.createElement('link')
|
||||
canonical.rel = 'canonical'
|
||||
document.head.appendChild(canonical)
|
||||
}
|
||||
canonical.href = window.location.origin + path
|
||||
canonical.href = absoluteUrl
|
||||
|
||||
// Update meta description
|
||||
let metaDesc = document.querySelector('meta[name="description"]') as HTMLMetaElement
|
||||
if (!metaDesc) {
|
||||
metaDesc = document.createElement('meta')
|
||||
metaDesc.name = 'description'
|
||||
document.head.appendChild(metaDesc)
|
||||
}
|
||||
metaDesc.content = description || `${title} - ${config.site?.description || ''}`
|
||||
metaDesc.content = metaDescription
|
||||
|
||||
// Update Open Graph tags
|
||||
let ogTitle = document.querySelector('meta[property="og:title"]') as HTMLMetaElement
|
||||
if (!ogTitle) {
|
||||
ogTitle = document.createElement('meta')
|
||||
ogTitle.setAttribute('property', 'og:title')
|
||||
document.head.appendChild(ogTitle)
|
||||
}
|
||||
ogTitle.content = title
|
||||
|
||||
let ogUrl = document.querySelector('meta[property="og:url"]') as HTMLMetaElement
|
||||
if (!ogUrl) {
|
||||
ogUrl = document.createElement('meta')
|
||||
ogUrl.setAttribute('property', 'og:url')
|
||||
document.head.appendChild(ogUrl)
|
||||
}
|
||||
ogUrl.content = window.location.origin + path
|
||||
|
||||
// Update Twitter meta tags if configured
|
||||
if (config.seo?.twitter) {
|
||||
Object.entries(config.seo.twitter).forEach(([key, value]) => {
|
||||
let twitterMeta = document.querySelector(`meta[name="twitter:${key}"]`) as HTMLMetaElement
|
||||
if (!twitterMeta) {
|
||||
twitterMeta = document.createElement('meta')
|
||||
twitterMeta.name = `twitter:${key}`
|
||||
document.head.appendChild(twitterMeta)
|
||||
}
|
||||
twitterMeta.content = value
|
||||
})
|
||||
}
|
||||
|
||||
// Set default image if available
|
||||
if (config.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)
|
||||
const ensureMeta = (selector: string, create: () => HTMLMetaElement) => {
|
||||
let el = document.querySelector(selector) as HTMLMetaElement | null
|
||||
if (!el) {
|
||||
el = create()
|
||||
document.head.appendChild(el)
|
||||
}
|
||||
ogImage.content = config.seo.defaultImage
|
||||
return el
|
||||
}
|
||||
|
||||
ensureMeta('meta[name="robots"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'robots'
|
||||
return el
|
||||
}).content = noindex ? 'noindex, nofollow' : 'index, follow'
|
||||
|
||||
ensureMeta('meta[property="og:title"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.setAttribute('property', 'og:title')
|
||||
return el
|
||||
}).content = formattedTitle
|
||||
|
||||
ensureMeta('meta[property="og:description"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.setAttribute('property', 'og:description')
|
||||
return el
|
||||
}).content = metaDescription
|
||||
|
||||
ensureMeta('meta[property="og:url"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.setAttribute('property', 'og:url')
|
||||
return el
|
||||
}).content = absoluteUrl
|
||||
|
||||
ensureMeta('meta[property="og:type"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.setAttribute('property', 'og:type')
|
||||
return el
|
||||
}).content = 'article'
|
||||
|
||||
ensureMeta('meta[name="twitter:card"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'twitter:card'
|
||||
return el
|
||||
}).content = 'summary'
|
||||
|
||||
ensureMeta('meta[name="twitter:title"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'twitter:title'
|
||||
return el
|
||||
}).content = formattedTitle
|
||||
|
||||
ensureMeta('meta[name="twitter:description"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'twitter:description'
|
||||
return el
|
||||
}).content = metaDescription
|
||||
|
||||
if (cfg.seo?.twitter?.handle) {
|
||||
ensureMeta('meta[name="twitter:site"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'twitter:site'
|
||||
return el
|
||||
}).content = cfg.seo.twitter.handle
|
||||
}
|
||||
|
||||
if (cfg.seo?.defaultImage) {
|
||||
const image = cfg.seo.defaultImage.startsWith('http')
|
||||
? cfg.seo.defaultImage
|
||||
: `${seoOrigin(cfg)}${cfg.seo.defaultImage}`
|
||||
ensureMeta('meta[property="og:image"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.setAttribute('property', 'og:image')
|
||||
return el
|
||||
}).content = image
|
||||
ensureMeta('meta[name="twitter:image"]', () => {
|
||||
const el = document.createElement('meta')
|
||||
el.name = 'twitter:image'
|
||||
return el
|
||||
}).content = image
|
||||
}
|
||||
|
||||
let jsonLd = document.querySelector('script#aiui-jsonld') as HTMLScriptElement | null
|
||||
if (!jsonLd) {
|
||||
jsonLd = document.createElement('script')
|
||||
jsonLd.type = 'application/ld+json'
|
||||
jsonLd.id = 'aiui-jsonld'
|
||||
document.head.appendChild(jsonLd)
|
||||
}
|
||||
jsonLd.textContent = JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'TechArticle',
|
||||
headline: title,
|
||||
description: metaDescription,
|
||||
url: absoluteUrl,
|
||||
isPartOf: {
|
||||
'@type': 'WebSite',
|
||||
name: cfg.site?.title || 'Documentation',
|
||||
url: `${seoOrigin(cfg)}/`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const selectNavItem = (item: NavItem) => {
|
||||
const path = normalizeDocPath(item.path || '/')
|
||||
setSelectedItem(item)
|
||||
setActiveItemPath(item.path || item.title)
|
||||
updateSEO(item.title, path, item.description, undefined, item.noindex)
|
||||
}
|
||||
|
||||
// Keep React nav state in sync when plugins perform SPA navigation
|
||||
useEffect(() => {
|
||||
const onPluginNavigate = (event: Event) => {
|
||||
const detail = (event as CustomEvent<{ path?: string }>).detail
|
||||
const path = normalizeDocPath(detail?.path ?? window.location.pathname)
|
||||
setCurrentPath(path)
|
||||
setMobileNavOpen(false)
|
||||
|
||||
const found = findNavItemByPath(nav, path)
|
||||
if (found) {
|
||||
selectNavItem(found)
|
||||
return
|
||||
}
|
||||
|
||||
if (path === '/') {
|
||||
const docsHome = findNavItemByPath(nav, '/docs/index')
|
||||
if (docsHome) {
|
||||
selectNavItem(docsHome)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('aiui:navigate', onPluginNavigate)
|
||||
return () => window.removeEventListener('aiui:navigate', onPluginNavigate)
|
||||
}, [nav, config])
|
||||
|
||||
useEffect(() => {
|
||||
async function loadManifests() {
|
||||
try {
|
||||
@@ -156,19 +273,33 @@ export default function App() {
|
||||
|
||||
// Apply theme from YAML config
|
||||
const theme = configData.site?.theme
|
||||
const yamlDark = theme?.darkMode !== false
|
||||
applyTheme(
|
||||
theme?.preset || 'zinc',
|
||||
theme?.darkMode !== false,
|
||||
yamlDark,
|
||||
theme?.radius || 'md'
|
||||
)
|
||||
applyStoredThemePreference(yamlDark)
|
||||
|
||||
// Handle initial URL path for SPA routing
|
||||
const currentPath = window.location.pathname
|
||||
if (currentPath && currentPath !== '/') {
|
||||
const found = findNavItemByPath(navData, currentPath)
|
||||
const initialPath = normalizeDocPath(window.location.pathname)
|
||||
setCurrentPath(initialPath)
|
||||
if (initialPath !== window.location.pathname) {
|
||||
window.history.replaceState(null, '', initialPath + window.location.search + window.location.hash)
|
||||
}
|
||||
if (initialPath !== '/') {
|
||||
const found = findNavItemByPath(navData, initialPath)
|
||||
if (found) {
|
||||
setSelectedItem(found)
|
||||
setActiveItemPath(found.path || found.title)
|
||||
updateSEO(found.title, initialPath, found.description, configData, found.noindex)
|
||||
}
|
||||
} else {
|
||||
const docsHome = findNavItemByPath(navData, '/docs/index')
|
||||
if (docsHome) {
|
||||
setSelectedItem(docsHome)
|
||||
setActiveItemPath(docsHome.path || docsHome.title)
|
||||
updateSEO(docsHome.title, normalizeDocPath(docsHome.path || '/docs/index'), docsHome.description, configData, docsHome.noindex)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -184,19 +315,21 @@ export default function App() {
|
||||
// Handle browser back/forward navigation in a separate effect
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
const path = window.location.pathname
|
||||
const path = normalizeDocPath(window.location.pathname)
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,18 +338,47 @@ export default function App() {
|
||||
}, [nav, config])
|
||||
|
||||
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, '-')}`
|
||||
const path = normalizeDocPath(item.path || `/${item.title.toLowerCase().replace(/\s+/g, '-')}`)
|
||||
window.history.pushState({ path }, item.title, path)
|
||||
// Update SEO meta tags
|
||||
updateSEO(item.title, path)
|
||||
window.dispatchEvent(new CustomEvent('aiui:navigate', { detail: { 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 = true
|
||||
|
||||
const docsChrome = (
|
||||
<>
|
||||
<MobileNavSheet
|
||||
open={mobileNavOpen}
|
||||
onOpenChange={setMobileNavOpen}
|
||||
nav={filteredNav}
|
||||
activeItem={activeItemPath}
|
||||
onItemClick={handleItemClick}
|
||||
title={config.site?.title || 'Navigation'}
|
||||
/>
|
||||
<Header
|
||||
config={config}
|
||||
templateKey={activeTemplateKey}
|
||||
onMenuClick={() => setMobileNavOpen(true)}
|
||||
navTabs={navTabs}
|
||||
activeTabIndex={activeTabIndex}
|
||||
onTabChange={setActiveTabIndex}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="dark flex items-center justify-center min-h-screen bg-background">
|
||||
<div className="flex items-center justify-center min-h-screen bg-background">
|
||||
<span className="text-muted-foreground">Loading...</span>
|
||||
</div>
|
||||
)
|
||||
@@ -224,53 +386,49 @@ export default function App() {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="dark flex flex-col items-center justify-center min-h-screen gap-4 bg-background">
|
||||
<div className="flex flex-col items-center justify-center min-h-screen gap-4 bg-background">
|
||||
<h2 className="text-xl font-semibold text-destructive">Failed to load</h2>
|
||||
<p className="text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 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} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} currentPath={currentPath} />
|
||||
</div>
|
||||
)
|
||||
case 'CenteredLayout':
|
||||
// Centered content, no sidebar
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full max-w-4xl px-6">
|
||||
{zones?.hero && zones.hero.length > 0 && <ZoneWidgets widgets={zones.hero} />}
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} currentPath={currentPath} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
case 'FullWidthLayout':
|
||||
// Full width content
|
||||
return (
|
||||
<div className="px-6">
|
||||
{zones?.hero && zones.hero.length > 0 && <ZoneWidgets widgets={zones.hero} />}
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
<div className="flex">
|
||||
{(nav?.items?.length ?? 0) > 0 && (
|
||||
<Sidebar nav={nav} activeItem={activeItemPath} onItemClick={handleItemClick} />
|
||||
)}
|
||||
<div className="flex-1 px-6">
|
||||
{zones?.hero && zones.hero.length > 0 && <ZoneWidgets widgets={zones.hero} />}
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} currentPath={currentPath} />
|
||||
</div>
|
||||
</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} />}
|
||||
@@ -285,9 +443,9 @@ export default function App() {
|
||||
</aside>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} currentPath={currentPath} />
|
||||
</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 +462,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} />
|
||||
<div className="docs-shell flex w-full min-h-[calc(100vh-3.5rem)]">
|
||||
<Sidebar nav={filteredNav} activeItem={activeItemPath} onItemClick={handleItemClick} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} currentPath={currentPath} />
|
||||
{showToc && <Toc selectedItem={selectedItem} zones={zones} showToc={showToc} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -347,14 +504,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 +518,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 +542,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>
|
||||
)
|
||||
}
|
||||
|
||||
+133
-50
@@ -1,109 +1,191 @@
|
||||
// Content component — markdown rendering and landing page
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Children, isValidElement, 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 { MermaidDiagram } from './markdown/MermaidDiagram'
|
||||
import { slugify } from './markdown/slug'
|
||||
import { MobileToc } from './Toc'
|
||||
import { docPathToMarkdown, isInternalDocHref, normalizeDocHref, normalizeDocPath } from './pathUtils'
|
||||
import type { UIConfig, NavItem, RouteManifest } from './types'
|
||||
|
||||
interface ContentProps {
|
||||
config: UIConfig
|
||||
routes: RouteManifest
|
||||
selectedItem: NavItem | null
|
||||
currentPath: string
|
||||
}
|
||||
|
||||
export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
export function Content({ config, routes, selectedItem, currentPath }: ContentProps) {
|
||||
const [markdown, setMarkdown] = useState<string>('')
|
||||
const [loadedPath, setLoadedPath] = useState('')
|
||||
const [loadingContent, setLoadingContent] = useState(false)
|
||||
const articleRef = useRef<HTMLElement>(null)
|
||||
const theme = config.site?.theme
|
||||
|
||||
// Load markdown content when selected item changes
|
||||
// Load markdown from URL path (source of truth) to avoid stale selectedItem races
|
||||
useEffect(() => {
|
||||
if (!selectedItem?.path) {
|
||||
setMarkdown('')
|
||||
const urlPath = normalizeDocPath(currentPath)
|
||||
if (urlPath === '/') {
|
||||
setLoadingContent(false)
|
||||
setLoadedPath('')
|
||||
if (!selectedItem?.path) {
|
||||
setMarkdown('')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const mdUrl = docPathToMarkdown(urlPath)
|
||||
const pageTitle = urlPath.split('/').pop()?.replace(/-/g, ' ') ?? 'page'
|
||||
const controller = new AbortController()
|
||||
|
||||
setMarkdown('')
|
||||
setLoadedPath('')
|
||||
setLoadingContent(true)
|
||||
|
||||
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}`)
|
||||
const response = await fetch(mdUrl, { signal: controller.signal })
|
||||
if (controller.signal.aborted) return
|
||||
if (response.ok) {
|
||||
const content = await response.text()
|
||||
setMarkdown(content)
|
||||
setMarkdown(preprocessMkdocsMarkdown(content))
|
||||
setLoadedPath(urlPath)
|
||||
window.dispatchEvent(new CustomEvent('aiui:content-loaded', {
|
||||
detail: { path: urlPath },
|
||||
}))
|
||||
} else {
|
||||
setMarkdown(`*Content for **${selectedItem.title}** not found.*`)
|
||||
setMarkdown(`*Content for **${pageTitle}** not found.*`)
|
||||
setLoadedPath(urlPath)
|
||||
}
|
||||
} catch {
|
||||
setMarkdown(`*Failed to load content for **${selectedItem.title}**.*`)
|
||||
} catch (err) {
|
||||
if (controller.signal.aborted) return
|
||||
setMarkdown(`*Failed to load content for **${pageTitle}**.*`)
|
||||
setLoadedPath(urlPath)
|
||||
} finally {
|
||||
setLoadingContent(false)
|
||||
if (!controller.signal.aborted) {
|
||||
setLoadingContent(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadContent()
|
||||
}, [selectedItem])
|
||||
return () => {
|
||||
controller.abort()
|
||||
setLoadingContent(false)
|
||||
}
|
||||
}, [currentPath, selectedItem?.path])
|
||||
|
||||
useEffect(() => {
|
||||
if (!markdown || !articleRef.current) return
|
||||
const article = articleRef.current
|
||||
const frame = window.requestAnimationFrame(() => {
|
||||
enhanceMkdocsDom(article)
|
||||
})
|
||||
return () => window.cancelAnimationFrame(frame)
|
||||
}, [markdown])
|
||||
|
||||
const headingId = (children?: React.ReactNode) => slugify(String(children ?? ''))
|
||||
|
||||
const mermaidChartFromPre = (children?: React.ReactNode): string | null => {
|
||||
try {
|
||||
const child = Children.only(children)
|
||||
if (!isValidElement<{ className?: string; children?: React.ReactNode }>(child)) return null
|
||||
const className = child.props.className ?? ''
|
||||
if (!className.includes('language-mermaid')) return null
|
||||
return String(child.props.children ?? '').replace(/\n$/, '').trim()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Custom components for react-markdown with Tailwind styling
|
||||
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>,
|
||||
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>,
|
||||
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-foreground leading-relaxed">{children}</p>,
|
||||
a: ({ href, children }: { href?: string; children?: React.ReactNode }) => {
|
||||
const seoHref = href && isInternalDocHref(href) ? normalizeDocHref(href) : href
|
||||
return (
|
||||
<a href={seoHref} 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>,
|
||||
ol: ({ children }: { children?: React.ReactNode }) => <ol className="list-decimal pl-6 my-4 space-y-1">{children}</ol>,
|
||||
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>,
|
||||
li: ({ children }: { children?: React.ReactNode }) => <li className="text-foreground">{children}</li>,
|
||||
blockquote: ({ children }: { children?: React.ReactNode }) => <blockquote className="border-l-4 border-primary pl-4 my-4 italic text-foreground/80">{children}</blockquote>,
|
||||
code: ({ className, children }: { className?: string; children?: React.ReactNode }) => {
|
||||
// Check if this is inside a pre block (fenced code block)
|
||||
if (className?.includes('language-mermaid')) {
|
||||
return <MermaidDiagram chart={String(children ?? '').replace(/\n$/, '').trim()} />
|
||||
}
|
||||
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 }) => {
|
||||
if (isValidElement(children) && children.type === MermaidDiagram) {
|
||||
return children
|
||||
}
|
||||
const chart = mermaidChartFromPre(children)
|
||||
if (chart) return <MermaidDiagram chart={chart} />
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
},
|
||||
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>
|
||||
),
|
||||
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>,
|
||||
tr: ({ children }: { children?: React.ReactNode }) => <tr className="border-b">{children}</tr>,
|
||||
th: ({ children }: { children?: React.ReactNode }) => <th className="px-4 py-2 text-left font-medium">{children}</th>,
|
||||
td: ({ children }: { children?: React.ReactNode }) => <td className="px-4 py-2 text-muted-foreground">{children}</td>,
|
||||
td: ({ children }: { children?: React.ReactNode }) => <td className="px-4 py-2 text-foreground">{children}</td>,
|
||||
hr: () => <hr className="my-6 border-border" />,
|
||||
strong: ({ children }: { children?: React.ReactNode }) => <strong className="font-semibold text-foreground">{children}</strong>,
|
||||
em: ({ children }: { children?: React.ReactNode }) => <em>{children}</em>,
|
||||
}
|
||||
|
||||
const contentReady = loadedPath === normalizeDocPath(currentPath) && markdown.length > 0
|
||||
|
||||
if (selectedItem) {
|
||||
return (
|
||||
<main id="main-content" className="flex-1 p-8 max-w-3xl">
|
||||
{loadingContent ? (
|
||||
<div className="text-muted-foreground">Loading content...</div>
|
||||
) : markdown ? (
|
||||
<article className="prose max-w-none">
|
||||
<Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{markdown}
|
||||
</Markdown>
|
||||
</article>
|
||||
) : (
|
||||
<div className="bg-muted/50 border rounded-lg p-6">
|
||||
<p className="text-muted-foreground">
|
||||
Content for <strong className="text-primary">{selectedItem.title}</strong> would be displayed here.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<main id="main-content" className="docs-main flex-1 min-w-0">
|
||||
<div className="docs-main-inner mx-auto w-full max-w-[720px] px-6 py-8 lg:px-8">
|
||||
<MobileToc selectedItem={selectedItem} />
|
||||
{loadingContent && (
|
||||
<div className="text-muted-foreground animate-pulse">Loading content...</div>
|
||||
)}
|
||||
{contentReady && (
|
||||
<article
|
||||
key={currentPath}
|
||||
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>
|
||||
</article>
|
||||
)}
|
||||
{!loadingContent && !contentReady && loadedPath === normalizeDocPath(currentPath) && (
|
||||
<div className="bg-muted/50 border rounded-lg p-6">
|
||||
<p className="text-muted-foreground">
|
||||
Content for <strong className="text-primary">{selectedItem.title}</strong> would be displayed here.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="flex-1 p-8 max-w-3xl">
|
||||
<main id="main-content" className="docs-main flex-1 min-w-0">
|
||||
<div className="docs-main-inner mx-auto w-full max-w-[720px] px-6 py-8 lg:px-8">
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
{config.site?.title || 'Documentation'}
|
||||
</h1>
|
||||
@@ -118,7 +200,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,9 +232,10 @@ 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>
|
||||
</div>
|
||||
</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]
|
||||
|
||||
+130
-30
@@ -1,10 +1,28 @@
|
||||
// Header component
|
||||
import type { UIConfig } from './types'
|
||||
// Header component — Mintlify-style docs chrome
|
||||
import { Github, Menu, Search } from 'lucide-react'
|
||||
import type { NavTabConfig, UIConfig } from './types'
|
||||
import { LocaleSwitcher } from './i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ThemeToggle } from '@/components/theme-toggle'
|
||||
|
||||
export function Header({ config }: { config: UIConfig }) {
|
||||
// Resolve header component via template slot ref (e.g., header_main)
|
||||
const headerSlot = config.templates?.docs?.slots?.header
|
||||
interface HeaderProps {
|
||||
config: UIConfig
|
||||
templateKey?: string
|
||||
onMenuClick?: () => void
|
||||
navTabs?: NavTabConfig[]
|
||||
activeTabIndex?: number
|
||||
onTabChange?: (index: number) => void
|
||||
}
|
||||
|
||||
export function Header({
|
||||
config,
|
||||
templateKey = 'docs',
|
||||
onMenuClick,
|
||||
navTabs,
|
||||
activeTabIndex = 0,
|
||||
onTabChange,
|
||||
}: HeaderProps) {
|
||||
const headerSlot = config.templates?.[templateKey]?.slots?.header
|
||||
const headerRef = headerSlot?.ref
|
||||
const headerComponent = headerRef
|
||||
? config.components?.[headerRef]
|
||||
@@ -16,56 +34,138 @@ 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)
|
||||
const logoHref = logoConfig?.href || '/'
|
||||
const logoText = header?.logoText || config.site?.title || 'PraisonAIUI'
|
||||
|
||||
const tabs = navTabs ?? config.navigation?.tabs ?? []
|
||||
const navbar = config.navbar
|
||||
const navbarLinks = navbar?.links ?? header?.links ?? []
|
||||
const primary = navbar?.primary ?? (header?.cta ? { type: 'button' as const, label: header.cta.label, href: header.cta.href } : undefined)
|
||||
const showMintlifyNav = tabs.length > 0
|
||||
|
||||
const onLogoClick = (event: React.MouseEvent) => {
|
||||
event.preventDefault()
|
||||
window.history.pushState({}, '', logoHref)
|
||||
window.dispatchEvent(new CustomEvent('aiui:navigate', { detail: { path: logoHref } }))
|
||||
}
|
||||
|
||||
const onSearchClick = () => {
|
||||
window.dispatchEvent(new CustomEvent('aiui:search-open'))
|
||||
}
|
||||
|
||||
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) => {
|
||||
e.preventDefault()
|
||||
window.history.pushState({}, '', logoHref)
|
||||
window.location.reload()
|
||||
}}>
|
||||
<header
|
||||
className="sticky top-0 z-50 border-b border-border/40 bg-background/90 backdrop-blur-xl supports-[backdrop-filter]:bg-background/75"
|
||||
data-aiui-header-tabs={showMintlifyNav ? 'true' : undefined}
|
||||
>
|
||||
<div className="flex h-14 items-center gap-4 px-4 lg:px-6">
|
||||
{onMenuClick && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="md:hidden shrink-0 -ml-1"
|
||||
onClick={onMenuClick}
|
||||
aria-label="Open navigation menu"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<a
|
||||
href={logoHref}
|
||||
className="flex items-center gap-2.5 group cursor-pointer shrink-0 min-w-0"
|
||||
onClick={onLogoClick}
|
||||
>
|
||||
{logoSrc ? (
|
||||
<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"
|
||||
<img
|
||||
src={logoSrc}
|
||||
alt="Logo"
|
||||
className="h-7 w-7 rounded-md object-contain"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary/80 to-primary flex items-center justify-center shadow-lg shadow-primary/20 group-hover:shadow-primary/40 transition-shadow">
|
||||
<span className="text-primary-foreground text-xs font-bold">AI</span>
|
||||
<div className="h-7 w-7 rounded-md bg-gradient-to-br from-primary/80 to-primary flex items-center justify-center">
|
||||
<span className="text-primary-foreground text-[10px] 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-[15px] tracking-tight truncate hidden sm:inline">{logoText}</span>
|
||||
</a>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-1 flex-1">
|
||||
{header?.links?.map((link) => (
|
||||
{showMintlifyNav && (
|
||||
<nav className="hidden md:flex items-center gap-1 ml-2" aria-label="Primary docs sections">
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
key={tab.tab}
|
||||
type="button"
|
||||
onClick={() => onTabChange?.(index)}
|
||||
className={`px-3 py-1.5 text-sm rounded-md transition-colors ${index === activeTabIndex
|
||||
? 'text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{tab.tab}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
{!showMintlifyNav && (
|
||||
<nav className="hidden md:flex items-center gap-1 flex-1 ml-2">
|
||||
{navbarLinks.map((link) => (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors rounded-md hover:bg-muted/50"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<div className="flex-1 min-w-0" />
|
||||
|
||||
{config.search?.enabled !== false && showMintlifyNav && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSearchClick}
|
||||
className="hidden sm:flex items-center gap-2 h-9 w-full max-w-[220px] px-3 text-sm text-muted-foreground border border-border/60 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors"
|
||||
aria-label="Search documentation"
|
||||
>
|
||||
<Search className="h-4 w-4 shrink-0" />
|
||||
<span className="flex-1 text-left truncate">Search...</span>
|
||||
<kbd className="hidden lg:inline text-[10px] font-medium text-muted-foreground/80 border border-border/60 rounded px-1.5 py-0.5">⌘K</kbd>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
{showMintlifyNav && navbarLinks.map((link) => (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="px-3 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors rounded-md hover:bg-muted/50"
|
||||
className="hidden lg:inline-flex px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<LocaleSwitcher />
|
||||
{header?.cta && (
|
||||
{primary && (
|
||||
<a
|
||||
href={header.cta.href}
|
||||
className="inline-flex items-center justify-center rounded-md text-sm font-medium bg-primary text-primary-foreground shadow h-9 px-4 hover:bg-primary/90 transition-colors"
|
||||
href={primary.href}
|
||||
target={primary.href.startsWith('http') ? '_blank' : undefined}
|
||||
rel={primary.href.startsWith('http') ? 'noopener noreferrer' : undefined}
|
||||
className="inline-flex items-center justify-center gap-1.5 rounded-lg text-sm font-medium bg-primary text-primary-foreground h-9 px-3.5 hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
{header.cta.label}
|
||||
{primary.type === 'github' && <Github className="h-4 w-4" />}
|
||||
{primary.label ?? (primary.type === 'github' ? 'GitHub' : 'Get started')}
|
||||
</a>
|
||||
)}
|
||||
|
||||
<ThemeToggle />
|
||||
<LocaleSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,55 +1,111 @@
|
||||
// Sidebar component — navigation tree
|
||||
// Sidebar component — Mintlify-style navigation tree
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
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 { normalizeDocPath } from './pathUtils'
|
||||
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
|
||||
|
||||
if (hasChildren) {
|
||||
return (
|
||||
<div key={item.title + (item.path || '')}>
|
||||
<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">
|
||||
<div key={item.title + (item.path || '')} className="mb-4">
|
||||
<div className="flex items-center gap-1 px-3 py-1.5 text-[11px] font-semibold text-muted-foreground/80 uppercase tracking-wider">
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="space-y-0.5">
|
||||
<div className="space-y-0.5 mt-1">
|
||||
{item.children!.map((child) => renderItem(child, depth + 1))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const href = normalizeDocPath(item.path || '/')
|
||||
|
||||
return (
|
||||
<button
|
||||
<a
|
||||
key={item.title + (item.path || '')}
|
||||
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'
|
||||
href={href}
|
||||
data-nav-path={item.path || ''}
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
onItemClick(item)
|
||||
}}
|
||||
className={`group flex items-center gap-1.5 w-full text-left px-3 py-1.5 text-[13px] rounded-md transition-colors ${isActive
|
||||
? 'text-primary font-medium bg-primary/10'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
|
||||
}`}
|
||||
style={{ paddingLeft: `${12 + depth * 12}px` }}
|
||||
style={{ paddingLeft: `${12 + depth * 10}px` }}
|
||||
>
|
||||
{item.title}
|
||||
</button>
|
||||
{depth > 0 && (
|
||||
<ChevronRight className={`h-3 w-3 shrink-0 opacity-0 group-hover:opacity-40 ${isActive ? 'opacity-60 text-primary' : ''}`} />
|
||||
)}
|
||||
<span className="truncate">{item.title}</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
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>
|
||||
<Separator className="my-4" />
|
||||
<nav className="space-y-0.5 pb-6">
|
||||
{nav.items?.map((group) => renderItem(group))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
interface SidebarProps extends NavTreeProps {
|
||||
nav: DocsNav
|
||||
}
|
||||
|
||||
export function Sidebar({ nav, activeItem, onItemClick }: SidebarProps) {
|
||||
return (
|
||||
<aside className="docs-sidebar w-[17rem] min-w-[17rem] shrink-0 hidden md:block border-r border-border/40">
|
||||
<ScrollArea className="h-[calc(100vh-3.5rem)] sticky top-14">
|
||||
<div className="py-5 px-2">
|
||||
<NavTree nav={nav} activeItem={activeItem} onItemClick={onItemClick} />
|
||||
</div>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
+137
-41
@@ -1,51 +1,147 @@
|
||||
// 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 }) {
|
||||
const rightSidebarWidgets = zones?.rightSidebar || []
|
||||
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 [activeId, setActiveId] = useState('')
|
||||
|
||||
const refreshHeadings = useCallback(() => {
|
||||
setHeadings(collectHeadings())
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
refreshHeadings()
|
||||
const timer = window.setTimeout(refreshHeadings, 100)
|
||||
const timer2 = window.setTimeout(refreshHeadings, 500)
|
||||
return () => {
|
||||
window.clearTimeout(timer)
|
||||
window.clearTimeout(timer2)
|
||||
}
|
||||
}, [selectedItem, refreshHeadings])
|
||||
|
||||
useEffect(() => {
|
||||
if (!headings.length) return
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const visible = entries
|
||||
.filter((entry) => entry.isIntersecting)
|
||||
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
|
||||
if (visible[0]?.target.id) {
|
||||
setActiveId(visible[0].target.id)
|
||||
}
|
||||
},
|
||||
{ rootMargin: '-20% 0px -70% 0px', threshold: 0 },
|
||||
)
|
||||
|
||||
for (const heading of headings) {
|
||||
const el = document.getElementById(heading.id)
|
||||
if (el) observer.observe(el)
|
||||
}
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [headings])
|
||||
|
||||
return { headings, activeId }
|
||||
}
|
||||
|
||||
function TocLinks({ headings, activeId }: { headings: TocHeading[]; activeId: string }) {
|
||||
if (headings.length === 0) {
|
||||
return <p className="text-sm text-muted-foreground">No sections on this page.</p>
|
||||
}
|
||||
|
||||
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>
|
||||
<div className="space-y-2.5">
|
||||
{headings.map((heading) => {
|
||||
const isActive = heading.id === activeId
|
||||
return (
|
||||
<a
|
||||
key={heading.id}
|
||||
href={`#${heading.id}`}
|
||||
className={`flex items-start gap-2 text-[13px] leading-snug transition-colors ${heading.level === 3 ? 'pl-3' : heading.level === 4 ? 'pl-5' : ''
|
||||
} ${isActive
|
||||
? 'text-primary font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`mt-[0.45rem] h-1.5 w-1.5 shrink-0 rounded-full ${isActive ? 'bg-primary' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
/>
|
||||
<span>{heading.text}</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{/* Zone Widgets (excluding Toc) */}
|
||||
{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')} />
|
||||
export function MobileToc({ selectedItem }: { selectedItem: NavItem | null }) {
|
||||
const { headings, activeId } = usePageHeadings(selectedItem)
|
||||
if (!selectedItem || headings.length === 0) return null
|
||||
|
||||
return (
|
||||
<details className="lg:hidden border border-border/60 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} activeId={activeId} />
|
||||
</nav>
|
||||
</details>
|
||||
)
|
||||
}
|
||||
|
||||
export function Toc({
|
||||
selectedItem,
|
||||
zones,
|
||||
showToc = true,
|
||||
}: {
|
||||
selectedItem: NavItem | null
|
||||
zones?: ZonesConfig
|
||||
showToc?: boolean
|
||||
}) {
|
||||
const { headings, activeId } = usePageHeadings(selectedItem)
|
||||
const rightSidebarWidgets = zones?.rightSidebar || []
|
||||
|
||||
if (!showToc && rightSidebarWidgets.filter((w) => w.type !== 'Toc').length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="docs-toc w-[16rem] min-w-[16rem] shrink-0 hidden lg:block">
|
||||
<div className="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto px-4 py-6">
|
||||
{showToc && (
|
||||
<div>
|
||||
<h4 className="text-[11px] font-semibold text-muted-foreground/80 uppercase tracking-wider mb-4">
|
||||
On this page
|
||||
</h4>
|
||||
<nav>
|
||||
<TocLinks headings={headings} activeId={activeId} />
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rightSidebarWidgets.filter((w) => w.type !== 'Toc').length > 0 && (
|
||||
<div className="pt-4 mt-4 border-t border-border/40">
|
||||
<ZoneWidgets widgets={rightSidebarWidgets.filter((w) => w.type !== 'Toc')} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,22 +6,16 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
type Theme = "light" | "dark" | "system"
|
||||
import { setThemePreference, type ThemePreference } from "../themes"
|
||||
|
||||
interface ThemeToggleProps {
|
||||
theme?: Theme
|
||||
onThemeChange?: (theme: Theme) => void
|
||||
theme?: ThemePreference
|
||||
onThemeChange?: (theme: ThemePreference) => void
|
||||
}
|
||||
|
||||
export function ThemeToggle({ onThemeChange }: ThemeToggleProps) {
|
||||
const setTheme = (newTheme: Theme) => {
|
||||
if (newTheme === "system") {
|
||||
const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
document.documentElement.classList.toggle("dark", systemDark)
|
||||
} else {
|
||||
document.documentElement.classList.toggle("dark", newTheme === "dark")
|
||||
}
|
||||
const setTheme = (newTheme: ThemePreference) => {
|
||||
setThemePreference(newTheme)
|
||||
onThemeChange?.(newTheme)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { NavItem, NavTabConfig } from '../types'
|
||||
|
||||
function sectionTitle(item: NavItem): string {
|
||||
return item.title.trim().toLowerCase()
|
||||
}
|
||||
|
||||
function collectPaths(item: NavItem): string[] {
|
||||
const paths: string[] = []
|
||||
if (item.path) paths.push(item.path.toLowerCase())
|
||||
for (const child of item.children ?? []) {
|
||||
paths.push(...collectPaths(child))
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
function pathMatchesPage(docPath: string, page: string): boolean {
|
||||
const normalizedPage = page.replace(/^\/+/, '').toLowerCase()
|
||||
const normalizedPath = docPath.replace(/\/+$/, '').toLowerCase()
|
||||
|
||||
if (normalizedPage === 'index') {
|
||||
return normalizedPath.endsWith('/index') || normalizedPath === '/docs'
|
||||
}
|
||||
|
||||
const suffix = `/${normalizedPage}`
|
||||
return normalizedPath.endsWith(suffix) || normalizedPath.endsWith(`${suffix}/index`)
|
||||
}
|
||||
|
||||
/** Whether a sidebar group belongs to the given navigation tab (from topnav plugin logic). */
|
||||
export function navItemBelongsToTab(item: NavItem, tab: NavTabConfig): boolean {
|
||||
if (!tab.groups?.length) return false
|
||||
|
||||
const header = sectionTitle(item)
|
||||
const paths = collectPaths(item)
|
||||
|
||||
for (const grp of tab.groups) {
|
||||
const groupName = grp.group.toLowerCase()
|
||||
if (header === groupName) return true
|
||||
|
||||
if (grp.prefix) {
|
||||
const prefix = grp.prefix.toLowerCase()
|
||||
if (header === prefix || header === prefix.replace(/-/g, ' ')) return true
|
||||
|
||||
const prefixPath = `/docs/${prefix}`
|
||||
if (paths.some((path) => path === prefixPath || path.startsWith(`${prefixPath}/`))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (grp.pages?.some((page) => paths.some((path) => pathMatchesPage(path, page)))) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!item.children?.length && grp.pages?.some((page) => {
|
||||
const slug = page.split('/').pop()?.replace(/-/g, ' ') ?? ''
|
||||
return header === slug || header === page.toLowerCase()
|
||||
})) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** Pick the active tab index from the current URL path. */
|
||||
export function detectActiveTabIndex(tabs: NavTabConfig[], path: string): number {
|
||||
const normalized = path.toLowerCase()
|
||||
let bestMatch = -1
|
||||
let bestMatchLen = 0
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i]
|
||||
for (const grp of tab.groups ?? []) {
|
||||
if (!grp.prefix) continue
|
||||
const needle = `/${grp.prefix.toLowerCase()}/`
|
||||
if (normalized.includes(needle) && needle.length > bestMatchLen) {
|
||||
bestMatch = i
|
||||
bestMatchLen = needle.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch >= 0) return bestMatch
|
||||
return 0
|
||||
}
|
||||
|
||||
export function filterNavByTab(items: NavItem[], tab: NavTabConfig | undefined): NavItem[] {
|
||||
if (!tab) return items
|
||||
return items.filter((item) => navItemBelongsToTab(item, tab))
|
||||
}
|
||||
+171
-32
@@ -12,37 +12,37 @@
|
||||
--radius-2xl: calc(var(--radius) + 8px);
|
||||
--radius-3xl: calc(var(--radius) + 12px);
|
||||
--radius-4xl: calc(var(--radius) + 16px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-background: hsl(var(--background));
|
||||
--color-foreground: hsl(var(--foreground));
|
||||
--color-card: hsl(var(--card));
|
||||
--color-card-foreground: hsl(var(--card-foreground));
|
||||
--color-popover: hsl(var(--popover));
|
||||
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||
--color-primary: hsl(var(--primary));
|
||||
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||
--color-secondary: hsl(var(--secondary));
|
||||
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||
--color-muted: hsl(var(--muted));
|
||||
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||
--color-accent: hsl(var(--accent));
|
||||
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||
--color-destructive: hsl(var(--destructive));
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-ring: hsl(var(--ring));
|
||||
--color-chart-1: hsl(var(--chart-1));
|
||||
--color-chart-2: hsl(var(--chart-2));
|
||||
--color-chart-3: hsl(var(--chart-3));
|
||||
--color-chart-4: hsl(var(--chart-4));
|
||||
--color-chart-5: hsl(var(--chart-5));
|
||||
--color-sidebar: hsl(var(--sidebar));
|
||||
--color-sidebar-foreground: hsl(var(--sidebar-foreground));
|
||||
--color-sidebar-primary: hsl(var(--sidebar-primary));
|
||||
--color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));
|
||||
--color-sidebar-accent: hsl(var(--sidebar-accent));
|
||||
--color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
|
||||
--color-sidebar-border: hsl(var(--sidebar-border));
|
||||
--color-sidebar-ring: hsl(var(--sidebar-ring));
|
||||
}
|
||||
|
||||
/* Color variables are injected via theme.css at build time.
|
||||
@@ -52,7 +52,146 @@
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Readable prose in light and dark mode */
|
||||
.prose-neutral {
|
||||
--tw-prose-body: hsl(var(--foreground));
|
||||
--tw-prose-headings: hsl(var(--foreground));
|
||||
--tw-prose-bold: hsl(var(--foreground));
|
||||
--tw-prose-counters: hsl(var(--muted-foreground));
|
||||
--tw-prose-bullets: hsl(var(--muted-foreground));
|
||||
--tw-prose-pre-code: hsl(var(--foreground));
|
||||
--tw-prose-pre-bg: hsl(var(--muted));
|
||||
--tw-prose-code: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.prose pre,
|
||||
.prose-neutral :where(pre) {
|
||||
@apply bg-muted text-foreground border border-border;
|
||||
color: hsl(var(--foreground)) !important;
|
||||
background-color: hsl(var(--muted)) !important;
|
||||
}
|
||||
|
||||
.prose pre code,
|
||||
.prose-neutral :where(pre code) {
|
||||
@apply text-foreground bg-transparent;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.mermaid-diagram {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 1.5rem 0;
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 12px;
|
||||
overflow-x: auto;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.mermaid-diagram-svg {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:root:not(.dark) .mermaid-diagram:not(.mermaid-error) {
|
||||
background: hsl(var(--muted));
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.dark .mermaid-diagram:not(.mermaid-error) {
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||
}
|
||||
|
||||
.mermaid-diagram:not(.mermaid-error):hover {
|
||||
border-color: rgba(20, 184, 166, 0.4);
|
||||
box-shadow: 0 0 24px rgba(13, 148, 136, 0.12);
|
||||
}
|
||||
|
||||
.mermaid-diagram svg {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mermaid-diagram .nodeLabel,
|
||||
.mermaid-diagram .label div,
|
||||
.mermaid-diagram .cluster-label .nodeLabel {
|
||||
color: #ffffff !important;
|
||||
fill: #ffffff !important;
|
||||
font-family: "Inter", system-ui, -apple-system, sans-serif !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
.mermaid-diagram .edgeLabel {
|
||||
color: #e2e8f0 !important;
|
||||
fill: #e2e8f0 !important;
|
||||
background-color: rgba(15, 23, 42, 0.85) !important;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mermaid-diagram .cluster-label .nodeLabel {
|
||||
font-weight: 600 !important;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.mermaid-diagram .node rect,
|
||||
.mermaid-diagram .node circle,
|
||||
.mermaid-diagram .node polygon {
|
||||
rx: 8;
|
||||
ry: 8;
|
||||
}
|
||||
|
||||
/* Mintlify-style docs shell */
|
||||
.docs-shell {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.docs-main-inner {
|
||||
min-height: calc(100vh - 3.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prose readability — must beat @tailwindcss/typography defaults (unlayered) */
|
||||
article.prose :where(p, li, td, th, blockquote, strong, em, h1, h3, h4, h5, h6) {
|
||||
color: hsl(var(--foreground)) !important;
|
||||
-webkit-text-fill-color: unset !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
article.prose :where(h2) {
|
||||
color: hsl(var(--primary)) !important;
|
||||
-webkit-text-fill-color: unset !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
article.prose .aiui-feature-grid > li {
|
||||
color: hsl(var(--foreground)) !important;
|
||||
}
|
||||
|
||||
.dark article.prose.dark\:prose-invert :where(p, li, td, th, blockquote, strong, h1, h2, h3, h4) {
|
||||
color: hsl(var(--foreground)) !important;
|
||||
-webkit-text-fill-color: unset !important;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { useEffect, useId, useRef, useState } from 'react'
|
||||
import { fitMermaidSvg, getMermaidConfig, isDarkMode } from './mermaidThemes'
|
||||
|
||||
let renderCounter = 0
|
||||
|
||||
function nextRenderId(baseId: string): string {
|
||||
renderCounter += 1
|
||||
return `mermaid-${baseId.replace(/:/g, '')}-${renderCounter}`
|
||||
}
|
||||
|
||||
export function MermaidDiagram({ chart }: { chart: string }) {
|
||||
const baseId = useId()
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const svgRef = useRef<HTMLDivElement>(null)
|
||||
const bindFunctionsRef = useRef<((element: Element) => void) | undefined>(undefined)
|
||||
const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading')
|
||||
const [errorMessage, setErrorMessage] = useState('')
|
||||
const [svgMarkup, setSvgMarkup] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current
|
||||
if (!container) return
|
||||
|
||||
let cancelled = false
|
||||
|
||||
const render = async () => {
|
||||
setStatus('loading')
|
||||
setErrorMessage('')
|
||||
setSvgMarkup('')
|
||||
const mermaid = (await import('mermaid')).default
|
||||
mermaid.initialize(getMermaidConfig(isDarkMode()))
|
||||
|
||||
const styles = getComputedStyle(container)
|
||||
const paddingX = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight)
|
||||
const containerWidth = container.clientWidth - paddingX
|
||||
|
||||
try {
|
||||
const { svg, bindFunctions } = await mermaid.render(nextRenderId(baseId), chart)
|
||||
if (cancelled) return
|
||||
bindFunctionsRef.current = bindFunctions
|
||||
setSvgMarkup(fitMermaidSvg(svg, containerWidth))
|
||||
setStatus('ready')
|
||||
} catch (err) {
|
||||
if (cancelled) return
|
||||
setStatus('error')
|
||||
setErrorMessage(err instanceof Error ? err.message : 'Failed to render diagram')
|
||||
}
|
||||
}
|
||||
|
||||
void render()
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
void render()
|
||||
})
|
||||
observer.observe(container)
|
||||
|
||||
const themeObserver = new MutationObserver(() => {
|
||||
void render()
|
||||
})
|
||||
themeObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
observer.disconnect()
|
||||
themeObserver.disconnect()
|
||||
}
|
||||
}, [chart, baseId])
|
||||
|
||||
useEffect(() => {
|
||||
if (status !== 'ready' || !svgRef.current) return
|
||||
bindFunctionsRef.current?.(svgRef.current)
|
||||
}, [status, svgMarkup])
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<pre className="mermaid-diagram mermaid-error text-destructive text-sm my-4 p-4 rounded-lg border border-destructive/30 bg-destructive/5 overflow-x-auto">
|
||||
{errorMessage}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`mermaid-diagram w-full${status === 'loading' ? ' mermaid-loading text-muted-foreground text-sm' : ''}`}
|
||||
aria-busy={status === 'loading'}
|
||||
aria-label={status === 'loading' ? 'Loading diagram' : 'Mermaid diagram'}
|
||||
>
|
||||
{status === 'loading' ? 'Loading diagram…' : null}
|
||||
{svgMarkup ? (
|
||||
<div ref={svgRef} className="mermaid-diagram-svg w-full" dangerouslySetInnerHTML={{ __html: svgMarkup }} />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/** Mermaid theme config — aligned with plugins/mermaid.js */
|
||||
|
||||
export const MERMAID_DARK_THEME = {
|
||||
theme: 'base' as const,
|
||||
themeVariables: {
|
||||
background: 'transparent',
|
||||
mainBkg: '#0d9488',
|
||||
primaryColor: '#0d9488',
|
||||
primaryBorderColor: '#14b8a6',
|
||||
primaryTextColor: '#ffffff',
|
||||
secondaryColor: '#6366f1',
|
||||
secondaryBorderColor: '#818cf8',
|
||||
secondaryTextColor: '#ffffff',
|
||||
tertiaryColor: '#e11d48',
|
||||
tertiaryBorderColor: '#fb7185',
|
||||
tertiaryTextColor: '#ffffff',
|
||||
textColor: '#f1f5f9',
|
||||
labelTextColor: '#f1f5f9',
|
||||
lineColor: '#94a3b8',
|
||||
arrowheadColor: '#cbd5e1',
|
||||
nodeBorder: '#14b8a6',
|
||||
clusterBkg: 'rgba(13, 148, 136, 0.08)',
|
||||
clusterBorder: 'rgba(20, 184, 166, 0.4)',
|
||||
defaultLinkColor: '#94a3b8',
|
||||
edgeLabelBackground: 'rgba(15, 23, 42, 0.85)',
|
||||
nodeTextColor: '#ffffff',
|
||||
actorBkg: '#0d9488',
|
||||
actorBorder: '#14b8a6',
|
||||
actorTextColor: '#ffffff',
|
||||
actorLineColor: '#64748b',
|
||||
signalColor: '#cbd5e1',
|
||||
signalTextColor: '#f1f5f9',
|
||||
activationBorderColor: '#14b8a6',
|
||||
activationBkgColor: 'rgba(13, 148, 136, 0.2)',
|
||||
sequenceNumberColor: '#ffffff',
|
||||
noteBkgColor: 'rgba(99, 102, 241, 0.2)',
|
||||
noteBorderColor: '#818cf8',
|
||||
noteTextColor: '#f1f5f9',
|
||||
titleColor: '#f8fafc',
|
||||
fontFamily: '"Inter", system-ui, -apple-system, sans-serif',
|
||||
fontSize: '16px',
|
||||
},
|
||||
}
|
||||
|
||||
export const MERMAID_LIGHT_THEME = {
|
||||
theme: 'base' as const,
|
||||
themeVariables: {
|
||||
background: 'transparent',
|
||||
mainBkg: '#0d9488',
|
||||
primaryColor: '#0d9488',
|
||||
primaryBorderColor: '#0f766e',
|
||||
primaryTextColor: '#ffffff',
|
||||
secondaryColor: '#6366f1',
|
||||
secondaryBorderColor: '#4f46e5',
|
||||
secondaryTextColor: '#ffffff',
|
||||
tertiaryColor: '#e11d48',
|
||||
tertiaryBorderColor: '#be123c',
|
||||
tertiaryTextColor: '#ffffff',
|
||||
textColor: '#1e293b',
|
||||
labelTextColor: '#334155',
|
||||
lineColor: '#94a3b8',
|
||||
arrowheadColor: '#64748b',
|
||||
nodeBorder: '#0f766e',
|
||||
clusterBkg: 'rgba(13, 148, 136, 0.06)',
|
||||
clusterBorder: 'rgba(15, 118, 110, 0.3)',
|
||||
defaultLinkColor: '#94a3b8',
|
||||
edgeLabelBackground: 'rgba(255, 255, 255, 0.9)',
|
||||
nodeTextColor: '#ffffff',
|
||||
actorBkg: '#0d9488',
|
||||
actorBorder: '#0f766e',
|
||||
actorTextColor: '#ffffff',
|
||||
actorLineColor: '#cbd5e1',
|
||||
signalColor: '#64748b',
|
||||
signalTextColor: '#1e293b',
|
||||
noteBkgColor: 'rgba(99, 102, 241, 0.1)',
|
||||
noteBorderColor: '#6366f1',
|
||||
noteTextColor: '#1e293b',
|
||||
titleColor: '#0f172a',
|
||||
fontFamily: '"Inter", system-ui, -apple-system, sans-serif',
|
||||
fontSize: '16px',
|
||||
},
|
||||
}
|
||||
|
||||
export function isDarkMode(): boolean {
|
||||
return document.documentElement.classList.contains('dark')
|
||||
}
|
||||
|
||||
export function getMermaidConfig(dark: boolean) {
|
||||
const theme = dark ? MERMAID_DARK_THEME : MERMAID_LIGHT_THEME
|
||||
return {
|
||||
startOnLoad: false,
|
||||
securityLevel: 'strict' as const,
|
||||
...theme,
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
htmlLabels: true,
|
||||
curve: 'basis' as const,
|
||||
},
|
||||
sequence: {
|
||||
useMaxWidth: true,
|
||||
wrap: true,
|
||||
},
|
||||
er: { useMaxWidth: true },
|
||||
gantt: { useMaxWidth: true },
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure rendered SVG stretches to the diagram container (Mintlify-style). */
|
||||
export function fitMermaidSvg(svg: string, containerWidth: number): string {
|
||||
const width = Math.max(Math.floor(containerWidth), 320)
|
||||
return svg.replace(/<svg([^>]*)>/, (_match, attrs: string) => {
|
||||
const cleaned = attrs
|
||||
.replace(/\swidth="[^"]*"/g, '')
|
||||
.replace(/\sheight="[^"]*"/g, '')
|
||||
.replace(/\sstyle="[^"]*"/g, '')
|
||||
return `<svg${cleaned} width="${width}" style="width:100%;max-width:100%;height:auto;display:block;">`
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/** Post-render class-only fixes for MkDocs-style content. No DOM moves — React owns the tree. */
|
||||
export function enhanceMkdocsDom(root: HTMLElement): void {
|
||||
const articles = new Set<HTMLElement>()
|
||||
if (root.matches('article.prose, #main-content article')) {
|
||||
articles.add(root)
|
||||
}
|
||||
root.querySelectorAll<HTMLElement>('article.prose, #main-content article').forEach((el) => articles.add(el))
|
||||
|
||||
for (const article of articles) {
|
||||
hideRawDivParagraphs(article)
|
||||
enhanceFeatureGrid(article)
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide MkDocs artefact paragraphs — never remove nodes React may still own. */
|
||||
function hideRawDivParagraphs(article: HTMLElement): void {
|
||||
for (const p of article.querySelectorAll('p')) {
|
||||
const text = p.textContent?.trim() ?? ''
|
||||
if (/^<\/?div[\s>]/.test(text) || text === '</div>') {
|
||||
p.classList.add('hidden')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** No-op — React article must not receive foreign plugin nodes. */
|
||||
export function teardownMkdocsEnhancements(_root: HTMLElement): void {}
|
||||
@@ -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,37 @@
|
||||
/** Canonical docs path: no trailing slash except root. */
|
||||
export function normalizeDocPath(path: string): string {
|
||||
const cleaned = (path || '/').replace(/\/+$/, '')
|
||||
return cleaned || '/'
|
||||
}
|
||||
|
||||
/** Normalise an internal docs href for SEO-friendly crawlable links. */
|
||||
export function normalizeDocHref(href: string): string {
|
||||
if (!href || href.startsWith('#')) return href
|
||||
try {
|
||||
const url = new URL(href, window.location.origin)
|
||||
if (url.origin !== window.location.origin) return href
|
||||
return normalizeDocPath(url.pathname) + url.search + url.hash
|
||||
} catch {
|
||||
return href
|
||||
}
|
||||
}
|
||||
|
||||
export function isInternalDocHref(href?: string): boolean {
|
||||
if (!href || href.startsWith('#')) return false
|
||||
if (href.startsWith('/docs')) return true
|
||||
try {
|
||||
const url = new URL(href, window.location.origin)
|
||||
return url.origin === window.location.origin && url.pathname.startsWith('/docs')
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/** Map a docs URL path to a markdown file path (leading slash, .md suffix). */
|
||||
export function docPathToMarkdown(path: string): string {
|
||||
const normalized = normalizeDocPath(path)
|
||||
if (normalized === '/') {
|
||||
return '/docs/index.md'
|
||||
}
|
||||
return `${normalized}.md`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -136,7 +136,9 @@ export const NAMED_THEME_PRESETS = {
|
||||
|
||||
export type NamedThemePreset = keyof typeof NAMED_THEME_PRESETS
|
||||
|
||||
export type ThemePreset = typeof THEME_PRESETS[number]
|
||||
export const THEME_STORAGE_KEY = 'aiui-theme-preference'
|
||||
|
||||
export type ThemePreference = 'light' | 'dark' | 'system'
|
||||
|
||||
export const RADIUS_PRESETS = {
|
||||
none: '0',
|
||||
@@ -146,6 +148,8 @@ export const RADIUS_PRESETS = {
|
||||
xl: '1rem',
|
||||
} as const
|
||||
|
||||
export type ThemePreset = typeof THEME_PRESETS[number]
|
||||
|
||||
export type RadiusPreset = keyof typeof RADIUS_PRESETS
|
||||
|
||||
// CSS variable names that shadcn/Tailwind expects
|
||||
@@ -199,6 +203,43 @@ export function isDarkMode(): boolean {
|
||||
return document.documentElement.classList.contains('dark')
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply stored user theme preference over YAML default dark mode.
|
||||
*/
|
||||
export function applyStoredThemePreference(yamlDefaultDark: boolean): void {
|
||||
let stored: string | null = null
|
||||
try {
|
||||
stored = localStorage.getItem(THEME_STORAGE_KEY)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
if (stored === 'light') {
|
||||
applyDarkMode(false)
|
||||
} else if (stored === 'dark') {
|
||||
applyDarkMode(true)
|
||||
} else if (stored === 'system') {
|
||||
applyDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
} else {
|
||||
applyDarkMode(yamlDefaultDark)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist and apply a theme preference.
|
||||
*/
|
||||
export function setThemePreference(preference: ThemePreference): void {
|
||||
try {
|
||||
localStorage.setItem(THEME_STORAGE_KEY, preference)
|
||||
} catch {
|
||||
// ignore storage failures
|
||||
}
|
||||
if (preference === 'system') {
|
||||
applyDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
} else {
|
||||
applyDarkMode(preference === 'dark')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available theme presets
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,8 @@ export interface NavItem {
|
||||
title: string
|
||||
href?: string
|
||||
path?: string
|
||||
description?: string
|
||||
noindex?: boolean
|
||||
children?: NavItem[]
|
||||
}
|
||||
|
||||
@@ -24,6 +26,8 @@ export interface SEOConfig {
|
||||
titleTemplate?: string
|
||||
defaultImage?: string
|
||||
twitter?: Record<string, string>
|
||||
siteUrl?: string
|
||||
robots?: { index?: boolean; follow?: boolean }
|
||||
}
|
||||
|
||||
export interface I18nConfig {
|
||||
@@ -47,6 +51,44 @@ export interface LogoConfig {
|
||||
href?: string
|
||||
}
|
||||
|
||||
export interface NavTabGroupConfig {
|
||||
group: string
|
||||
icon?: string
|
||||
prefix?: string
|
||||
pages?: string[]
|
||||
}
|
||||
|
||||
export interface NavTabConfig {
|
||||
tab: string
|
||||
url?: string
|
||||
groups?: NavTabGroupConfig[]
|
||||
}
|
||||
|
||||
export interface NavigationConfig {
|
||||
tabs?: NavTabConfig[]
|
||||
}
|
||||
|
||||
export interface NavbarLinkConfig {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export interface NavbarPrimaryConfig {
|
||||
type?: 'button' | 'github'
|
||||
label?: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export interface NavbarConfig {
|
||||
primary?: NavbarPrimaryConfig
|
||||
links?: NavbarLinkConfig[]
|
||||
}
|
||||
|
||||
export interface SearchConfig {
|
||||
enabled?: boolean
|
||||
provider?: string
|
||||
}
|
||||
|
||||
export interface WidgetConfig {
|
||||
type: string
|
||||
props?: Record<string, unknown>
|
||||
@@ -89,6 +131,13 @@ export interface UIConfig {
|
||||
i18n?: I18nConfig
|
||||
a11y?: A11yConfig
|
||||
logo?: LogoConfig
|
||||
navigation?: NavigationConfig
|
||||
navbar?: NavbarConfig
|
||||
search?: SearchConfig
|
||||
footer?: {
|
||||
socials?: Record<string, string>
|
||||
links?: { header: string; items: NavbarLinkConfig[] }[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface DocsNav {
|
||||
@@ -96,7 +145,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.137"
|
||||
__version__ = "0.3.142"
|
||||
@@ -65,7 +65,8 @@ class Compiler:
|
||||
|
||||
# Ensure output directory exists
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
files: list[str] = []
|
||||
(output_dir / ".nojekyll").touch()
|
||||
files: list[str] = [".nojekyll"]
|
||||
|
||||
indent = None if minify else 2
|
||||
|
||||
@@ -111,6 +112,11 @@ class Compiler:
|
||||
nav = self._generate_docs_nav()
|
||||
route_files = self._generate_route_pages(output_dir, nav)
|
||||
files.extend(route_files)
|
||||
files.extend(self._generate_sitemap(output_dir, nav))
|
||||
files.extend(self._generate_robots(output_dir))
|
||||
files.extend(self._generate_llms_txt(output_dir, nav))
|
||||
files.extend(self._generate_llms_full_txt(output_dir, nav))
|
||||
self._patch_homepage_seo(output_dir, nav)
|
||||
|
||||
return CompileResult(success=True, files=files)
|
||||
|
||||
@@ -322,6 +328,8 @@ class Compiler:
|
||||
"titleTemplate": self.config.seo.title_template,
|
||||
"defaultImage": self.config.seo.default_image,
|
||||
"twitter": self.config.seo.twitter,
|
||||
"siteUrl": self.config.seo.site_url or self._site_url(),
|
||||
"robots": self.config.seo.robots,
|
||||
}
|
||||
|
||||
# A11y configuration - basic implementation for frontend
|
||||
@@ -441,6 +449,7 @@ class Compiler:
|
||||
output_dir / "index.html",
|
||||
)
|
||||
self._patch_antiflicker(output_dir / "index.html")
|
||||
self._patch_static_shell(output_dir / "index.html")
|
||||
# Create 404.html as a copy of patched index.html
|
||||
# for SPA routing on static hosts like GitHub Pages
|
||||
shutil.copy(
|
||||
@@ -470,15 +479,39 @@ class Compiler:
|
||||
self._copy_plugins(output_dir, frontend_dir)
|
||||
|
||||
def _patch_antiflicker(self, html_path: Path) -> None:
|
||||
"""Patch anti-flicker script in index.html to respect darkMode."""
|
||||
"""Patch theme bootstrap in index.html to respect darkMode."""
|
||||
theme = self.config.site.theme
|
||||
dark_mode = theme.dark_mode if theme else True
|
||||
html = html_path.read_text(encoding="utf-8")
|
||||
if not dark_mode:
|
||||
# Remove the dark class addition for light-mode sites
|
||||
html = html.replace("__AIUI_DARK_DEFAULT__", "true" if dark_mode else "false")
|
||||
html_path.write_text(html, encoding="utf-8")
|
||||
|
||||
def _patch_static_shell(self, html_path: Path) -> None:
|
||||
"""Patch built index.html for static hosts (GitHub Pages).
|
||||
|
||||
- Strip trailing slashes from the URL bar before React hydrates.
|
||||
- Load plugin-loader.js (not included in the Vite bundle).
|
||||
"""
|
||||
html = html_path.read_text(encoding="utf-8")
|
||||
canonical_script = (
|
||||
'<script id="aiui-canonical-url">'
|
||||
"(function(){var p=location.pathname.replace(/\\/+$/, '')||'/';"
|
||||
"if(p!==location.pathname){history.replaceState(null,'',"
|
||||
"p+location.search+location.hash);}})();"
|
||||
"</script>"
|
||||
)
|
||||
if 'id="aiui-canonical-url"' not in html:
|
||||
html = html.replace("<head>", f"<head>\n {canonical_script}", 1)
|
||||
if 'rel="sitemap"' not in html:
|
||||
html = html.replace(
|
||||
"document.documentElement.classList.add('dark');",
|
||||
"/* light mode — no dark class */",
|
||||
"<head>",
|
||||
'<head>\n <link rel="sitemap" type="application/xml" href="/sitemap.xml" />',
|
||||
1,
|
||||
)
|
||||
if "plugin-loader.js" not in html:
|
||||
html = html.replace(
|
||||
"</body>",
|
||||
' <script src="/plugins/plugin-loader.js"></script>\n</body>',
|
||||
)
|
||||
html_path.write_text(html, encoding="utf-8")
|
||||
|
||||
@@ -528,8 +561,10 @@ class Compiler:
|
||||
def _generate_route_pages(self, output_dir: Path, nav: dict) -> list[str]:
|
||||
"""Generate per-route HTML files for SPA fallback and SEO.
|
||||
|
||||
For each page in the docs navigation, creates an index.html at
|
||||
the route path (e.g. _site/docs/concepts/configuration/index.html).
|
||||
For each page in the docs navigation, creates a flat HTML file at
|
||||
the route path (e.g. _site/docs/concepts/configuration.html).
|
||||
GitHub Pages serves /docs/concepts/configuration from that file
|
||||
without appending a trailing slash.
|
||||
Each file is a copy of the SPA shell with page-specific:
|
||||
- <title> tag
|
||||
- <meta name="description">
|
||||
@@ -554,15 +589,16 @@ class Compiler:
|
||||
path = page["path"] # e.g. "/docs/concepts/configuration"
|
||||
title = page["title"] # e.g. "YAML Configuration"
|
||||
|
||||
# Build file system path
|
||||
# Build file system path — flat .html file so static hosts (GitHub Pages)
|
||||
# serve /docs/testing without a trailing-slash redirect.
|
||||
relative = path.lstrip("/")
|
||||
page_dir = output_dir / relative
|
||||
page_dir.mkdir(parents=True, exist_ok=True)
|
||||
page_file = page_dir / "index.html"
|
||||
page_file = output_dir / f"{relative}.html"
|
||||
page_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Build page-specific HTML
|
||||
page_title = f"{title} | {site_title}"
|
||||
page_desc = f"{title} - {site_desc}"
|
||||
page_desc = page.get("description") or f"{title} - {site_desc}"
|
||||
page_noindex = bool(page.get("noindex"))
|
||||
|
||||
html = template_html
|
||||
|
||||
@@ -579,50 +615,283 @@ class Compiler:
|
||||
)
|
||||
|
||||
# Add canonical URL, OG tags, and noscript content before </head>
|
||||
seo_tags = self._build_seo_tags(path, page_title, page_desc)
|
||||
seo_tags = self._build_seo_tags(path, page_title, page_desc, noindex=page_noindex)
|
||||
json_ld = self._build_json_ld(path, title, page_desc)
|
||||
|
||||
# Try to load markdown content for noscript pre-rendering
|
||||
noscript_content = self._get_noscript_content(output_dir, path)
|
||||
crawl_nav = self._build_crawl_nav_html(nav)
|
||||
noscript_block = crawl_nav
|
||||
if noscript_content:
|
||||
noscript_block += noscript_content
|
||||
|
||||
# Inject SEO tags before </head>
|
||||
html = html.replace(
|
||||
"</head>",
|
||||
f"{seo_tags}\n</head>",
|
||||
f"{seo_tags}\n{json_ld}\n</head>",
|
||||
)
|
||||
|
||||
# Inject noscript block inside <div id="root">
|
||||
if noscript_content:
|
||||
html = html.replace(
|
||||
'<div id="root"></div>',
|
||||
f'<div id="root"><noscript>{noscript_content}</noscript></div>',
|
||||
)
|
||||
# Inject noscript crawl content inside <div id="root">
|
||||
html = html.replace(
|
||||
'<div id="root"></div>',
|
||||
f'<div id="root"><noscript>{noscript_block}</noscript></div>',
|
||||
)
|
||||
|
||||
page_file.write_text(html, encoding="utf-8")
|
||||
files.append(f"{relative}/index.html")
|
||||
files.append(f"{relative}.html")
|
||||
|
||||
return files
|
||||
|
||||
def _collect_nav_pages(self, nav: dict) -> list[dict]:
|
||||
"""Recursively collect all pages from docs-nav.json."""
|
||||
pages: list[dict] = []
|
||||
for item in nav.get("items", []):
|
||||
if item.get("path"):
|
||||
pages.append({"path": item["path"], "title": item.get("title", "")})
|
||||
for child in item.get("children", []):
|
||||
if child.get("path"):
|
||||
pages.append({"path": child["path"], "title": child.get("title", "")})
|
||||
# Handle deeper nesting if present
|
||||
for grandchild in child.get("children", []):
|
||||
if grandchild.get("path"):
|
||||
pages.append(
|
||||
{"path": grandchild["path"], "title": grandchild.get("title", "")}
|
||||
)
|
||||
|
||||
def walk(items: list[dict]) -> None:
|
||||
for item in items:
|
||||
if item.get("path"):
|
||||
pages.append(
|
||||
{
|
||||
"path": item["path"],
|
||||
"title": item.get("title", ""),
|
||||
"description": item.get("description", ""),
|
||||
"noindex": bool(item.get("noindex")),
|
||||
}
|
||||
)
|
||||
if item.get("children"):
|
||||
walk(item["children"])
|
||||
|
||||
walk(nav.get("items", []))
|
||||
return pages
|
||||
|
||||
def _build_seo_tags(self, path: str, title: str, description: str) -> str:
|
||||
def _site_url(self) -> str | None:
|
||||
if self.config.seo and self.config.seo.site_url:
|
||||
return self.config.seo.site_url.rstrip("/")
|
||||
for candidate in (self.base_path / "CNAME", self.base_path / "docs" / "CNAME"):
|
||||
if candidate.exists():
|
||||
domain = candidate.read_text(encoding="utf-8").strip()
|
||||
if domain and "." in domain:
|
||||
return f"https://{domain.rstrip('/')}"
|
||||
return None
|
||||
|
||||
def _absolute_url(self, path: str) -> str:
|
||||
site = self._site_url()
|
||||
if not site:
|
||||
return path
|
||||
if not path.startswith("/"):
|
||||
path = f"/{path}"
|
||||
return f"{site}{path}"
|
||||
|
||||
def _generate_sitemap(self, output_dir: Path, nav: dict) -> list[str]:
|
||||
site = self._site_url()
|
||||
if not site:
|
||||
return []
|
||||
|
||||
urls = []
|
||||
site_root = self._absolute_url("/")
|
||||
root_lastmod = self._format_lastmod(self.base_path / "docs" / "index.md")
|
||||
urls.append(self._sitemap_url_entry(site_root, root_lastmod))
|
||||
for page in self._collect_nav_pages(nav):
|
||||
if page.get("noindex"):
|
||||
continue
|
||||
path = page["path"].rstrip("/") or "/"
|
||||
loc = self._absolute_url(path)
|
||||
lastmod = self._format_lastmod(self._resolve_md_source(path))
|
||||
urls.append(self._sitemap_url_entry(loc, lastmod))
|
||||
|
||||
xml = (
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
||||
+ "\n".join(urls)
|
||||
+ "\n</urlset>\n"
|
||||
)
|
||||
(output_dir / "sitemap.xml").write_text(xml, encoding="utf-8")
|
||||
return ["sitemap.xml"]
|
||||
|
||||
@staticmethod
|
||||
def _sitemap_url_entry(loc: str, lastmod: str | None) -> str:
|
||||
safe_loc = Compiler._escape_html(loc)
|
||||
if lastmod:
|
||||
return f" <url><loc>{safe_loc}</loc><lastmod>{lastmod}</lastmod></url>"
|
||||
return f" <url><loc>{safe_loc}</loc></url>"
|
||||
|
||||
@staticmethod
|
||||
def _format_lastmod(path: Path | None) -> str | None:
|
||||
if not path or not path.is_file():
|
||||
return None
|
||||
from datetime import UTC, datetime
|
||||
|
||||
mtime = path.stat().st_mtime
|
||||
return datetime.fromtimestamp(mtime, tz=UTC).strftime("%Y-%m-%d")
|
||||
|
||||
def _resolve_md_source(self, route_path: str) -> Path | None:
|
||||
"""Map a docs route path to its source markdown file."""
|
||||
if not self.config.content or not self.config.content.docs:
|
||||
return None
|
||||
|
||||
base = self.config.site.route_base_docs.rstrip("/")
|
||||
if not route_path.startswith(base):
|
||||
return None
|
||||
|
||||
rel = route_path[len(base) :].lstrip("/")
|
||||
docs_dir = self.base_path / self.config.content.docs.dir
|
||||
candidates = [
|
||||
docs_dir / f"{rel}.md",
|
||||
docs_dir / rel / "index.md",
|
||||
docs_dir / rel / "README.md",
|
||||
]
|
||||
if rel in ("", "index"):
|
||||
candidates.insert(0, docs_dir / "index.md")
|
||||
|
||||
for candidate in candidates:
|
||||
if candidate.is_file():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
def _generate_robots(self, output_dir: Path) -> list[str]:
|
||||
robots = self.config.seo.robots if self.config.seo else None
|
||||
allow_index = robots.get("index", True) if robots else True
|
||||
allow_follow = robots.get("follow", True) if robots else True
|
||||
|
||||
lines = ["User-agent: *"]
|
||||
if allow_index:
|
||||
lines.append("Allow: /")
|
||||
else:
|
||||
lines.append("Disallow: /")
|
||||
if not allow_follow:
|
||||
lines.append("Disallow: /docs/")
|
||||
|
||||
site = self._site_url()
|
||||
if site:
|
||||
lines.append(f"Sitemap: {site}/sitemap.xml")
|
||||
lines.append(f"# LLM index: {site}/llms.txt")
|
||||
|
||||
(output_dir / "robots.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
return ["robots.txt"]
|
||||
|
||||
def _generate_llms_txt(self, output_dir: Path, nav: dict) -> list[str]:
|
||||
"""Generate llms.txt index for AI crawlers (llmstxt.org convention)."""
|
||||
site = self._site_url()
|
||||
if not site:
|
||||
return []
|
||||
|
||||
site_title = self.config.site.title
|
||||
site_desc = self.config.site.description or f"Documentation for {site_title}"
|
||||
lines = [
|
||||
f"# {site_title}",
|
||||
"",
|
||||
f"> {site_desc}",
|
||||
"",
|
||||
"## Documentation",
|
||||
"",
|
||||
]
|
||||
|
||||
for page in self._collect_nav_pages(nav):
|
||||
if page.get("noindex"):
|
||||
continue
|
||||
path = page["path"].rstrip("/") or "/"
|
||||
title = page["title"]
|
||||
desc = page.get("description") or title
|
||||
url = self._absolute_url(path)
|
||||
lines.append(f"- [{title}]({url}): {desc}")
|
||||
|
||||
lines.extend(["", "## Full export", "", f"- [Complete documentation]({site}/llms-full.txt): All pages in one file"])
|
||||
content = "\n".join(lines).rstrip() + "\n"
|
||||
(output_dir / "llms.txt").write_text(content, encoding="utf-8")
|
||||
return ["llms.txt"]
|
||||
|
||||
def _generate_llms_full_txt(self, output_dir: Path, nav: dict) -> list[str]:
|
||||
"""Generate llms-full.txt — concatenated markdown for AI crawlers."""
|
||||
site = self._site_url()
|
||||
if not site:
|
||||
return []
|
||||
|
||||
sections: list[str] = []
|
||||
for page in self._collect_nav_pages(nav):
|
||||
if page.get("noindex"):
|
||||
continue
|
||||
md_path = self._resolve_md_source(page["path"])
|
||||
if not md_path:
|
||||
continue
|
||||
body = self._strip_frontmatter(md_path.read_text(encoding="utf-8")).strip()
|
||||
if not body:
|
||||
continue
|
||||
url = self._absolute_url(page["path"].rstrip("/") or "/")
|
||||
title = page["title"]
|
||||
sections.append(f"# {title}\n\nSource: {url}\n\n{body}")
|
||||
|
||||
if not sections:
|
||||
return []
|
||||
|
||||
(output_dir / "llms-full.txt").write_text("\n\n---\n\n".join(sections) + "\n", encoding="utf-8")
|
||||
return ["llms-full.txt"]
|
||||
|
||||
@staticmethod
|
||||
def _strip_frontmatter(content: str) -> str:
|
||||
if content.startswith("---"):
|
||||
end_idx = content.find("---", 3)
|
||||
if end_idx != -1:
|
||||
return content[end_idx + 3 :].lstrip("\n")
|
||||
return content
|
||||
|
||||
def _patch_homepage_seo(self, output_dir: Path, nav: dict) -> None:
|
||||
"""Add canonical and crawl hints to root index.html."""
|
||||
index_path = output_dir / "index.html"
|
||||
if not index_path.exists():
|
||||
return
|
||||
|
||||
docs_home = "/docs/index"
|
||||
for page in self._collect_nav_pages(nav):
|
||||
if page["path"].rstrip("/").endswith("/docs/index") or page["path"] == "/docs/index":
|
||||
docs_home = page["path"].rstrip("/") or "/docs/index"
|
||||
break
|
||||
|
||||
site_title = self.config.site.title
|
||||
site_desc = self.config.site.description or f"Documentation for {site_title}"
|
||||
page_title = f"{site_title}"
|
||||
seo_tags = self._build_seo_tags(docs_home, page_title, site_desc)
|
||||
crawl_nav = self._build_crawl_nav_html(nav)
|
||||
|
||||
html = index_path.read_text(encoding="utf-8")
|
||||
html = html.replace(
|
||||
"<title>Documentation</title>",
|
||||
f"<title>{self._escape_html(page_title)}</title>",
|
||||
)
|
||||
html = html.replace(
|
||||
'content="Documentation built with PraisonAIUI"',
|
||||
f'content="{self._escape_html(site_desc)}"',
|
||||
)
|
||||
if "</head>" in html and 'rel="canonical"' not in html:
|
||||
html = html.replace("</head>", f"{seo_tags}\n</head>", 1)
|
||||
if '<div id="root"></div>' in html:
|
||||
html = html.replace(
|
||||
'<div id="root"></div>',
|
||||
f'<div id="root"><noscript>{crawl_nav}</noscript></div>',
|
||||
)
|
||||
index_path.write_text(html, encoding="utf-8")
|
||||
|
||||
def _build_crawl_nav_html(self, nav: dict) -> str:
|
||||
"""Static nav links for non-JS crawlers."""
|
||||
links: list[str] = []
|
||||
for page in self._collect_nav_pages(nav):
|
||||
path = page["path"].rstrip("/") or "/"
|
||||
title = self._escape_html(page["title"])
|
||||
url = self._escape_html(self._absolute_url(path))
|
||||
links.append(f'<li><a href="{url}">{title}</a></li>')
|
||||
|
||||
if not links:
|
||||
return ""
|
||||
|
||||
return (
|
||||
'<nav aria-label="Documentation">'
|
||||
f'<ul>{"".join(links)}</ul>'
|
||||
"</nav>"
|
||||
)
|
||||
|
||||
def _build_seo_tags(self, path: str, title: str, description: str, *, noindex: bool = False) -> str:
|
||||
"""Build canonical, OG, and Twitter meta tags."""
|
||||
d = self._escape_html(description)
|
||||
p = self._escape_html(path)
|
||||
canonical = self._escape_html(self._absolute_url(path))
|
||||
og_url = canonical
|
||||
|
||||
# Extract original page title by removing the site title suffix if present
|
||||
site_title = self.config.site.title
|
||||
@@ -646,15 +915,19 @@ class Compiler:
|
||||
t = self._escape_html(title)
|
||||
|
||||
tags = [
|
||||
f' <link rel="canonical" href="{p}" />',
|
||||
f' <link rel="canonical" href="{canonical}" />',
|
||||
f' <meta property="og:title" content="{t}" />',
|
||||
f' <meta property="og:description" content="{d}" />',
|
||||
f' <meta property="og:url" content="{p}" />',
|
||||
f' <meta property="og:url" content="{og_url}" />',
|
||||
' <meta property="og:type" content="article" />',
|
||||
]
|
||||
|
||||
# Add default OG image if configured
|
||||
if self.config.seo and self.config.seo.default_image:
|
||||
og_image = self._escape_html(self.config.seo.default_image)
|
||||
image = self.config.seo.default_image
|
||||
if not image.startswith(("http://", "https://")):
|
||||
image = self._absolute_url(image)
|
||||
og_image = self._escape_html(image)
|
||||
tags.append(f' <meta property="og:image" content="{og_image}" />')
|
||||
|
||||
# Add Twitter card tags
|
||||
@@ -667,8 +940,34 @@ class Compiler:
|
||||
handle = self._escape_html(self.config.seo.twitter["handle"])
|
||||
tags.append(f' <meta name="twitter:site" content="{handle}" />')
|
||||
|
||||
robots = self.config.seo.robots if self.config.seo else None
|
||||
if noindex:
|
||||
tags.append(' <meta name="robots" content="noindex, nofollow" />')
|
||||
elif robots:
|
||||
index = "index" if robots.get("index", True) else "noindex"
|
||||
follow = "follow" if robots.get("follow", True) else "nofollow"
|
||||
tags.append(f' <meta name="robots" content="{index}, {follow}" />')
|
||||
|
||||
return "\n".join(tags)
|
||||
|
||||
def _build_json_ld(self, path: str, title: str, description: str) -> str:
|
||||
url = self._absolute_url(path)
|
||||
site_url = self._site_url()
|
||||
payload: dict = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "TechArticle",
|
||||
"headline": title,
|
||||
"description": description,
|
||||
"url": url,
|
||||
}
|
||||
if site_url:
|
||||
payload["isPartOf"] = {
|
||||
"@type": "WebSite",
|
||||
"name": self.config.site.title,
|
||||
"url": f"{site_url}/",
|
||||
}
|
||||
return f' <script type="application/ld+json">{json.dumps(payload, ensure_ascii=False)}</script>'
|
||||
|
||||
def _get_noscript_content(self, output_dir: Path, path: str) -> str:
|
||||
"""Load markdown file and convert to simple HTML for noscript block."""
|
||||
# Try to find the corresponding markdown file
|
||||
@@ -736,14 +1035,36 @@ class Compiler:
|
||||
# List items
|
||||
if re.match(r"^[-*+]\s+", stripped):
|
||||
text = re.sub(r"^[-*+]\s+", "", stripped)
|
||||
result.append(f"<li>{text}</li>")
|
||||
result.append(f"<li>{Compiler._inline_md_to_html(text)}</li>")
|
||||
continue
|
||||
|
||||
# Paragraphs
|
||||
result.append(f"<p>{stripped}</p>")
|
||||
result.append(f"<p>{Compiler._inline_md_to_html(stripped)}</p>")
|
||||
|
||||
return "\n".join(result)
|
||||
|
||||
@staticmethod
|
||||
def _inline_md_to_html(text: str) -> str:
|
||||
"""Convert inline markdown (links, emphasis) for noscript HTML."""
|
||||
escaped = (
|
||||
text.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
)
|
||||
|
||||
def link_repl(match: re.Match[str]) -> str:
|
||||
label = match.group(1)
|
||||
href = match.group(2)
|
||||
if href.startswith("/") or href.startswith("http://") or href.startswith("https://"):
|
||||
safe_href = href.replace('"', """)
|
||||
return f'<a href="{safe_href}">{label}</a>'
|
||||
return label
|
||||
|
||||
escaped = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", link_repl, escaped)
|
||||
escaped = re.sub(r"\*\*([^*]+)\*\*", r"<strong>\1</strong>", escaped)
|
||||
escaped = re.sub(r"`([^`]+)`", r"<code>\1</code>", escaped)
|
||||
return escaped
|
||||
|
||||
@staticmethod
|
||||
def _escape_html(text: str) -> str:
|
||||
"""Escape HTML special characters for meta tags."""
|
||||
|
||||
@@ -15,6 +15,8 @@ class DocPage:
|
||||
slug: str
|
||||
title: str
|
||||
order: int = 0
|
||||
description: str = ""
|
||||
noindex: bool = False
|
||||
frontmatter: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
@@ -82,6 +84,10 @@ class DocsScanner:
|
||||
|
||||
# Determine title (from frontmatter or first heading)
|
||||
title = frontmatter.get("title") or self._extract_title(content) or slug
|
||||
description = str(frontmatter.get("description", "") or "").strip()
|
||||
if not description:
|
||||
description = self._extract_description(content)
|
||||
noindex = bool(frontmatter.get("noindex", False))
|
||||
|
||||
# Extract order from frontmatter or filename
|
||||
order = frontmatter.get("order", 0)
|
||||
@@ -96,9 +102,42 @@ class DocsScanner:
|
||||
slug=slug,
|
||||
title=title,
|
||||
order=order,
|
||||
description=description,
|
||||
noindex=noindex,
|
||||
frontmatter=frontmatter,
|
||||
)
|
||||
|
||||
def _extract_description(self, content: str) -> str:
|
||||
"""First paragraph after frontmatter — used for meta description when omitted."""
|
||||
if content.startswith("---"):
|
||||
end_idx = content.find("---", 3)
|
||||
if end_idx != -1:
|
||||
content = content[end_idx + 3 :]
|
||||
|
||||
in_code = False
|
||||
for line in content.splitlines():
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("```"):
|
||||
in_code = not in_code
|
||||
continue
|
||||
if in_code or not stripped:
|
||||
continue
|
||||
if stripped.startswith("#"):
|
||||
continue
|
||||
if stripped.startswith(("-", "*", "+", "|", ">", "!", "---")):
|
||||
continue
|
||||
|
||||
text = stripped
|
||||
text = re.sub(r"!\[([^\]]*)\]\([^)]+\)", r"\1", text)
|
||||
text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text)
|
||||
text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
|
||||
text = re.sub(r"\*([^*]+)\*", r"\1", text)
|
||||
text = re.sub(r"`([^`]+)`", r"\1", text)
|
||||
text = re.sub(r"\s+", " ", text).strip()
|
||||
if len(text) >= 20:
|
||||
return text[:160].rstrip()
|
||||
return ""
|
||||
|
||||
def _path_to_slug(self, file_path: Path) -> str:
|
||||
"""Convert a file path to a URL slug."""
|
||||
rel_path = file_path.relative_to(self.docs_dir)
|
||||
|
||||
@@ -13,6 +13,8 @@ class NavItem:
|
||||
|
||||
title: str
|
||||
path: str
|
||||
description: str = ""
|
||||
noindex: bool = False
|
||||
children: list["NavItem"] = field(default_factory=list)
|
||||
|
||||
|
||||
@@ -38,7 +40,12 @@ class NavBuilder:
|
||||
full_path = f"{self.base_path}/{page.slug}".rstrip("/")
|
||||
|
||||
# Create NavItem for this page
|
||||
item = NavItem(title=page.title, path=full_path)
|
||||
item = NavItem(
|
||||
title=page.title,
|
||||
path=full_path,
|
||||
description=page.description,
|
||||
noindex=page.noindex,
|
||||
)
|
||||
|
||||
# Determine parent path
|
||||
parts = page.slug.split("/")
|
||||
@@ -76,6 +83,10 @@ class NavBuilder:
|
||||
def _item_to_dict(self, item: NavItem) -> dict:
|
||||
"""Convert a NavItem to dictionary."""
|
||||
result = {"title": item.title, "path": item.path}
|
||||
if item.description:
|
||||
result["description"] = item.description
|
||||
if item.noindex:
|
||||
result["noindex"] = True
|
||||
if item.children:
|
||||
result["children"] = [self._item_to_dict(child) for child in item.children]
|
||||
return result
|
||||
@@ -83,6 +83,7 @@ def auto_register_defaults() -> None:
|
||||
(".workflows", "WorkflowsFeature"),
|
||||
(".guardrails", "GuardrailsFeature"),
|
||||
(".eval", "EvalFeature"),
|
||||
(".training", "TrainingFeature"),
|
||||
(".telemetry", "TelemetryFeature"),
|
||||
(".tracing", "TracingFeature"),
|
||||
(".security", "SecurityFeature"),
|
||||
|
||||
@@ -26,16 +26,23 @@ 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."""
|
||||
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
|
||||
)
|
||||
"""Detect an embedding model access / not-found failure from an exception."""
|
||||
return _is_embedding_error(str(err))
|
||||
|
||||
|
||||
# ── Knowledge Protocol ───────────────────────────────────────────────
|
||||
@@ -102,14 +109,14 @@ 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__}
|
||||
|
||||
def last_search_meta(self) -> Dict[str, Any]:
|
||||
"""Metadata about the most recent search (mode, error)."""
|
||||
return {"mode": "vector", "error": None}
|
||||
|
||||
|
||||
# ── Simple Knowledge Manager (Default, no deps) ─────────────────────
|
||||
|
||||
@@ -264,7 +271,7 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
"using local in-memory index"
|
||||
)
|
||||
self._sdk_knowledge = None
|
||||
elif _is_embedding_unavailable(e):
|
||||
elif _is_embedding_error(err_msg):
|
||||
self._embedding_unavailable = True
|
||||
self._embedding_error = err_msg
|
||||
logger.warning("Embedding model unavailable: %s", e)
|
||||
@@ -275,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,
|
||||
@@ -331,10 +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_unavailable(e):
|
||||
if _is_embedding_error(err_msg):
|
||||
self._embedding_unavailable = True
|
||||
self._embedding_error = str(e)
|
||||
self._embedding_error = err_msg
|
||||
|
||||
# Fallback to local text search
|
||||
query_lower = query.lower()
|
||||
@@ -361,9 +373,6 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
self._last_search_meta = {"mode": "local", "error": None}
|
||||
return results
|
||||
|
||||
def last_search_meta(self) -> Dict[str, Any]:
|
||||
return dict(self._last_search_meta)
|
||||
|
||||
def add_file(
|
||||
self,
|
||||
file_path: str,
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
"""Training Lab feature — read-mostly proxy for praisonai-train sessions.
|
||||
|
||||
Architecture:
|
||||
TrainingFeature (BaseFeatureProtocol)
|
||||
├── reads JSON session files from ~/.praison/train/*.json
|
||||
└── optionally delegates apply to praisonai_train.train.agents.apply_training
|
||||
|
||||
The praisonai-train package is an optional dependency. When it is not installed
|
||||
the feature degrades gracefully: session listing/detail still work from the raw
|
||||
JSON storage, while apply returns a 503 with an install hint.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.routing import Route
|
||||
|
||||
from ._base import BaseFeatureProtocol
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_EARLY_STOP_THRESHOLD = 9.5
|
||||
|
||||
|
||||
def _storage_dir() -> Path:
|
||||
"""Resolve the praisonai-train JSON storage directory."""
|
||||
override = os.environ.get("PRAISON_TRAIN_DIR")
|
||||
if override:
|
||||
return Path(override).expanduser()
|
||||
return Path.home() / ".praison" / "train"
|
||||
|
||||
|
||||
def _train_available() -> bool:
|
||||
"""Return True when the praisonai-train package can be imported."""
|
||||
import importlib.util
|
||||
|
||||
return importlib.util.find_spec("praisonai_train") is not None
|
||||
|
||||
|
||||
def _train_version() -> str | None:
|
||||
try:
|
||||
from importlib.metadata import version
|
||||
|
||||
return version("praisonai-train")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _iter_scores(iterations: list[dict[str, Any]]) -> list[float]:
|
||||
scores: list[float] = []
|
||||
for it in iterations:
|
||||
score = it.get("score")
|
||||
if isinstance(score, (int, float)):
|
||||
scores.append(float(score))
|
||||
return scores
|
||||
|
||||
|
||||
def _best_iteration_index(iterations: list[dict[str, Any]]) -> int | None:
|
||||
best_idx: int | None = None
|
||||
best_score = float("-inf")
|
||||
for idx, it in enumerate(iterations):
|
||||
score = it.get("score")
|
||||
if isinstance(score, (int, float)) and float(score) > best_score:
|
||||
best_score = float(score)
|
||||
best_idx = idx
|
||||
return best_idx
|
||||
|
||||
|
||||
def _summarize_session(session_id: str, path: Path, data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Build a session summary row (parity with praisonai-train list)."""
|
||||
report = data.get("report") or {}
|
||||
metadata = report.get("metadata") or data.get("metadata") or {}
|
||||
iterations = data.get("iterations") or report.get("iterations") or []
|
||||
|
||||
completed = int(report.get("total_iterations", len(iterations)) or 0)
|
||||
requested = metadata.get("target_iterations")
|
||||
requested = int(requested) if isinstance(requested, (int, float)) else completed
|
||||
mode = metadata.get("mode", "llm")
|
||||
agent_id = metadata.get("agent_id") or data.get("agent_id")
|
||||
|
||||
scores = _iter_scores(iterations)
|
||||
avg_score = report.get("avg_score")
|
||||
if avg_score is None and scores:
|
||||
avg_score = sum(scores) / len(scores)
|
||||
improvement = report.get("improvement")
|
||||
if improvement is None and len(scores) >= 2:
|
||||
improvement = scores[-1] - scores[0]
|
||||
passed = report.get("passed")
|
||||
|
||||
early_stopped = bool(requested > completed and mode == "llm")
|
||||
|
||||
try:
|
||||
stat = path.stat()
|
||||
size_bytes = stat.st_size
|
||||
modified_at = _iso_from_mtime(stat.st_mtime)
|
||||
except OSError:
|
||||
size_bytes = 0
|
||||
modified_at = None
|
||||
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"agent_id": agent_id,
|
||||
"completed_iterations": completed,
|
||||
"requested_iterations": requested,
|
||||
"early_stopped": early_stopped,
|
||||
"avg_score": round(float(avg_score), 4) if avg_score is not None else None,
|
||||
"improvement": round(float(improvement), 4) if improvement is not None else None,
|
||||
"passed": passed,
|
||||
"status_label": _status_label(passed, completed),
|
||||
"mode": mode,
|
||||
"size_bytes": size_bytes,
|
||||
"modified_at": modified_at,
|
||||
}
|
||||
|
||||
|
||||
def _iso_from_mtime(mtime: float) -> str:
|
||||
from datetime import datetime, timezone
|
||||
|
||||
return datetime.fromtimestamp(mtime, tz=timezone.utc).replace(microsecond=0).isoformat()
|
||||
|
||||
|
||||
def _status_label(passed: Any, completed: int) -> str:
|
||||
if completed == 0:
|
||||
return "INCOMPLETE"
|
||||
if passed is True:
|
||||
return "PASSED"
|
||||
if passed is False:
|
||||
return "NEEDS WORK"
|
||||
return "UNKNOWN"
|
||||
|
||||
|
||||
def _load_session_file(path: Path) -> dict[str, Any]:
|
||||
with path.open("r", encoding="utf-8") as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
class TrainingFeature(BaseFeatureProtocol):
|
||||
"""Training Lab — inspect praisonai-train agent sessions and apply profiles."""
|
||||
|
||||
feature_name = "training"
|
||||
feature_description = "Inspect praisonai-train agent sessions and apply training profiles"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.feature_name
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return self.feature_description
|
||||
|
||||
def routes(self) -> list[Route]:
|
||||
return [
|
||||
Route("/api/training/status", self._status, methods=["GET"]),
|
||||
Route("/api/training/sessions", self._list_sessions, methods=["GET"]),
|
||||
Route("/api/training/sessions/{session_id}", self._get_session, methods=["GET"]),
|
||||
Route("/api/training/sessions/{session_id}/apply", self._apply, methods=["POST"]),
|
||||
]
|
||||
|
||||
async def health(self) -> dict[str, Any]:
|
||||
available = _train_available()
|
||||
return {
|
||||
"status": "ok" if available else "degraded",
|
||||
"feature": self.name,
|
||||
"train_package_available": available,
|
||||
}
|
||||
|
||||
# ── storage helpers ──────────────────────────────────────────────
|
||||
|
||||
def _read_sessions(self, storage: Path) -> list[dict[str, Any]]:
|
||||
if not storage.exists():
|
||||
return []
|
||||
summaries: list[dict[str, Any]] = []
|
||||
for path in storage.glob("*.json"):
|
||||
session_id = path.stem
|
||||
try:
|
||||
data = _load_session_file(path)
|
||||
except (OSError, json.JSONDecodeError):
|
||||
logger.warning("Skipping unreadable training session: %s", path)
|
||||
continue
|
||||
summaries.append(_summarize_session(session_id, path, data))
|
||||
summaries.sort(key=lambda s: s.get("modified_at") or "", reverse=True)
|
||||
return summaries
|
||||
|
||||
# ── route handlers ───────────────────────────────────────────────
|
||||
|
||||
async def _status(self, request: Request) -> JSONResponse:
|
||||
storage = _storage_dir()
|
||||
available = _train_available()
|
||||
sessions = self._read_sessions(storage)
|
||||
warnings: list[str] = []
|
||||
if not available:
|
||||
warnings.append(
|
||||
"praisonai-train not installed. Install with: pip install praisonai-train"
|
||||
)
|
||||
return JSONResponse(
|
||||
{
|
||||
"status": "ok" if available else "degraded",
|
||||
"feature": self.name,
|
||||
"train_package_available": available,
|
||||
"train_package_version": _train_version(),
|
||||
"storage_dir": str(storage),
|
||||
"storage_backend": "json",
|
||||
"session_count": len(sessions),
|
||||
"sqlite_supported": False,
|
||||
"warnings": warnings,
|
||||
}
|
||||
)
|
||||
|
||||
async def _list_sessions(self, request: Request) -> JSONResponse:
|
||||
storage = _storage_dir()
|
||||
try:
|
||||
limit = int(request.query_params.get("limit", "50"))
|
||||
except ValueError:
|
||||
limit = 50
|
||||
agent_id = request.query_params.get("agent_id")
|
||||
|
||||
sessions = self._read_sessions(storage)
|
||||
if agent_id:
|
||||
sessions = [s for s in sessions if s.get("agent_id") == agent_id]
|
||||
limited = sessions[: max(limit, 0)]
|
||||
return JSONResponse({"sessions": limited, "count": len(limited)})
|
||||
|
||||
async def _get_session(self, request: Request) -> JSONResponse:
|
||||
session_id = request.path_params["session_id"]
|
||||
storage = _storage_dir()
|
||||
path = storage / f"{session_id}.json"
|
||||
if not path.exists():
|
||||
return JSONResponse(
|
||||
{"error": f"Session not found: {session_id}"}, status_code=404
|
||||
)
|
||||
try:
|
||||
data = _load_session_file(path)
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return JSONResponse(
|
||||
{"error": f"Corrupt session JSON: {session_id}"}, status_code=422
|
||||
)
|
||||
|
||||
report = data.get("report") or {}
|
||||
iterations = list(data.get("iterations") or report.get("iterations") or [])
|
||||
best_idx = _best_iteration_index(iterations)
|
||||
for idx, it in enumerate(iterations):
|
||||
it["is_best"] = idx == best_idx
|
||||
|
||||
summary = _summarize_session(session_id, path, data)
|
||||
best_iteration = iterations[best_idx] if best_idx is not None else None
|
||||
|
||||
return JSONResponse(
|
||||
{
|
||||
"session_id": session_id,
|
||||
"report": report,
|
||||
"iterations": iterations,
|
||||
"best_iteration": best_iteration,
|
||||
"best_iteration_num": (
|
||||
best_iteration.get("iteration_num", best_idx + 1)
|
||||
if best_iteration is not None
|
||||
else None
|
||||
),
|
||||
"early_stopped": summary["early_stopped"],
|
||||
"early_stop_reason": (
|
||||
"score_threshold"
|
||||
if summary["early_stopped"]
|
||||
and _iter_scores(iterations)
|
||||
and _iter_scores(iterations)[-1] >= _EARLY_STOP_THRESHOLD
|
||||
else None
|
||||
),
|
||||
"requested_iterations": summary["requested_iterations"],
|
||||
"completed_iterations": summary["completed_iterations"],
|
||||
"avg_score": summary["avg_score"],
|
||||
"improvement": summary["improvement"],
|
||||
"passed": summary["passed"],
|
||||
"mode": summary["mode"],
|
||||
"scenarios": data.get("scenarios") or [],
|
||||
}
|
||||
)
|
||||
|
||||
async def _apply(self, request: Request) -> JSONResponse:
|
||||
session_id = request.path_params["session_id"]
|
||||
storage = _storage_dir()
|
||||
path = storage / f"{session_id}.json"
|
||||
if not path.exists():
|
||||
return JSONResponse(
|
||||
{"error": f"Session not found: {session_id}"}, status_code=404
|
||||
)
|
||||
|
||||
if not _train_available():
|
||||
return JSONResponse(
|
||||
{
|
||||
"error": "praisonai-train is not installed",
|
||||
"hint": "pip install praisonai-train",
|
||||
},
|
||||
status_code=503,
|
||||
)
|
||||
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
body = {}
|
||||
agent_id = body.get("agent_id")
|
||||
if not agent_id:
|
||||
return JSONResponse({"error": "agent_id is required"}, status_code=422)
|
||||
iteration = body.get("iteration")
|
||||
|
||||
from .agents import get_agent_registry
|
||||
|
||||
registry = get_agent_registry()
|
||||
agent_def = registry.get(agent_id)
|
||||
if agent_def is None:
|
||||
return JSONResponse(
|
||||
{"error": f"Agent not found: {agent_id}"}, status_code=404
|
||||
)
|
||||
|
||||
try:
|
||||
result = self._apply_training(agent_def, session_id, iteration)
|
||||
except Exception as exc:
|
||||
logger.exception("apply_training failed for session %s", session_id)
|
||||
return JSONResponse({"error": str(exc)}, status_code=500)
|
||||
|
||||
return JSONResponse(
|
||||
{
|
||||
"success": True,
|
||||
"session_id": session_id,
|
||||
"agent_id": agent_id,
|
||||
"iteration_applied": result.get("iteration_applied"),
|
||||
"quality_score": result.get("quality_score"),
|
||||
"suggestions_count": result.get("suggestions_count", 0),
|
||||
"hook_id": result.get("hook_id", f"training_hook_{session_id}"),
|
||||
"message": f"Training profile applied to agent '{agent_id}'",
|
||||
}
|
||||
)
|
||||
|
||||
def _apply_training(
|
||||
self, agent_def: dict[str, Any], session_id: str, iteration: int | None
|
||||
) -> dict[str, Any]:
|
||||
from praisonai_train.train.agents import apply_training # type: ignore
|
||||
from praisonaiagents import Agent # type: ignore
|
||||
|
||||
agent = Agent(
|
||||
name=agent_def.get("name", agent_def.get("id", "agent")),
|
||||
instructions=agent_def.get("instructions", agent_def.get("description", "")),
|
||||
llm=agent_def.get("model"),
|
||||
)
|
||||
apply_training(agent, session_id=session_id, iteration=iteration)
|
||||
hook_id = getattr(agent, "_training_hook_id", f"training_hook_{session_id}")
|
||||
|
||||
storage = _storage_dir()
|
||||
applied_iteration = iteration
|
||||
quality_score = None
|
||||
suggestions_count = 0
|
||||
try:
|
||||
data = _load_session_file(storage / f"{session_id}.json")
|
||||
iterations = list(data.get("iterations") or [])
|
||||
idx = _best_iteration_index(iterations) if iteration is None else None
|
||||
chosen = None
|
||||
if idx is not None:
|
||||
chosen = iterations[idx]
|
||||
applied_iteration = chosen.get("iteration_num", idx + 1)
|
||||
elif iteration is not None:
|
||||
for it in iterations:
|
||||
if it.get("iteration_num") == iteration:
|
||||
chosen = it
|
||||
break
|
||||
if chosen is not None:
|
||||
quality_score = chosen.get("score")
|
||||
suggestions_count = len(chosen.get("suggestions") or [])
|
||||
except (OSError, json.JSONDecodeError):
|
||||
pass
|
||||
|
||||
return {
|
||||
"iteration_applied": applied_iteration,
|
||||
"quality_score": quality_score,
|
||||
"suggestions_count": suggestions_count,
|
||||
"hook_id": hook_id,
|
||||
}
|
||||
@@ -187,9 +187,11 @@ class A11yConfig(BaseModel):
|
||||
class SEOConfig(BaseModel):
|
||||
"""SEO configuration."""
|
||||
|
||||
title_template: str = Field(default="%s", alias="titleTemplate")
|
||||
title_template: str = Field(default="%s | %s", alias="titleTemplate")
|
||||
default_image: Optional[str] = Field(default=None, alias="defaultImage")
|
||||
twitter: Optional[dict[str, str]] = None
|
||||
site_url: Optional[str] = Field(default=None, alias="siteUrl")
|
||||
robots: Optional[dict[str, bool]] = None
|
||||
|
||||
|
||||
class DependenciesConfig(BaseModel):
|
||||
|
||||
@@ -687,12 +687,21 @@ def _build_html(style: str) -> str:
|
||||
title = f"{_site_title} Dashboard" if style == "dashboard" else _site_title
|
||||
cache_bust = int(_server_start_time)
|
||||
|
||||
# Anti-flicker: dark background + hide React content until plugins render
|
||||
# Anti-flicker: hide React content until plugins render (uses theme CSS vars)
|
||||
anti_flicker = ""
|
||||
theme_bootstrap = (
|
||||
'<script id="aiui-theme-bootstrap">'
|
||||
"(function(){try{var t=localStorage.getItem('aiui-theme-preference');"
|
||||
"if(t==='dark'||(t==='system'&&matchMedia('(prefers-color-scheme: dark)').matches))"
|
||||
"document.documentElement.classList.add('dark');"
|
||||
"else if(t==='light')document.documentElement.classList.remove('dark');"
|
||||
"}catch(e){}})();"
|
||||
"</script>"
|
||||
)
|
||||
if style == "docs":
|
||||
anti_flicker = (
|
||||
'<style id="aiui-anti-flicker">'
|
||||
"html,body{background:#0f172a!important;color:#e2e8f0}"
|
||||
"html,body{background:hsl(var(--background))!important;color:hsl(var(--foreground))}"
|
||||
"#root>*{opacity:0;transition:opacity .15s ease}"
|
||||
"</style>"
|
||||
)
|
||||
@@ -751,16 +760,26 @@ def _build_html(style: str) -> str:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
canonical_script = (
|
||||
'<script id="aiui-canonical-url">'
|
||||
"(function(){var p=location.pathname.replace(/\\/+$/, '')||'/';"
|
||||
"if(p!==location.pathname){history.replaceState(null,'',"
|
||||
"p+location.search+location.hash);}})();"
|
||||
"</script>"
|
||||
)
|
||||
|
||||
return (
|
||||
'<!doctype html><html lang="en" style="background:#0f172a;color:#e2e8f0"><head>'
|
||||
'<!doctype html><html lang="en"><head>'
|
||||
'<meta charset="UTF-8">'
|
||||
'<meta name="viewport" content="width=device-width,initial-scale=1.0">'
|
||||
f"{canonical_script}"
|
||||
f"{theme_bootstrap}"
|
||||
f'<link rel="stylesheet" href="/assets/index.css?v={cache_bust}">'
|
||||
f"{anti_flicker}"
|
||||
f"{theme_vars_tag}"
|
||||
f"{custom_css_tag}"
|
||||
f"<title>{title}</title>"
|
||||
'</head><body style="background:#0f172a;margin:0">'
|
||||
'</head><body style="margin:0">'
|
||||
'<div id="root"></div>'
|
||||
f"{react_script}"
|
||||
f'<script src="/plugins/plugin-loader.js?v={cache_bust}"></script>'
|
||||
@@ -813,10 +832,8 @@ async def _plugins_config(request: Request) -> JSONResponse:
|
||||
"syntax-highlight",
|
||||
"code-copy",
|
||||
"content-loader",
|
||||
"toc",
|
||||
"mermaid",
|
||||
"nav-intercept",
|
||||
"mkdocs-compat",
|
||||
"homepage",
|
||||
]
|
||||
else:
|
||||
@@ -3320,6 +3337,14 @@ def create_app(
|
||||
"description": "Manage conversation sessions",
|
||||
"order": 20,
|
||||
},
|
||||
{
|
||||
"id": "runs",
|
||||
"title": "Runs",
|
||||
"icon": "⏱️",
|
||||
"group": "Control",
|
||||
"description": "Run flight recorder — timeline debugger",
|
||||
"order": 22,
|
||||
},
|
||||
{
|
||||
"id": "usage",
|
||||
"title": "Usage",
|
||||
@@ -3496,6 +3521,14 @@ def create_app(
|
||||
"description": "Agent evaluation & accuracy",
|
||||
"order": 40,
|
||||
},
|
||||
{
|
||||
"id": "training",
|
||||
"title": "Training",
|
||||
"icon": "🧪",
|
||||
"group": "Control",
|
||||
"description": "Inspect praisonai-train sessions & apply profiles",
|
||||
"order": 42,
|
||||
},
|
||||
{
|
||||
"id": "code-studio",
|
||||
"title": "Code Studio",
|
||||
@@ -3548,6 +3581,7 @@ def create_app(
|
||||
]
|
||||
_page_api_overrides = {
|
||||
"sessions": "/sessions",
|
||||
"runs": "/api/sessions",
|
||||
"cron": "/api/schedules",
|
||||
"channels": "/api/channels",
|
||||
"mcp": "/api/mcp/servers",
|
||||
@@ -3587,6 +3621,8 @@ def create_app(
|
||||
# Always mount built-in frontend plugins and assets BEFORE catch-all /
|
||||
_frontend_dir = Path(__file__).parent / "templates" / "frontend"
|
||||
_plugins_dir = _frontend_dir / "plugins"
|
||||
if static_dir and (Path(static_dir) / "plugins").is_dir():
|
||||
_plugins_dir = Path(static_dir) / "plugins"
|
||||
_assets_dir = _frontend_dir / "assets"
|
||||
|
||||
# Dynamic plugins.json route — MUST come before static /plugins mount
|
||||
@@ -3613,6 +3649,13 @@ def create_app(
|
||||
routes.append(Route("/", _serve_index, methods=["GET"]))
|
||||
|
||||
_static_app = StaticFiles(directory=str(static_dir), html=True)
|
||||
_static_root = Path(static_dir)
|
||||
|
||||
async def _serve_static_file(scope, receive, send, file_path: Path, media_type: str | None = None):
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
response = FileResponse(str(file_path), media_type=media_type)
|
||||
await response(scope, receive, send)
|
||||
|
||||
async def _http_only_static(scope, receive, send):
|
||||
if scope["type"] != "http":
|
||||
@@ -3621,7 +3664,25 @@ def create_app(
|
||||
await send({"type": "websocket.close", "code": 1008})
|
||||
return
|
||||
|
||||
# Try StaticFiles first; on 404, fall back to SPA index
|
||||
request_path = scope.get("path", "")
|
||||
rel = request_path.lstrip("/")
|
||||
if rel:
|
||||
exact = _static_root / rel
|
||||
if exact.is_file():
|
||||
await _serve_static_file(scope, receive, send, exact)
|
||||
return
|
||||
if not request_path.endswith("/"):
|
||||
html_path = _static_root / f"{rel}.html"
|
||||
if html_path.is_file():
|
||||
await _serve_static_file(scope, receive, send, html_path, media_type="text/html")
|
||||
return
|
||||
if exact.is_dir():
|
||||
index = exact / "index.html"
|
||||
if index.is_file():
|
||||
await _serve_static_file(scope, receive, send, index, media_type="text/html")
|
||||
return
|
||||
|
||||
# Try StaticFiles; on 404, fall back to SPA index
|
||||
# (paths like /chat, /memory don't have static files but need
|
||||
# the SPA shell so JS routing can take over).
|
||||
response_started = False
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import{g as p,r as u,d as a}from"./chunk-SVP7TREG.js";import{p as f}from"./chunk-JWPE2WC7.js";import{_ as n,l as o}from"./mermaid.core.js";import{M as c,b as d}from"./cynefin-OW5HDTMX.js";import"./index.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
||||
@@ -0,0 +1 @@
|
||||
import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|
||||
@@ -0,0 +1,36 @@
|
||||
import{p as qe}from"./chunk-JWPE2WC7.js";import{_ as ct,D as Qe,Y as Je,l as Fe,b as Ke,a as je,n as _e,o as tr,g as er,s as rr,p as ir,A as ar,y as nr,B as or,c as Ee,b5 as Te,ak as pe,i as sr,j as hr,q as lr,al as fr,b6 as cr}from"./mermaid.core.js";import{p as gr}from"./cynefin-OW5HDTMX.js";import{c as be}from"./cytoscape.esm.js";import{g as ur}from"./index.js";var he={exports:{}},le={exports:{}},fe={exports:{}},dr=fe.exports,De;function vr(){return De||(De=1,(function(x,M){(function(R,T){x.exports=T()})(dr,function(){return(function(w){var R={};function T(v){if(R[v])return R[v].exports;var l=R[v]={i:v,l:!1,exports:{}};return w[v].call(l.exports,l,l.exports,T),l.l=!0,l.exports}return T.m=w,T.c=R,T.i=function(v){return v},T.d=function(v,l,i){T.o(v,l)||Object.defineProperty(v,l,{configurable:!1,enumerable:!0,get:i})},T.n=function(v){var l=v&&v.__esModule?function(){return v.default}:function(){return v};return T.d(l,"a",l),l},T.o=function(v,l){return Object.prototype.hasOwnProperty.call(v,l)},T.p="",T(T.s=28)})([(function(w,R,T){function v(){}v.QUALITY=1,v.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,v.DEFAULT_INCREMENTAL=!1,v.DEFAULT_ANIMATION_ON_LAYOUT=!0,v.DEFAULT_ANIMATION_DURING_LAYOUT=!1,v.DEFAULT_ANIMATION_PERIOD=50,v.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,v.DEFAULT_GRAPH_MARGIN=15,v.NODE_DIMENSIONS_INCLUDE_LABELS=!1,v.SIMPLE_NODE_SIZE=40,v.SIMPLE_NODE_HALF_SIZE=v.SIMPLE_NODE_SIZE/2,v.EMPTY_COMPOUND_NODE_SIZE=40,v.MIN_EDGE_LENGTH=1,v.WORLD_BOUNDARY=1e6,v.INITIAL_WORLD_BOUNDARY=v.WORLD_BOUNDARY/1e3,v.WORLD_CENTER_X=1200,v.WORLD_CENTER_Y=900,w.exports=v}),(function(w,R,T){var v=T(2),l=T(8),i=T(9);function r(f,e,d){v.call(this,d),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=d,this.bendpoints=[],this.source=f,this.target=e}r.prototype=Object.create(v.prototype);for(var a in v)r[a]=v[a];r.prototype.getSource=function(){return this.source},r.prototype.getTarget=function(){return this.target},r.prototype.isInterGraph=function(){return this.isInterGraph},r.prototype.getLength=function(){return this.length},r.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},r.prototype.getBendpoints=function(){return this.bendpoints},r.prototype.getLca=function(){return this.lca},r.prototype.getSourceInLca=function(){return this.sourceInLca},r.prototype.getTargetInLca=function(){return this.targetInLca},r.prototype.getOtherEnd=function(f){if(this.source===f)return this.target;if(this.target===f)return this.source;throw"Node is not incident with this edge"},r.prototype.getOtherEndInGraph=function(f,e){for(var d=this.getOtherEnd(f),t=e.getGraphManager().getRoot();;){if(d.getOwner()==e)return d;if(d.getOwner()==t)break;d=d.getOwner().getParent()}return null},r.prototype.updateLength=function(){var f=new Array(4);this.isOverlapingSourceAndTarget=l.getIntersection(this.target.getRect(),this.source.getRect(),f),this.isOverlapingSourceAndTarget||(this.lengthX=f[0]-f[2],this.lengthY=f[1]-f[3],Math.abs(this.lengthX)<1&&(this.lengthX=i.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=i.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},r.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=i.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=i.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},w.exports=r}),(function(w,R,T){function v(l){this.vGraphObject=l}w.exports=v}),(function(w,R,T){var v=T(2),l=T(10),i=T(13),r=T(0),a=T(16),f=T(5);function e(t,s,o,c){o==null&&c==null&&(c=s),v.call(this,c),t.graphManager!=null&&(t=t.graphManager),this.estimatedSize=l.MIN_VALUE,this.inclusionTreeDepth=l.MAX_VALUE,this.vGraphObject=c,this.edges=[],this.graphManager=t,o!=null&&s!=null?this.rect=new i(s.x,s.y,o.width,o.height):this.rect=new i}e.prototype=Object.create(v.prototype);for(var d in v)e[d]=v[d];e.prototype.getEdges=function(){return this.edges},e.prototype.getChild=function(){return this.child},e.prototype.getOwner=function(){return this.owner},e.prototype.getWidth=function(){return this.rect.width},e.prototype.setWidth=function(t){this.rect.width=t},e.prototype.getHeight=function(){return this.rect.height},e.prototype.setHeight=function(t){this.rect.height=t},e.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},e.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},e.prototype.getCenter=function(){return new f(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},e.prototype.getLocation=function(){return new f(this.rect.x,this.rect.y)},e.prototype.getRect=function(){return this.rect},e.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},e.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.reLine truncated
|
||||
.edge {
|
||||
stroke-width: ${x.archEdgeWidth};
|
||||
stroke: ${x.archEdgeColor};
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
fill: ${x.archEdgeArrowColor};
|
||||
}
|
||||
|
||||
.node-bkg {
|
||||
fill: none;
|
||||
stroke: ${x.archGroupBorderColor};
|
||||
stroke-width: ${x.archGroupBorderWidth};
|
||||
stroke-dasharray: 8;
|
||||
}
|
||||
.node-icon-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.node-icon-text > div {
|
||||
color: #fff;
|
||||
margin: 1px;
|
||||
height: fit-content;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
`,"getStyles"),Fr=Sr;function me(x,M){if(x===0)return M();const w=Math.random;let R=x>>>0;Math.random=function(){R=R+1831565813>>>0;let T=R;return T=Math.imul(T^T>>>15,T|1),T^=T+Math.imul(T^T>>>7,T|61),((T^T>>>14)>>>0)/4294967296};try{return M()}finally{Math.random=w}}ct(me,"withSeededRandom");var re=ct(x=>`<g><rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>${x}</g>`,"wrapIcon"),ne={prefix:"mermaid-architecture",height:80,width:80,icons:{database:{body:re('<path id="b" data-name="4" d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path id="c" data-name="3" d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path id="d" data-name="2" d="m20,34.05c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse id="e" data-name="1" cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>')},server:{body:re('<rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="47.5" x2="62.5" y2="47.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><g><path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g>')},disk:{body:re('<rect x="20" y="15" width="40" height="50" rx="1" ry="1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="24" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="56" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="24" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="56" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="40" cy="33.75" rx="14" ry="14.58" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="40" cy="33.75" rx="4" ry="4.17" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path d="m37.51,42.52l-4.83,13.22c-.26.71-1.1,1.02-1.76.64l-4.18-2.42c-.66-.38-.81-1.26-.33-1.84l9.01-10.8c.88-1.05,2.56-.08,2.09,1.2Z" style="fill: #fff; stroke-width: 0px;"/>')},internet:{body:re('<circle cx="40" cy="40" r="22.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="40" y1="17.5" x2="40" y2="62.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="40" x2="62.5" y2="40" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path d="m39.99,17.51c-15.28,11.1-15.28,33.88,0,44.98" style="fill: none; sLine truncated
|
||||
translate(${L}, ${F-E.height/2})
|
||||
translate(${p*y.width/2}, ${m*y.height/2})
|
||||
rotate(${-1*p*m*45}, 0, ${E.height/2})
|
||||
`)}}}}}))},"drawEdges"),Pr=ct(async function(x,M,w,R){const v=w.getConfigField("padding")*.75,l=w.getConfigField("fontSize"),r=w.getConfigField("iconSize")/2;await Promise.all(M.nodes().map(async a=>{const f=ie(a);if(f.type==="group"){const{h:e,w:d,x1:t,y1:s}=a.boundingBox(),o=x.append("rect");o.attr("id",`${R}-group-${f.id}`).attr("x",t+r).attr("y",s+r).attr("width",d).attr("height",e).attr("class","node-bkg");const c=x.append("g");let h=t,N=s;if(f.icon){const g=c.append("g");g.html(`<g>${await pe(f.icon,{height:v,width:v,fallbackPrefix:ne.prefix})}</g>`),g.attr("transform","translate("+(h+r+1)+", "+(N+r+1)+")"),h+=v,N+=l/2-1-2}if(f.label){const g=c.append("g");await Te(g,f.label,{useHtmlLabels:!1,width:d,classes:"architecture-service-label"},Ee()),g.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","start").attr("text-anchor","start"),g.attr("transform","translate("+(h+r+4)+", "+(N+r+2)+")")}w.setElementForId(f.id,o)}}))},"drawGroups"),Gr=ct(async function(x,M,w,R){const T=Ee();for(const v of w){const l=M.append("g"),i=x.getConfigField("iconSize");if(v.title){const e=l.append("g");await Te(e,v.title,{useHtmlLabels:!1,width:i*1.5,classes:"architecture-service-label"},T),e.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle"),e.attr("transform","translate("+i/2+", "+i+")")}const r=l.append("g");if(v.icon)r.html(`<g>${await pe(v.icon,{height:i,width:i,fallbackPrefix:ne.prefix})}</g>`);else if(v.iconText){r.html(`<g>${await pe("blank",{height:i,width:i,fallbackPrefix:ne.prefix})}</g>`);const t=r.append("g").append("foreignObject").attr("width",i).attr("height",i).append("div").attr("class","node-icon-text").attr("style",`height: ${i}px;`).append("div").html(sr(v.iconText,T)),s=parseInt(window.getComputedStyle(t.node(),null).getPropertyValue("font-size").replace(/\D/g,""))??16;t.attr("style",`-webkit-line-clamp: ${Math.floor((i-2)/s)};`)}else r.append("path").attr("class","node-bkg").attr("id",`${R}-node-${v.id}`).attr("d",`M0,${i} V5 Q0,0 5,0 H${i-5} Q${i},0 ${i},5 V${i} Z`);l.attr("id",`${R}-service-${v.id}`).attr("class","architecture-service");const{width:a,height:f}=l.node().getBBox();v.width=a,v.height=f,x.setElementForId(v.id,l)}return 0},"drawServices"),Ur=ct(function(x,M,w,R){w.forEach(T=>{const v=M.append("g"),l=x.getConfigField("iconSize");v.append("g").append("rect").attr("id",`${R}-node-${T.id}`).attr("fill-opacity","0").attr("width",l).attr("height",l),v.attr("class","architecture-junction");const{width:r,height:a}=v._groups[0][0].getBBox();v.width=r,v.height=a,x.setElementForId(T.id,v)})},"drawJunctions");fr([{name:ne.prefix,icons:ne}]);be.use(Nr);function Xe(x,M,w){x.forEach(R=>{M.add({group:"nodes",data:{type:"service",id:R.id,icon:R.icon,label:R.title,parent:R.in,width:w.getConfigField("iconSize"),height:w.getConfigField("iconSize")},classes:"node-service"})})}ct(Xe,"addServices");function He(x,M,w){x.forEach(R=>{M.add({group:"nodes",data:{type:"junction",id:R.id,parent:R.in,width:w.getConfigField("iconSize"),height:w.getConfigField("iconSize")},classes:"node-junction"})})}ct(He,"addJunctions");function We(x,M){M.nodes().map(w=>{const R=ie(w);if(R.type==="group")return;R.x=w.position().x,R.y=w.position().y,x.getElementById(R.id).attr("transform","translate("+(R.x||0)+","+(R.y||0)+")")})}ct(We,"positionNodes");function Ve(x,M){x.forEach(w=>{M.add({group:"nodes",data:{type:"group",id:w.id,icon:w.icon,label:w.title,parent:w.in},classes:"node-group"})})}ct(Ve,"addGroups");function $e(x,M){x.forEach(w=>{const{lhsId:R,rhsId:T,lhsInto:v,lhsGroup:l,rhsInto:i,lhsDir:r,rhsDir:a,rhsGroup:f,title:e}=w,d=Ne(w.lhsDir,w.rhsDir)?"segments":"straight",t={id:`${R}-${T}`,label:e,source:R,sourceDir:r,sourceArrow:v,sourceGroup:l,sourceEndpoint:r==="L"?"0 50%":r==="R"?"100% 50%":r==="T"?"50% 0":"50% 100%",target:T,targetDir:a,targetArrow:i,targetGroup:f,targetEndpoint:a==="L"?"0 50%":a==="R"?"100% 50%":a==="T"?"50% 0":"50% 100%"};M.add({group:"edges",data:t,classes:d})})}ct($e,"addEdges");function ze(x,M,w,R=[]){const T=ct((d,t)=>{const s=new Map;for(const[o,c]of d.entries()){const h=`${o}`;let N=0;const g=[...c.entries()];if(g.length===1){s.set(h,g[0][1]);continue}for(let u=0;u<g.length-1;u++)for(let L=u+1;L<g.length;L++){const[F,C]=g[u],[G,V]=g[L];if(w.get(Pe(F,G))===t)s.set(h,[...s.get(h)??[],...C,...V]);else if(F==="default"||G==="default")s.set(h,[...s.get(h)??[],...C,...V]);else{const $=`${h}-${N++}`;s.set($,C);const A=`${h}-${N++}`;s.set(A,V)}}}return s},"flattenAlignments"),v=M.map(d=>{const t=new Map,s=new Map;return d.forEach(([o,c],h)=>{const N=x.getNode(h)?.in??"default",g=t.get(c)??new Map;t.has(c)||t.set(c,g);const u=s.get(o)??new Map;s.has(o)||s.set(o,u);for(const L of[g,u]){const F=L.get(N)??[];L.has(N)||L.set(N,F),F.push(h)}}),{horiz:[...T(t,"horizontal").values()].filter(o=>o.length>1),vert:[...T(s,"vertical").values()].filter(o=>o.length>1)}}),[l,i]=v.reduce(([d,t],{horiz:s,vert:o})=>[[...d,Line truncated
|
||||
@@ -0,0 +1,129 @@
|
||||
import{g as Pe}from"./chunk-5VM5RSS4.js";import{aD as Fe,aE as xe,aF as Me,aG as Ke,aH as Ye,aI as We,aJ as Ve,aK as Ue,aL as He,aM as Xe,aN as je,aO as Ge,aP as qe,aQ as Je,aR as Ze,aS as Qe,aT as $e,aU as et,aV as tt,aW as st,aX as rt,aY as it,aZ as at,a_ as nt,a$ as ot,_ as u,y as Y,j as X,b0 as ct,d as lt,l as m,p as ut,r as gt,c as Le,Q as ht,az as dt,ay as pt,aA as ft,k as bt,b1 as St,ag as ye,ah as xt}from"./mermaid.core.js";import{c as Lt}from"./channel.js";import"./index.js";function yt(e){return Array.isArray(e)}function mt(e){if(Fe(e))return e;const a=xe(e);if(!wt(e))return{};if(yt(e)){const r=Array.from(e);return e.length>0&&typeof e[0]=="string"&&Object.hasOwn(e,"index")&&(r.index=e.index,r.input=e.input),r}if(Me(e)){const r=e,l=r.constructor;return new l(r.buffer,r.byteOffset,r.length)}if(a==="[object ArrayBuffer]")return new ArrayBuffer(e.byteLength);if(a==="[object DataView]"){const r=e,l=r.buffer,d=r.byteOffset,s=r.byteLength,h=new ArrayBuffer(s),x=new Uint8Array(l,d,s);return new Uint8Array(h).set(x),new DataView(h)}if(a==="[object Boolean]"||a==="[object Number]"||a==="[object String]"){const r=e.constructor,l=new r(e.valueOf());return a==="[object String]"?_t(l,e):se(l,e),l}if(a==="[object Date]")return new Date(Number(e));if(a==="[object RegExp]"){const r=e,l=new RegExp(r.source,r.flags);return l.lastIndex=r.lastIndex,l}if(a==="[object Symbol]")return Object(Symbol.prototype.valueOf.call(e));if(a==="[object Map]"){const r=e,l=new Map;return r.forEach((d,s)=>{l.set(s,d)}),l}if(a==="[object Set]"){const r=e,l=new Set;return r.forEach(d=>{l.add(d)}),l}if(a==="[object Arguments]"){const r=e,l={};return se(l,r),l.length=r.length,l[Symbol.iterator]=r[Symbol.iterator],l}const o={};return kt(o,e),se(o,e),Et(o,e),o}function wt(e){switch(xe(e)){case ot:case nt:case at:case it:case rt:case st:case tt:case et:case $e:case Qe:case Ze:case Je:case qe:case Ge:case je:case Xe:case He:case Ue:case Ve:case We:case Ye:case Ke:return!0;default:return!1}}function se(e,a){for(const o in a)Object.hasOwn(a,o)&&(e[o]=a[o])}function Et(e,a){const o=Object.getOwnPropertySymbols(a);for(let r=0;r<o.length;r++){const l=o[r];Object.prototype.propertyIsEnumerable.call(a,l)&&(e[l]=a[l])}}function _t(e,a){const o=a.valueOf().length;for(const r in a)Object.hasOwn(a,r)&&(Number.isNaN(Number(r))||Number(r)>=o)&&(e[r]=a[r])}function kt(e,a){const o=Object.getPrototypeOf(a);o!==null&&typeof a.constructor=="function"&&Object.setPrototypeOf(e,o)}var ie=(function(){var e=u(function(v,g,i,c){for(i=i||{},c=v.length;c--;i[v[c]]=g);return i},"o"),a=[1,15],o=[1,7],r=[1,13],l=[1,14],d=[1,19],s=[1,16],h=[1,17],x=[1,18],y=[8,30],S=[8,10,21,28,29,30,31,39,43,46],f=[1,23],L=[1,24],w=[8,10,15,16,21,28,29,30,31,39,43,46],b=[8,10,15,16,21,27,28,29,30,31,39,43,46],k=[1,49],_={trace:u(function(){},"trace"),yy:{},symbols_:{error:2,spaceLines:3,SPACELINE:4,NL:5,separator:6,SPACE:7,EOF:8,start:9,BLOCK_DIAGRAM_KEY:10,document:11,stop:12,statement:13,link:14,LINK:15,START_LINK:16,LINK_LABEL:17,STR:18,nodeStatement:19,columnsStatement:20,SPACE_BLOCK:21,blockStatement:22,classDefStatement:23,cssClassStatement:24,styleStatement:25,node:26,SIZE:27,COLUMNS:28,"id-block":29,end:30,NODE_ID:31,nodeShapeNLabel:32,dirList:33,DIR:34,NODE_DSTART:35,NODE_DEND:36,BLOCK_ARROW_START:37,BLOCK_ARROW_END:38,classDef:39,CLASSDEF_ID:40,CLASSDEF_STYLEOPTS:41,DEFAULT:42,class:43,CLASSENTITY_IDS:44,STYLECLASS:45,style:46,STYLE_ENTITY_IDS:47,STYLE_DEFINITION_DATA:48,$accept:0,$end:1},terminals_:{2:"error",4:"SPACELINE",5:"NL",7:"SPACE",8:"EOF",10:"BLOCK_DIAGRAM_KEY",15:"LINK",16:"START_LINK",17:"LINK_LABEL",18:"STR",21:"SPACE_BLOCK",27:"SIZE",28:"COLUMNS",29:"id-block",30:"end",31:"NODE_ID",34:"DIR",35:"NODE_DSTART",36:"NODE_DEND",37:"BLOCK_ARROW_START",38:"BLOCK_ARROW_END",39:"classDef",40:"CLASSDEF_ID",41:"CLASSDEF_STYLEOPTS",42:"DEFAULT",43:"class",44:"CLASSENTITY_IDS",45:"STYLECLASS",46:"style",47:"STYLE_ENTITY_IDS",48:"STYLE_DEFINITION_DATA"},productions_:[0,[3,1],[3,2],[3,2],[6,1],[6,1],[6,1],[9,3],[12,1],[12,1],[12,2],[12,2],[11,1],[11,2],[14,1],[14,4],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[19,3],[19,2],[19,1],[20,1],[22,4],[22,3],[26,1],[26,2],[33,1],[33,2],[32,3],[32,4],[23,3],[23,3],[24,3],[25,3]],performAction:u(function(g,i,c,p,E,t,M){var n=t.length-1;switch(E){case 4:p.getLogger().debug("Rule: separator (NL) ");break;case 5:p.getLogger().debug("Rule: separator (Space) ");break;case 6:p.getLogger().debug("Rule: separator (EOF) ");break;case 7:p.getLogger().debug("Rule: hierarchy: ",t[n-1]),p.setHierarchy(t[n-1]);break;case 8:p.getLogger().debug("Stop NL ");break;case 9:p.getLogger().debug("Stop EOF ");break;case 10:p.getLogger().debug("Stop NL2 ");break;case 11:p.getLogger().debug("Stop EOF2 ");break;case 12:p.getLogger().debug("Rule: statement: ",t[n]),typeof t[n].length=="number"?this.$=t[n]:this.$=[t[n]];break;case 13:p.getLogger().debug("Rule: statement #2: ",t[n-1]),this.$=[t[n-1]].concat(t[n]);break;case 14:p.getLogger().debug("Rule: link: ",Line truncated
|
||||
`+D.showPosition()+`
|
||||
Expecting `+H.join(", ")+", got '"+(this.terminals_[N]||N)+"'":te="Parse error on line "+(P+1)+": Unexpected "+(N==V?"end of input":"'"+(this.terminals_[N]||N)+"'"),this.parseError(te,{text:D.match,token:this.terminals_[N]||N,line:D.yylineno,loc:$,expected:H})}if(C[0]instanceof Array&&C.length>1)throw new Error("Parse Error: multiple actions possible at state: "+R+", token: "+N);switch(C[0]){case 1:c.push(N),E.push(D.yytext),t.push(D.yylloc),c.push(C[1]),N=null,W=D.yyleng,n=D.yytext,P=D.yylineno,$=D.yylloc;break;case 2:if(B=this.productions_[C[1]][1],F.$=E[E.length-B],F._$={first_line:t[t.length-(B||1)].first_line,last_line:t[t.length-1].last_line,first_column:t[t.length-(B||1)].first_column,last_column:t[t.length-1].last_column},Be&&(F._$.range=[t[t.length-(B||1)].range[0],t[t.length-1].range[1]]),ee=this.performAction.apply(F,[n,W,P,A.yy,C[1],E,t].concat(Z)),typeof ee<"u")return ee;B&&(c=c.slice(0,-1*B*2),E=E.slice(0,-1*B),t=t.slice(0,-1*B)),c.push(this.productions_[C[1]][0]),E.push(F.$),t.push(F._$),pe=M[c[c.length-2]][c[c.length-1]],c.push(pe);break;case 3:return!0}}return!0},"parse")},T=(function(){var v={EOF:1,parseError:u(function(i,c){if(this.yy.parser)this.yy.parser.parseError(i,c);else throw new Error(i)},"parseError"),setInput:u(function(g,i){return this.yy=i||this.yy||{},this._input=g,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:u(function(){var g=this._input[0];this.yytext+=g,this.yyleng++,this.offset++,this.match+=g,this.matched+=g;var i=g.match(/(?:\r\n?|\n).*/g);return i?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),g},"input"),unput:u(function(g){var i=g.length,c=g.split(/(?:\r\n?|\n)/g);this._input=g+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-i),this.offset-=i;var p=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var E=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===p.length?this.yylloc.first_column:0)+p[p.length-c.length].length-c[0].length:this.yylloc.first_column-i},this.options.ranges&&(this.yylloc.range=[E[0],E[0]+this.yyleng-i]),this.yyleng=this.yytext.length,this},"unput"),more:u(function(){return this._more=!0,this},"more"),reject:u(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:u(function(g){this.unput(this.match.slice(g))},"less"),pastInput:u(function(){var g=this.matched.substr(0,this.matched.length-this.match.length);return(g.length>20?"...":"")+g.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:u(function(){var g=this.match;return g.length<20&&(g+=this._input.substr(0,20-g.length)),(g.substr(0,20)+(g.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:u(function(){var g=this.pastInput(),i=new Array(g.length+1).join("-");return g+this.upcomingInput()+`
|
||||
`+i+"^"},"showPosition"),test_match:u(function(g,i){var c,p,E;if(this.options.backtrack_lexer&&(E={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(E.yylloc.range=this.yylloc.range.slice(0))),p=g[0].match(/(?:\r\n?|\n).*/g),p&&(this.yylineno+=p.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:p?p[p.length-1].length-p[p.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+g[0].length},this.yytext+=g[0],this.match+=g[0],this.matches=g,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(g[0].length),this.matched+=g[0],c=this.performAction.call(this,this.yy,this,i,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),c)return c;if(this._backtrack){for(var t in E)this[t]=E[t];return!1}return!1},"test_match"),next:u(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var g,i,c,p;this._more||(this.yytext="",this.match="");for(var E=this._currentRules(),t=0;t<E.length;t++)if(c=this._input.match(this.rules[E[t]]),c&&(!i||c[0].length>i[0].length)){if(i=c,p=t,this.options.backtrack_lexer){if(g=this.test_match(c,E[t]),g!==!1)return g;if(this._backtrack){i=!1;continue}else return!1}else if(!this.options.flex)break}return i?(g=this.test_match(i,E[p]),g!==!1?g:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:u(function(){var i=this.next();return i||this.lex()},"lex"),begin:u(function(i){this.conditionStack.push(i)},"begin"),popState:u(function(){var i=this.conditionStack.length-1;return i>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:u(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:u(function(i){return i=this.conditionStack.length-1-Math.abs(i||0),i>=0?this.conditionStack[i]:"INITIAL"},"topState"),pushState:u(function(i){this.begin(i)},"pushState"),stateStackSize:u(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:u(function(i,c,p,E){switch(p){case 0:return i.getLogger().debug("Found block-beta"),10;case 1:return i.getLogger().debug("Found id-block"),29;case 2:return i.getLogger().debug("Found block"),10;case 3:i.getLogger().debug(".",c.yytext);break;case 4:i.getLogger().debug("_",c.yytext);break;case 5:return 5;case 6:return c.yytext=-1,28;case 7:return c.yytext=c.yytext.replace(/columns\s+/,""),i.getLogger().debug("COLUMNS (LEX)",c.yytext),28;case 8:this.pushState("md_string");break;case 9:return"MD_STR";case 10:this.popState();break;case 11:this.pushState("string");break;case 12:i.getLogger().debug("LEX: POPPING STR:",c.yytext),this.popState();break;case 13:return i.getLogger().debug("LEX: STR end:",c.yytext),"STR";case 14:return c.yytext=c.yytext.replace(/space\:/,""),i.getLogger().debug("SPACE NUM (LEX)",c.yytext),21;case 15:return c.yytext="1",i.getLogger().debug("COLUMNS (LEX)",c.yytext),21;case 16:return 42;case 17:return"LINKSTYLE";case 18:return"INTERPOLATE";case 19:return this.pushState("CLASSDEF"),39;case 20:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";case 21:return this.popState(),this.pushState("CLASSDEFID"),40;case 22:return this.popState(),41;case 23:return this.pushState("CLASS"),43;case 24:return this.popState(),this.pushState("CLASS_STYLE"),44;case 25:return this.popState(),45;case 26:return this.pushState("STYLE_STMNT"),46;case 27:return this.popState(),this.pushState("STYLE_DEFINITION"),47;case 28:return this.popState(),48;case 29:return this.pushState("acc_title"),"acc_title";case 30:return this.popState(),"acc_title_value";case 31:return this.pushState("acc_descr"),"acc_descr";case 32:return this.popState(),"acc_descr_value";case 33:this.pushState("acc_descr_multiline");break;case 34:this.popState();break;case 35:return"acc_descr_multiline_value";case 36:return 30;case 37:return this.popState(),i.getLogger().debug("Lex: (("),"NODE_DEND";case 38:return this.popState(),i.getLogger().debug("Lex: (("),"NODE_DEND";case 39:return this.popState(),i.getLogger().debug("Lex: ))"),"NODE_DEND";case 40:return this.popState(),i.getLogger().debug("Lex: (("),"NODE_DEND";case 41:return this.popState(),i.getLogger().debug("Lex: (("),"NODE_DEND";case 42:return this.popState(),i.getLogger().debug("Lex: (-"),"NODE_DEND";case 43:return this.popState(),i.getLogger().debug("Lex: -)"),"NODE_DEND";case 44:return this.popState(),i.getLogger().debug("Lex: (("),"NODE_DEND";case 45:return this.popState(),i.getLogger().debug("Lex: ]]"),"NODE_DEND";case 46:return this.popState(),i.getLogger().debug("Lex: ("),"NODE_DEND";case 47:return this.popState(),i.getLogger().debug("Lex: ])"),"NODE_DEND";case 48:return this.popState(),i.getLogger().debug("Lex: /]"),"NODE_DEND";case 49:return this.popState(),i.getLogger().debug("Lex: /]"),"NODE_DEND";case 50:return this.popState(),i.getLogger().debug("Lex: )]"),"NODE_DEND";case 51:return this.popState(),i.getLogger().debug("Lex: )"),"NODE_DEND";case 52:return this.popState(),i.getLogger().debug("Lex: ]>"),"NODE_DEND";case 53:return this.popState(),i.getLogger().debug("Lex: ]"),"NODE_DEND";case 54:return i.getLogger().debug("Lexa: -)"),this.pushState("NODE"),35;case 55:return i.getLogger().debug("Lexa: (-"),this.pushState("NODE"),35;case 56:return i.getLogger().debug("Lexa: ))"),this.pushState("NODE"),35;case 57:return i.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 58:return i.getLogger().debug("Lex: ((("),this.pushState("NODE"),35;case 59:return i.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 60:return i.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 61:return i.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 62:return i.getLogger().debug("Lexc: >"),this.pushState("NODE"),35;case 63:return i.getLogger().debug("Lexa: (["),this.pushState("NODE"),35;case 64:return i.getLogger().debug("Lexa: )"),this.pushState("NODE"),35;case 65:return this.pushState("NODE"),35;case 66:return this.pushState("NODE"),35;case 67:return this.pushState("NODE"),35;case 68:return this.pushState("NODE"),35;case 69:return this.pushState("NODE"),35;case 70:return this.pushState("NODE"),35;case 71:return this.p
Line truncated
|
||||
font-family: ${e.fontFamily};
|
||||
color: ${e.nodeTextColor||e.textColor};
|
||||
}
|
||||
.cluster-label text {
|
||||
fill: ${e.titleColor};
|
||||
}
|
||||
.cluster-label span {
|
||||
color: ${e.titleColor};
|
||||
}
|
||||
|
||||
|
||||
|
||||
.label text,span {
|
||||
fill: ${e.nodeTextColor||e.textColor};
|
||||
color: ${e.nodeTextColor||e.textColor};
|
||||
}
|
||||
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon,
|
||||
.node path {
|
||||
fill: ${e.mainBkg};
|
||||
stroke: ${e.nodeBorder};
|
||||
stroke-width: 1px;
|
||||
}
|
||||
.flowchart-label text {
|
||||
text-anchor: middle;
|
||||
}
|
||||
// .flowchart-label .text-outer-tspan {
|
||||
// text-anchor: middle;
|
||||
// }
|
||||
// .flowchart-label .text-inner-tspan {
|
||||
// text-anchor: start;
|
||||
// }
|
||||
|
||||
.node .label {
|
||||
text-align: center;
|
||||
}
|
||||
.node.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.arrowheadPath {
|
||||
fill: ${e.arrowheadColor};
|
||||
}
|
||||
|
||||
.edgePaths .path {
|
||||
stroke: ${e.lineColor};
|
||||
stroke-width: 2.0px;
|
||||
}
|
||||
|
||||
.flowchart-link {
|
||||
stroke: ${e.lineColor};
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.edgeLabel {
|
||||
background-color: ${e.edgeLabelBackground};
|
||||
/*
|
||||
* This is for backward compatibility with existing code that didn't
|
||||
* add a \`<p>\` around edge labels.
|
||||
*
|
||||
* TODO: We should probably remove this in a future release.
|
||||
*/
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
rect {
|
||||
opacity: 0.5;
|
||||
background-color: ${e.edgeLabelBackground};
|
||||
fill: ${e.edgeLabelBackground};
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* For html labels only */
|
||||
.labelBkg {
|
||||
background-color: ${e.edgeLabelBackground};
|
||||
}
|
||||
|
||||
.node .cluster {
|
||||
// fill: ${re(e.mainBkg,.5)};
|
||||
fill: ${re(e.clusterBkg,.5)};
|
||||
stroke: ${re(e.clusterBorder,.2)};
|
||||
box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: ${e.titleColor};
|
||||
}
|
||||
|
||||
.cluster span {
|
||||
color: ${e.titleColor};
|
||||
}
|
||||
/* .cluster div {
|
||||
color: ${e.titleColor};
|
||||
} */
|
||||
|
||||
div.mermaidTooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: ${e.fontFamily};
|
||||
font-size: 12px;
|
||||
background: ${e.tertiaryColor};
|
||||
border: 1px solid ${e.border2};
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.flowchartTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${e.textColor};
|
||||
}
|
||||
${Pe()}
|
||||
`,"getStyles"),Gt=jt;function ne(e,a){if(e===0||!Number.isInteger(e))throw new Error("Columns must be an integer !== 0.");if(a<0||!Number.isInteger(a))throw new Error("Position must be a non-negative integer."+a);if(e<0)return{px:a,py:0};if(e===1)return{px:0,py:a};const o=a%e,r=Math.floor(a/e);return{px:o,py:r}}u(ne,"calculateBlockPosition");var qt=u(e=>{let a=0,o=0;for(const r of e.children){const{width:l,height:d,x:s,y:h}=r.size??{width:0,height:0,x:0,y:0};if(m.debug("getMaxChildSize abc95 child:",r.id,"width:",l,"height:",d,"x:",s,"y:",h,r.type),r.type==="space")continue;const x=l/(r.widthInColumns??1);x>a&&(a=x),d>o&&(o=d)}return{width:a,height:o}},"getMaxChildSize");function G(e,a,o=0,r=0,l=8){m.debug("setBlockSizes abc95 (start)",e.id,e?.size?.x,"block width =",e?.size,"siblingWidth",o),e?.size?.width||(e.size={width:o,height:r,x:0,y:0});let d=0,s=0;if(e.children?.length>0){for(const b of e.children)G(b,a,0,0,l);const h=qt(e);d=h.width,s=h.height,m.debug("setBlockSizes abc95 maxWidth of",e.id,":s children is ",d,s);for(const b of e.children)b.size&&(m.debug(`abc95 Setting size of children of ${e.id} id=${b.id} ${d} ${s} ${JSON.stringify(b.size)}`),b.size.width=d*(b.widthInColumns??1)+l*((b.widthInColumns??1)-1),b.size.height=s,b.size.x=0,b.size.y=0,m.debug(`abc95 updating size of ${e.id} children child:${b.id} maxWidth:${d} maxHeight:${s}`));for(const b of e.children)G(b,a,d,s,l);const x=e.columns??-1;let y=0;for(const b of e.children)y+=b.widthInColumns??1;let S=e.children.length;x>0&&x<y&&(S=x);const f=Math.ceil(y/S);let L=S*(d+l)+l,w=f*(s+l)+l;if(L<o){m.debug(`Detected to small sibling: abc95 ${e.id} siblingWidth ${o} siblingHeight ${r} width ${L}`),L=o,w=r;const b=(o-S*l-l)/S,k=(r-f*l-l)/f;m.debug("Size indata abc88",e.id,"childWidth",b,"maxWidth",d),m.debug("Size indata abc88",e.id,"childHeight",k,"maxHeight",s),m.debug("Size indata abc88 xSize",S,"padding",l);for(const _ of e.children)_.size&&(_.size.width=b,_.size.height=k,_.size.x=0,_.size.y=0)}if(m.debug(`abc95 (finale calc) ${e.id} xSize ${S} ySize ${f} columns ${x}${e.children.length} width=${Math.max(L,e.size?.width||0)}`),L<(e?.size?.width||0)){L=e?.size?.width||0;const b=x>0?Math.min(e.children.length,x):e.children.length;if(b>0){const k=(L-b*l-l)/b;m.debug("abc95 (growing to fit) width",e.id,L,e.size?.width,k);for(const _ of e.children)_.size&&(_.size.width=k)}}e.size={width:L,height:w,x:0,y:0}}m.debug("setBlockSizes abc94 (done)",e.id,e?.size?.x,e?.size?.width,e?.size?.y,e?.size?.height)}u(G,"setBlockSizes");function ue(e,a,o=8){m.debug(`abc85 layout blocks (=>layoutBlocks) ${e.id} x: ${e?.size?.x} y: ${e?.size?.y} width: ${e?.size?.width}`);const r=e.columns??-1;if(m.debug("layoutBlocks columns abc95",e.id,"=>",r,e),e.children&&e.children.length>0){const l=e?.children[0]?.size?.width??0,d=e.children.length*l+(e.children.length-1)*o;m.debug("widthOfChildren 88",d,"posX");const s=new Map;{let f=0;for(const L of e.children){if(!L.size)continue;const{py:w}=ne(r,f),b=s.get(w)??0;L.size.height>b&&s.set(w,L.size.height);let k=L?.widthInColumns??1;r>0&&(k=Math.min(k,r-f%r)),f+=k}}const h=new Map;{let f=0;const L=[...s.keys()].sort((w,b)=>w-b);for(const w of L)h.set(w,f),f+=(s.get(w)??0)+o}let x=0;m.debug("abc91 block?.size?.x",e.id,e?.size?.x);let y=e?.size?.x?e?.size?.x+(-e?.size?.width/2||0):-o,S=0;for(const f of e.children){const L=e;if(!f.size)continue;const{width:w,height:b}=f.size,{px:k,py:_}=ne(r,x);if(_!=S&&(S=_,y=e?.size?.x?e?.size?.x+(-e?.size?.width/2||0):-o,m.debug("New row in layout for block",e.id," and child ",f.id,S)),m.debug(`abc89 layout blocks (child) id: ${f.id} Pos: ${x} (px, py) ${k},${_} (${L?.size?.x},${L?.size?.y}) parent: ${L.id} width: ${w}${o}`),L.size){const O=w/2;f.size.x=y+o+O,m.debug(`abc91 layout blocks (calc) px, pyid:${f.id} startingPos=X${y} new startingPosX${f.size.x} ${O} padding=${o} width=${w} halfWidth=${O} => x:${f.size.x} y:${f.size.y} ${f.widthInColumns} (width * (child?.w || 1)) / 2 ${w*(f?.widthInColumns??1)/2}`),y=f.size.x+O;const v=h.get(_)??0,g=s.get(_)??b;f.size.y=L.size.y-L.size.height/2+v+g/2+o,m.debug(`abc88 layout blocks (calc) px, pyid:${f.id}startingPosX${y}${o}${O}=>x:${f.size.x}y:${f.size.y}${f.widthInColumns}(width * (child?.w || 1)) / 2${w*(f?.widthInColumns??1)/2}`)}f.children&&ue(f,a,o);let T=f?.widthInColumns??1;r>0&&(T=Math.min(T,r-x%r)),x+=T,m.debug("abc88 columnsPos",f,x)}}m.debug(`layout blocks (<==layoutBlocks) ${e.id} x: ${e?.size?.x} y: ${e?.size?.y} width: ${e?.size?.width}`)}u(ue,"layoutBlocks");function ge(e,{minX:a,minY:o,maxX:r,maxY:l}={minX:0,minY:0,maxX:0,maxY:0}){if(e.size&&e.id!=="root"){const{x:d,y:s,width:h,height:x}=e.size;d-h/2<a&&(a=d-h/2),s-x/2<o&&(o=s-x/2),d+h/2>r&&(r=d+h/2),s+x/2>l&&(l=s+x/2)}if(e.children)for(const d of e.children)({minX:a,minY:o,maxX:r,maxY:l}=ge(d,{minX:a,minY:o,maxX:r,maxY:l}));return{minX:a,minY:o,maxX:r,maxY:l}}u(ge,"findBounds");function Ie(e){const a=e.getBlock("root");if(!a)return;const o=Le()?.block?.padding??8;G(a,e,0,0,o),ue(a,
Line truncated
|
||||
@@ -0,0 +1,38 @@
|
||||
import{d as Le}from"./chunk-F27PBJKO.js";import{s as Ie,g as Me,a as Be,b as Ve,_ as d,c as wt,l as he,d as Ye,e as Fe,f as Dt,h as Ue,i as be,j as Jt,w as je,k as $t,m as de}from"./mermaid.core.js";import"./index.js";var Ut=(function(){var t=d(function(yt,m,x,E){for(x=x||{},E=yt.length;E--;x[yt[E]]=m);return x},"o"),e=[1,24],s=[1,25],a=[1,26],u=[1,27],i=[1,28],n=[1,63],l=[1,64],r=[1,65],h=[1,66],y=[1,67],f=[1,68],b=[1,69],v=[1,29],w=[1,30],N=[1,31],O=[1,32],P=[1,33],I=[1,34],W=[1,35],X=[1,36],q=[1,37],H=[1,38],Q=[1,39],K=[1,40],G=[1,41],J=[1,42],Z=[1,43],$=[1,44],tt=[1,45],et=[1,46],nt=[1,47],st=[1,48],rt=[1,50],at=[1,51],it=[1,52],ot=[1,53],lt=[1,54],ct=[1,55],ut=[1,56],ht=[1,57],dt=[1,58],pt=[1,59],ft=[1,60],kt=[14,42],Wt=[14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],Ot=[12,14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],S=[1,82],k=[1,83],T=[1,84],C=[1,85],R=[12,14,42],ie=[12,14,33,42],Mt=[12,14,33,42,76,77,79,80],gt=[12,33],Xt=[34,36,37,38,39,40,41,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],qt={trace:d(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,direction:5,direction_tb:6,direction_bt:7,direction_rl:8,direction_lr:9,graphConfig:10,C4_CONTEXT:11,NEWLINE:12,statements:13,EOF:14,C4_CONTAINER:15,C4_COMPONENT:16,C4_DYNAMIC:17,C4_DEPLOYMENT:18,otherStatements:19,diagramStatements:20,otherStatement:21,title:22,accDescription:23,acc_title:24,acc_title_value:25,acc_descr:26,acc_descr_value:27,acc_descr_multiline_value:28,boundaryStatement:29,boundaryStartStatement:30,boundaryStopStatement:31,boundaryStart:32,LBRACE:33,ENTERPRISE_BOUNDARY:34,attributes:35,SYSTEM_BOUNDARY:36,BOUNDARY:37,CONTAINER_BOUNDARY:38,NODE:39,NODE_L:40,NODE_R:41,RBRACE:42,diagramStatement:43,PERSON:44,PERSON_EXT:45,SYSTEM:46,SYSTEM_DB:47,SYSTEM_QUEUE:48,SYSTEM_EXT:49,SYSTEM_EXT_DB:50,SYSTEM_EXT_QUEUE:51,CONTAINER:52,CONTAINER_DB:53,CONTAINER_QUEUE:54,CONTAINER_EXT:55,CONTAINER_EXT_DB:56,CONTAINER_EXT_QUEUE:57,COMPONENT:58,COMPONENT_DB:59,COMPONENT_QUEUE:60,COMPONENT_EXT:61,COMPONENT_EXT_DB:62,COMPONENT_EXT_QUEUE:63,REL:64,BIREL:65,REL_U:66,REL_D:67,REL_L:68,REL_R:69,REL_B:70,REL_INDEX:71,UPDATE_EL_STYLE:72,UPDATE_REL_STYLE:73,UPDATE_LAYOUT_CONFIG:74,attribute:75,STR:76,STR_KEY:77,STR_VALUE:78,ATTRIBUTE:79,ATTRIBUTE_EMPTY:80,$accept:0,$end:1},terminals_:{2:"error",6:"direction_tb",7:"direction_bt",8:"direction_rl",9:"direction_lr",11:"C4_CONTEXT",12:"NEWLINE",14:"EOF",15:"C4_CONTAINER",16:"C4_COMPONENT",17:"C4_DYNAMIC",18:"C4_DEPLOYMENT",22:"title",23:"accDescription",24:"acc_title",25:"acc_title_value",26:"acc_descr",27:"acc_descr_value",28:"acc_descr_multiline_value",33:"LBRACE",34:"ENTERPRISE_BOUNDARY",36:"SYSTEM_BOUNDARY",37:"BOUNDARY",38:"CONTAINER_BOUNDARY",39:"NODE",40:"NODE_L",41:"NODE_R",42:"RBRACE",44:"PERSON",45:"PERSON_EXT",46:"SYSTEM",47:"SYSTEM_DB",48:"SYSTEM_QUEUE",49:"SYSTEM_EXT",50:"SYSTEM_EXT_DB",51:"SYSTEM_EXT_QUEUE",52:"CONTAINER",53:"CONTAINER_DB",54:"CONTAINER_QUEUE",55:"CONTAINER_EXT",56:"CONTAINER_EXT_DB",57:"CONTAINER_EXT_QUEUE",58:"COMPONENT",59:"COMPONENT_DB",60:"COMPONENT_QUEUE",61:"COMPONENT_EXT",62:"COMPONENT_EXT_DB",63:"COMPONENT_EXT_QUEUE",64:"REL",65:"BIREL",66:"REL_U",67:"REL_D",68:"REL_L",69:"REL_R",70:"REL_B",71:"REL_INDEX",72:"UPDATE_EL_STYLE",73:"UPDATE_REL_STYLE",74:"UPDATE_LAYOUT_CONFIG",76:"STR",77:"STR_KEY",78:"STR_VALUE",79:"ATTRIBUTE",80:"ATTRIBUTE_EMPTY"},productions_:[0,[3,1],[3,1],[5,1],[5,1],[5,1],[5,1],[4,1],[10,4],[10,4],[10,4],[10,4],[10,4],[13,1],[13,1],[13,2],[19,1],[19,2],[19,3],[21,1],[21,1],[21,2],[21,2],[21,1],[29,3],[30,3],[30,3],[30,4],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[31,1],[20,1],[20,2],[20,3],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,1],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[35,1],[35,2],[75,1],[75,2],[75,1],[75,1]],performAction:d(function(m,x,E,_,D,o,Pt){var p=o.length-1;switch(D){case 3:_.setDirection("TB");break;case 4:_.setDirection("BT");break;case 5:_.setDirection("RL");break;case 6:_.setDirection("LR");break;case 8:case 9:case 10:case 11:case 12:_.setC4Type(o[p-3]);break;case 19:_.setTitle(o[p].substring(6)),this.$=o[p].substring(6);break;case 20:_.setAccDescription(o[p].substring(15)),this.$=o[p].substring(15);break;case 21:this.$=o[p].trim(),_.setTitle(this.$);break;case 22:case 23:this.$=o[p].trim(),_.setAccDescription(this.$);break;case 28:o[p].splice(2,0,"ENTERPRISE"),_.addPersonOrSystemBoundary(...o[p]),this.$=o[p];break;case 29:o[p].splice(2,0,"SYSTEM"),_.addPersonOrSystemBoundary(...o[p]),this.$=o[p];break;case 30:_.addPersonOrSystemBoundary(...o[p]),this.$=o[p];break;case 31:o[p].splice(2,0,"CONTAINER"),_.addContainerBoundary(...o[p]),this.$=o[p];break;case 32:_.addDeploymentNode("node",...Line truncated
|
||||
`+A.showPosition()+`
|
||||
Expecting `+Yt.join(", ")+", got '"+(this.terminals_[M]||M)+"'":Gt="Parse error on line "+(xt+1)+": Unexpected "+(M==le?"end of input":"'"+(this.terminals_[M]||M)+"'"),this.parseError(Gt,{text:A.match,token:this.terminals_[M]||M,line:A.yylineno,loc:Qt,expected:Yt})}if(V[0]instanceof Array&&V.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Et+", token: "+M);switch(V[0]){case 1:E.push(M),D.push(A.yytext),o.push(A.yylloc),E.push(V[1]),M=null,oe=A.yyleng,p=A.yytext,xt=A.yylineno,Qt=A.yylloc;break;case 2:if(j=this.productions_[V[1]][1],Tt.$=D[D.length-j],Tt._$={first_line:o[o.length-(j||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(j||1)].first_column,last_column:o[o.length-1].last_column},Ne&&(Tt._$.range=[o[o.length-(j||1)].range[0],o[o.length-1].range[1]]),Kt=this.performAction.apply(Tt,[p,oe,xt,vt.yy,V[1],D,o].concat(De)),typeof Kt<"u")return Kt;j&&(E=E.slice(0,-1*j*2),D=D.slice(0,-1*j),o=o.slice(0,-1*j)),E.push(this.productions_[V[1]][0]),D.push(Tt.$),o.push(Tt._$),ue=Pt[E[E.length-2]][E[E.length-1]],E.push(ue);break;case 3:return!0}}return!0},"parse")},Oe=(function(){var yt={EOF:1,parseError:d(function(x,E){if(this.yy.parser)this.yy.parser.parseError(x,E);else throw new Error(x)},"parseError"),setInput:d(function(m,x){return this.yy=x||this.yy||{},this._input=m,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:d(function(){var m=this._input[0];this.yytext+=m,this.yyleng++,this.offset++,this.match+=m,this.matched+=m;var x=m.match(/(?:\r\n?|\n).*/g);return x?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),m},"input"),unput:d(function(m){var x=m.length,E=m.split(/(?:\r\n?|\n)/g);this._input=m+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-x),this.offset-=x;var _=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),E.length-1&&(this.yylineno-=E.length-1);var D=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:E?(E.length===_.length?this.yylloc.first_column:0)+_[_.length-E.length].length-E[0].length:this.yylloc.first_column-x},this.options.ranges&&(this.yylloc.range=[D[0],D[0]+this.yyleng-x]),this.yyleng=this.yytext.length,this},"unput"),more:d(function(){return this._more=!0,this},"more"),reject:d(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:d(function(m){this.unput(this.match.slice(m))},"less"),pastInput:d(function(){var m=this.matched.substr(0,this.matched.length-this.match.length);return(m.length>20?"...":"")+m.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:d(function(){var m=this.match;return m.length<20&&(m+=this._input.substr(0,20-m.length)),(m.substr(0,20)+(m.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:d(function(){var m=this.pastInput(),x=new Array(m.length+1).join("-");return m+this.upcomingInput()+`
|
||||
`+x+"^"},"showPosition"),test_match:d(function(m,x){var E,_,D;if(this.options.backtrack_lexer&&(D={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(D.yylloc.range=this.yylloc.range.slice(0))),_=m[0].match(/(?:\r\n?|\n).*/g),_&&(this.yylineno+=_.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:_?_[_.length-1].length-_[_.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+m[0].length},this.yytext+=m[0],this.match+=m[0],this.matches=m,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(m[0].length),this.matched+=m[0],E=this.performAction.call(this,this.yy,this,x,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),E)return E;if(this._backtrack){for(var o in D)this[o]=D[o];return!1}return!1},"test_match"),next:d(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var m,x,E,_;this._more||(this.yytext="",this.match="");for(var D=this._currentRules(),o=0;o<D.length;o++)if(E=this._input.match(this.rules[D[o]]),E&&(!x||E[0].length>x[0].length)){if(x=E,_=o,this.options.backtrack_lexer){if(m=this.test_match(E,D[o]),m!==!1)return m;if(this._backtrack){x=!1;continue}else return!1}else if(!this.options.flex)break}return x?(m=this.test_match(x,D[_]),m!==!1?m:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:d(function(){var x=this.next();return x||this.lex()},"lex"),begin:d(function(x){this.conditionStack.push(x)},"begin"),popState:d(function(){var x=this.conditionStack.length-1;return x>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:d(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:d(function(x){return x=this.conditionStack.length-1-Math.abs(x||0),x>=0?this.conditionStack[x]:"INITIAL"},"topState"),pushState:d(function(x){this.begin(x)},"pushState"),stateStackSize:d(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:d(function(x,E,_,D){switch(_){case 0:return 6;case 1:return 7;case 2:return 8;case 3:return 9;case 4:return 22;case 5:return 23;case 6:return this.begin("acc_title"),24;case 7:return this.popState(),"acc_title_value";case 8:return this.begin("acc_descr"),26;case 9:return this.popState(),"acc_descr_value";case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:break;case 14:c;break;case 15:return 12;case 16:break;case 17:return 11;case 18:return 15;case 19:return 16;case 20:return 17;case 21:return 18;case 22:return this.begin("person_ext"),45;case 23:return this.begin("person"),44;case 24:return this.begin("system_ext_queue"),51;case 25:return this.begin("system_ext_db"),50;case 26:return this.begin("system_ext"),49;case 27:return this.begin("system_queue"),48;case 28:return this.begin("system_db"),47;case 29:return this.begin("system"),46;case 30:return this.begin("boundary"),37;case 31:return this.begin("enterprise_boundary"),34;case 32:return this.begin("system_boundary"),36;case 33:return this.begin("container_ext_queue"),57;case 34:return this.begin("container_ext_db"),56;case 35:return this.begin("container_ext"),55;case 36:return this.begin("container_queue"),54;case 37:return this.begin("container_db"),53;case 38:return this.begin("container"),52;case 39:return this.begin("container_boundary"),38;case 40:return this.begin("component_ext_queue"),63;case 41:return this.begin("component_ext_db"),62;case 42:return this.begin("component_ext"),61;case 43:return this.begin("component_queue"),60;case 44:return this.begin("component_db"),59;case 45:return this.begin("component"),58;case 46:return this.begin("node"),39;case 47:return this.begin("node"),39;case 48:return this.begin("node_l"),40;case 49:return this.begin("node_r"),41;case 50:return this.begin("rel"),64;case 51:return this.begin("birel"),65;case 52:return this.begin("rel_u"),66;case 53:return this.begin("rel_u"),66;case 54:return this.begin("rel_d"),67;case 55:return this.begin("rel_d"),67;case 56:return this.begin("rel_l"),68;case 57:return this.begin("rel_l"),68;case 58:return this.begin("rel_r"),69;case 59:return this.begin("rel_r"),69;case 60:return this.begin("rel_b"),70;case 61:return this.begin("rel_index"),71;case 62:return this.begin("update_el_style"),72;case 63:return this.begin("update_rel_style"),73;case 64:return this.begin("update_layout_config"),74;case 65:return"EOF_IN_STRUCT";case 66:return this.begin("attribute"),"ATTRIBUTE_EMPTY";case 67:this.begin("attribute");break;case 68:this.popState(),this.popState();break;case 69:return 80;case 70:break;case 71:return 80;case 72:this.begin("string");break;case 73:this.popState();break;case 74:return"STR";case 75:this.begin("string_kv");break;case 76:return this.begin("string_kv_key"),"STR_KEY";case 77:this.popState(),this.begin("string_kv_value");break;case 78:return"STR_VALUE";case 79:this.popState(),this.popState();break;case 80:return"STR";case 81:return"LBRACE";case 82:return"RBRACE";case 83:return"SPACE";case 84:return"EOL";case 85:return 14}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:title\s[^#\n;]+)/,/^(?:accDescription\s[^#\n;]+)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:C4Context\b)/,/^(?:C4Container\b)/,/^(?:C4Component\b)/,/^(?:C4Dynamic\b)/,/^(?:C4Deployment\b)/,/^(?:Person_Ext\b)/,/^(?:Person\b)/,/^(?:SystemQueue_Ext\b)/,/^(?:SystemDb_Ext\b)/,/^(?:System_Ext\b)/,/^(?:SystemQueue\b)/,/^(?:SystemDb\b)/,/^(?:System\b)/,/^(?:Boundary\b)/,/^(?:Enterprise_Boundary\b)/,/^(?:System_Boundary\b)/,/^(?:ContainerQueue_Ext\b)/,/^(?:ContainerDb_Ext\b)/,/^(?:Container_Ext\b)/,/^(?:ContainerQueue\b)/,/^(?:ContainerDb\b)/,/^(?:Container\b)/,/^(?:Container_Boundary\b)/,/^(?:ComponentQueue_Ext\b)/,/^(?:ComponentDb_Ext\b)/,/^(?:Component_Ext\b)/,/^(?:ComponentQueue\b)/,/^(?:ComponentDb\b)/,/^(?:Component\bLine truncated
|
||||
`)},"elementFontStyles"),W0=d(t=>`.person {
|
||||
stroke: ${t.personBorder};
|
||||
fill: ${t.personBkg};
|
||||
}
|
||||
${z0()}
|
||||
|
||||
/* The element font colour is set inline per element (default white); the
|
||||
label text takes it via currentColor. */
|
||||
.c4-shape .label,
|
||||
.c4-shape .label text {
|
||||
color: inherit;
|
||||
fill: currentColor;
|
||||
}
|
||||
/* Structurizr typography: bold name, smaller stereotype/type and description lines. */
|
||||
.c4-shape .label .c4-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
.c4-shape .label .c4-type {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
.c4-shape .label .c4-descr {
|
||||
font-size: 0.82em;
|
||||
}
|
||||
.c4-shape .basic,
|
||||
.c4-shape rect,
|
||||
.c4-shape path,
|
||||
.c4-shape circle,
|
||||
.c4-shape ellipse,
|
||||
.c4-shape line {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
`,"getStyles"),X0=W0,K0={parser:ze,db:te,renderer:_e,styles:X0,init:d(({c4:t,wrap:e})=>{_e.setConf(t),te.setWrap(e)},"init")};export{K0 as diagram};
|
||||
@@ -0,0 +1 @@
|
||||
import{ai as o,aj as n}from"./mermaid.core.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as s}from"./mermaid.core.js";var t,e=(t=class{constructor(i){this.init=i,this.records=this.init()}reset(){this.records=this.init()}},s(t,"ImperativeState"),t);export{e as I};
|
||||
@@ -0,0 +1,15 @@
|
||||
import{_ as e}from"./mermaid.core.js";var l=e(()=>`
|
||||
/* Font Awesome icon styling - consolidated */
|
||||
.label-icon {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
}
|
||||
|
||||
.node .label-icon path {
|
||||
fill: currentColor;
|
||||
stroke: revert;
|
||||
stroke-width: revert;
|
||||
}
|
||||
`,"getIconStyles");export{l as g};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as i,j as l,U as d,G as o}from"./mermaid.core.js";var x=i((r,t)=>{const e=r.append("rect");if(e.attr("x",t.x),e.attr("y",t.y),e.attr("fill",t.fill),e.attr("stroke",t.stroke),e.attr("width",t.width),e.attr("height",t.height),t.name&&e.attr("name",t.name),t.rx&&e.attr("rx",t.rx),t.ry&&e.attr("ry",t.ry),t.attrs!==void 0)for(const s in t.attrs)e.attr(s,t.attrs[s]);return t.class&&e.attr("class",t.class),e},"drawRect"),p=i((r,t)=>{const e={x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,stroke:t.stroke,class:"rect"};x(r,e).lower()},"drawBackgroundRect"),y=i((r,t)=>{const e=t.text.replace(d," "),s=r.append("text");s.attr("x",t.x),s.attr("y",t.y),s.attr("class","legend"),s.style("text-anchor",t.anchor),t.class&&s.attr("class",t.class);const a=s.append("tspan");return a.attr("x",t.x+t.textMargin*2),a.text(e),s},"drawText"),m=i((r,t,e,s)=>{const a=r.append("image");a.attr("x",t),a.attr("y",e);const n=o.sanitizeUrl(s);a.attr("xlink:href",n)},"drawImage"),g=i((r,t,e,s)=>{const a=r.append("use");a.attr("x",t),a.attr("y",e);const n=o.sanitizeUrl(s);a.attr("xlink:href",`#${n}`)},"drawEmbeddedImage"),h=i(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),f=i(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj"),w=i(()=>{let r=l(".mermaidTooltip");return r.empty()&&(r=l("body").append("div").attr("class","mermaidTooltip").style("opacity",0).style("position","absolute").style("text-align","center").style("max-width","200px").style("padding","2px").style("font-size","12px").style("background","#ffffde").style("border","1px solid #333").style("border-radius","2px").style("pointer-events","none").style("z-index","100")),r},"createTooltip");export{p as a,h as b,g as c,x as d,m as e,w as f,f as g,y as h};
|
||||
@@ -0,0 +1,231 @@
|
||||
import{g as ee}from"./chunk-XXDRQBXY.js";import{s as se}from"./chunk-POPQ4Y6H.js";import{_ as p,l as b,c as $,v as ie,x as re,a as ae,b as ne,g as oe,s as le,n as ce,o as he,T as ue,k as j,p as de,j as kt,O as fe}from"./mermaid.core.js";import{f as pe}from"./chunk-F27PBJKO.js";var Ct=(function(){var t=p(function(V,o,h,a){for(h=h||{},a=V.length;a--;h[V[a]]=o);return h},"o"),e=[1,2],s=[1,3],n=[1,4],r=[2,4],c=[1,9],u=[1,11],S=[1,16],d=[1,17],T=[1,18],E=[1,19],m=[1,33],L=[1,20],D=[1,21],f=[1,22],I=[1,23],R=[1,24],C=[1,26],F=[1,27],A=[1,28],P=[1,29],w=[1,30],z=[1,31],rt=[1,32],at=[1,35],nt=[1,36],ot=[1,37],lt=[1,38],K=[1,34],y=[1,4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,41,45,48,51,52,53,54,57],ct=[1,4,5,14,15,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,39,40,41,45,48,51,52,53,54,57],Lt=[4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,41,45,48,51,52,53,54,57],gt={trace:p(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,SD:6,document:7,line:8,statement:9,classDefStatement:10,styleStatement:11,cssClassStatement:12,idStatement:13,DESCR:14,"-->":15,HIDE_EMPTY:16,scale:17,WIDTH:18,COMPOSIT_STATE:19,STRUCT_START:20,STRUCT_STOP:21,STATE_DESCR:22,AS:23,ID:24,FORK:25,JOIN:26,CHOICE:27,CONCURRENT:28,note:29,notePosition:30,NOTE_TEXT:31,direction:32,acc_title:33,acc_title_value:34,acc_descr:35,acc_descr_value:36,acc_descr_multiline_value:37,CLICK:38,STRING:39,HREF:40,classDef:41,CLASSDEF_ID:42,CLASSDEF_STYLEOPTS:43,DEFAULT:44,style:45,STYLE_IDS:46,STYLEDEF_STYLEOPTS:47,class:48,CLASSENTITY_IDS:49,STYLECLASS:50,direction_tb:51,direction_bt:52,direction_rl:53,direction_lr:54,eol:55,";":56,EDGE_STATE:57,STYLE_SEPARATOR:58,left_of:59,right_of:60,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",6:"SD",14:"DESCR",15:"-->",16:"HIDE_EMPTY",17:"scale",18:"WIDTH",19:"COMPOSIT_STATE",20:"STRUCT_START",21:"STRUCT_STOP",22:"STATE_DESCR",23:"AS",24:"ID",25:"FORK",26:"JOIN",27:"CHOICE",28:"CONCURRENT",29:"note",31:"NOTE_TEXT",33:"acc_title",34:"acc_title_value",35:"acc_descr",36:"acc_descr_value",37:"acc_descr_multiline_value",38:"CLICK",39:"STRING",40:"HREF",41:"classDef",42:"CLASSDEF_ID",43:"CLASSDEF_STYLEOPTS",44:"DEFAULT",45:"style",46:"STYLE_IDS",47:"STYLEDEF_STYLEOPTS",48:"class",49:"CLASSENTITY_IDS",50:"STYLECLASS",51:"direction_tb",52:"direction_bt",53:"direction_rl",54:"direction_lr",56:";",57:"EDGE_STATE",58:"STYLE_SEPARATOR",59:"left_of",60:"right_of"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[9,1],[9,1],[9,1],[9,1],[9,2],[9,3],[9,4],[9,1],[9,2],[9,1],[9,4],[9,3],[9,6],[9,1],[9,1],[9,1],[9,1],[9,4],[9,4],[9,1],[9,2],[9,2],[9,1],[9,5],[9,5],[10,3],[10,3],[11,3],[12,3],[32,1],[32,1],[32,1],[32,1],[55,1],[55,1],[13,1],[13,1],[13,3],[13,3],[30,1],[30,1]],performAction:p(function(o,h,a,g,_,i,B){var l=i.length-1;switch(_){case 3:return g.setRootDoc(i[l]),i[l];case 4:this.$=[];break;case 5:i[l]!="nl"&&(i[l-1].push(i[l]),this.$=i[l-1]);break;case 6:case 7:this.$=i[l];break;case 8:this.$="nl";break;case 12:this.$=i[l];break;case 13:const Z=i[l-1];Z.description=g.trimColon(i[l]),this.$=Z;break;case 14:this.$={stmt:"relation",state1:i[l-2],state2:i[l]};break;case 15:const Tt=g.trimColon(i[l]);this.$={stmt:"relation",state1:i[l-3],state2:i[l-1],description:Tt};break;case 19:this.$={stmt:"state",id:i[l-3],type:"default",description:"",doc:i[l-1]};break;case 20:var Y=i[l],X=i[l-2].trim();if(i[l].match(":")){var ut=i[l].split(":");Y=ut[0],X=[X,ut[1]]}this.$={stmt:"state",id:Y,type:"default",description:X};break;case 21:this.$={stmt:"state",id:i[l-3],type:"default",description:i[l-5],doc:i[l-1]};break;case 22:this.$={stmt:"state",id:i[l],type:"fork"};break;case 23:this.$={stmt:"state",id:i[l],type:"join"};break;case 24:this.$={stmt:"state",id:i[l],type:"choice"};break;case 25:this.$={stmt:"state",id:g.getDividerId(),type:"divider"};break;case 26:this.$={stmt:"state",id:i[l-1].trim(),note:{position:i[l-2].trim(),text:i[l].trim()}};break;case 29:this.$=i[l].trim(),g.setAccTitle(this.$);break;case 30:case 31:this.$=i[l].trim(),g.setAccDescription(this.$);break;case 32:this.$={stmt:"click",id:i[l-3],url:i[l-2],tooltip:i[l-1]};break;case 33:this.$={stmt:"click",id:i[l-3],url:i[l-1],tooltip:""};break;case 34:case 35:this.$={stmt:"classDef",id:i[l-1].trim(),classes:i[l].trim()};break;case 36:this.$={stmt:"style",id:i[l-1].trim(),styleClass:i[l].trim()};break;case 37:this.$={stmt:"applyClass",id:i[l-1].trim(),styleClass:i[l].trim()};break;case 38:g.setDirection("TB"),this.$={stmt:"dir",value:"TB"};break;case 39:g.setDirection("BT"),this.$={stmt:"dir",value:"BT"};break;case 40:g.setDirection("RL"),this.$={stmt:"dir",value:"RL"};break;case 41:g.setDirection("LR"),this.$={stmt:"dir",value:"LR"};break;case 44:case 45:this.$={stmt:"state",id:i[l].trim(),type:"default",description:""};break;case 46:this.$={stmt:"state",id:i[l-2].trim(),classes:[i[l].trim()],type:"default",description:""};break;case 47:this.$={stmt:"state",id:i[l-2].trim(),classes:[i[l].trim()],type:"default",description:""};break}Line truncated
|
||||
`+k.showPosition()+`
|
||||
Expecting `+ft.join(", ")+", got '"+(this.terminals_[x]||x)+"'":bt="Parse error on line "+(Y+1)+": Unexpected "+(x==Z?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(bt,{text:k.match,token:this.terminals_[x]||x,line:k.yylineno,loc:_t,expected:ft})}if(N[0]instanceof Array&&N.length>1)throw new Error("Parse Error: multiple actions possible at state: "+W+", token: "+x);switch(N[0]){case 1:a.push(x),_.push(k.yytext),i.push(k.yylloc),a.push(N[1]),x=null,X=k.yyleng,l=k.yytext,Y=k.yylineno,_t=k.yylloc;break;case 2:if(G=this.productions_[N[1]][1],J.$=_[_.length-G],J._$={first_line:i[i.length-(G||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(G||1)].first_column,last_column:i[i.length-1].last_column},Zt&&(J._$.range=[i[i.length-(G||1)].range[0],i[i.length-1].range[1]]),mt=this.performAction.apply(J,[l,X,Y,U.yy,N[1],_,i].concat(Tt)),typeof mt<"u")return mt;G&&(a=a.slice(0,-1*G*2),_=_.slice(0,-1*G),i=i.slice(0,-1*G)),a.push(this.productions_[N[1]][0]),_.push(J.$),i.push(J._$),wt=B[a[a.length-2]][a[a.length-1]],a.push(wt);break;case 3:return!0}}return!0},"parse")},Qt=(function(){var V={EOF:1,parseError:p(function(h,a){if(this.yy.parser)this.yy.parser.parseError(h,a);else throw new Error(h)},"parseError"),setInput:p(function(o,h){return this.yy=h||this.yy||{},this._input=o,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:p(function(){var o=this._input[0];this.yytext+=o,this.yyleng++,this.offset++,this.match+=o,this.matched+=o;var h=o.match(/(?:\r\n?|\n).*/g);return h?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),o},"input"),unput:p(function(o){var h=o.length,a=o.split(/(?:\r\n?|\n)/g);this._input=o+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-h),this.offset-=h;var g=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),a.length-1&&(this.yylineno-=a.length-1);var _=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:a?(a.length===g.length?this.yylloc.first_column:0)+g[g.length-a.length].length-a[0].length:this.yylloc.first_column-h},this.options.ranges&&(this.yylloc.range=[_[0],_[0]+this.yyleng-h]),this.yyleng=this.yytext.length,this},"unput"),more:p(function(){return this._more=!0,this},"more"),reject:p(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:p(function(o){this.unput(this.match.slice(o))},"less"),pastInput:p(function(){var o=this.matched.substr(0,this.matched.length-this.match.length);return(o.length>20?"...":"")+o.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:p(function(){var o=this.match;return o.length<20&&(o+=this._input.substr(0,20-o.length)),(o.substr(0,20)+(o.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:p(function(){var o=this.pastInput(),h=new Array(o.length+1).join("-");return o+this.upcomingInput()+`
|
||||
`+h+"^"},"showPosition"),test_match:p(function(o,h){var a,g,_;if(this.options.backtrack_lexer&&(_={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(_.yylloc.range=this.yylloc.range.slice(0))),g=o[0].match(/(?:\r\n?|\n).*/g),g&&(this.yylineno+=g.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:g?g[g.length-1].length-g[g.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+o[0].length},this.yytext+=o[0],this.match+=o[0],this.matches=o,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(o[0].length),this.matched+=o[0],a=this.performAction.call(this,this.yy,this,h,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a)return a;if(this._backtrack){for(var i in _)this[i]=_[i];return!1}return!1},"test_match"),next:p(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var o,h,a,g;this._more||(this.yytext="",this.match="");for(var _=this._currentRules(),i=0;i<_.length;i++)if(a=this._input.match(this.rules[_[i]]),a&&(!h||a[0].length>h[0].length)){if(h=a,g=i,this.options.backtrack_lexer){if(o=this.test_match(a,_[i]),o!==!1)return o;if(this._backtrack){h=!1;continue}else return!1}else if(!this.options.flex)break}return h?(o=this.test_match(h,_[g]),o!==!1?o:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:p(function(){var h=this.next();return h||this.lex()},"lex"),begin:p(function(h){this.conditionStack.push(h)},"begin"),popState:p(function(){var h=this.conditionStack.length-1;return h>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:p(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:p(function(h){return h=this.conditionStack.length-1-Math.abs(h||0),h>=0?this.conditionStack[h]:"INITIAL"},"topState"),pushState:p(function(h){this.begin(h)},"pushState"),stateStackSize:p(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:p(function(h,a,g,_){function i(){const B=a.yytext.indexOf("%%");if(B===0)return!1;if(B>0){const l=a.yytext.slice(0,B),Y=a.yytext.slice(B);Y&&h.lexer.unput(Y),a.yytext=l}return!0}switch(p(i,"processId"),g){case 0:return 38;case 1:return 40;case 2:return 39;case 3:return 44;case 4:return 51;case 5:return 52;case 6:return 53;case 7:return 54;case 8:return 5;case 9:break;case 10:break;case 11:break;case 12:break;case 13:return this.pushState("SCALE"),17;case 14:return 18;case 15:this.popState();break;case 16:return this.begin("acc_title"),33;case 17:return this.popState(),"acc_title_value";case 18:return this.begin("acc_descr"),35;case 19:return this.popState(),"acc_descr_value";case 20:this.begin("acc_descr_multiline");break;case 21:this.popState();break;case 22:return"acc_descr_multiline_value";case 23:return this.pushState("CLASSDEF"),41;case 24:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";case 25:return this.popState(),this.pushState("CLASSDEFID"),42;case 26:return this.popState(),43;case 27:return this.pushState("CLASS"),48;case 28:return this.popState(),this.pushState("CLASS_STYLE"),49;case 29:return this.popState(),50;case 30:return this.pushState("STYLE"),45;case 31:return this.popState(),this.pushState("STYLEDEF_STYLES"),46;case 32:return this.popState(),47;case 33:return this.pushState("SCALE"),17;case 34:return 18;case 35:this.popState();break;case 36:this.pushState("STATE");break;case 37:return this.popState(),a.yytext=a.yytext.slice(0,-8).trim(),25;case 38:return this.popState(),a.yytext=a.yytext.slice(0,-8).trim(),26;case 39:return this.popState(),a.yytext=a.yytext.slice(0,-10).trim(),27;case 40:return this.popState(),a.yytext=a.yytext.slice(0,-8).trim(),25;case 41:return this.popState(),a.yytext=a.yytext.slice(0,-8).trim(),26;case 42:return this.popState(),a.yytext=a.yytext.slice(0,-10).trim(),27;case 43:return 51;case 44:return 52;case 45:return 53;case 46:return 54;case 47:this.pushState("STATE_STRING");break;case 48:return this.pushState("STATE_ID"),"AS";case 49:return i()?(this.popState(),"ID"):void 0;case 50:this.popState();break;case 51:return"STATE_DESCR";case 52:throw new Error('Error: State name must be a single word. Found: "'+a.yytext.trim()+'"');case 53:return 19;case 54:this.popState();break;case 55:return this.popState(),this.pushState("struct"),20;case 56:return this.popState(),21;case 57:break;case 58:return this.begin("NOTE"),29;case 59:return this.popState(),this.pushState("NOTE_ID"),59;case 60:return this.popState(),this.pushState("NOTE_ID"),60;case 61:this.popState(),this.pushState("FLOATING_NOTE");break;case 62:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 63:break;case 64:return"NOTE_TEXT";case 65:return i()?(this.popState(),"ID"):void 0;case 66:return i()?(this.popState(),this.pushState("NOTE_TEXT"),24):void 0;case 67:return this.popState(),a.yytext=a.yytext.substr(2).trim(),31;case 68:return this.popState(),a.yytext=a.yytext.slice(0,-8).trim(),31;case 69:return 6;case 70:return 6;case 71:return 16;case 72:return 57;case 73:return i()?24:void 0;case 74:return a.yytext=a.yytext.trim(),14;case 75:return 15;case 76:return 28;case 77:return 58;case 78:return 5;case 79:return"INVALID"}},"anonymous"),rules:[/^(?:click\b)/i,/^(?:href\b)/i,/^(?:"[^"]*")/i,/^(?:default\b)/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:classDef\s+)/i,/^(?:DEFAULT\s+)/i,/^(?:\w+\s+)/i,/^(?:[^\n]*)/i,/^(?:class\s+)/i,/^(?:(\w+)+((,\s*\w+)*))/i,/^(?:[^\n]*)/i,/^(?:style\s+)/i,/^(?:[\w,]+\s+)/i,/^(?:[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<<fork>>)/i,/^(?:.*<<join>>)/i,/^(?:.*<<choice>>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:.*\[\[choice\]\])/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*directio
Line truncated
|
||||
defs [id$="-barbEnd"] {
|
||||
fill: ${t.transitionColor};
|
||||
stroke: ${t.transitionColor};
|
||||
}
|
||||
g.stateGroup text {
|
||||
fill: ${t.nodeBorder};
|
||||
stroke: none;
|
||||
font-size: 10px;
|
||||
}
|
||||
g.stateGroup text {
|
||||
fill: ${t.textColor};
|
||||
stroke: none;
|
||||
font-size: 10px;
|
||||
|
||||
}
|
||||
g.stateGroup .state-title {
|
||||
font-weight: bolder;
|
||||
fill: ${t.stateLabelColor};
|
||||
}
|
||||
|
||||
g.stateGroup rect {
|
||||
fill: ${t.mainBkg};
|
||||
stroke: ${t.nodeBorder};
|
||||
}
|
||||
|
||||
g.stateGroup line {
|
||||
stroke: ${t.lineColor};
|
||||
stroke-width: ${t.strokeWidth||1};
|
||||
}
|
||||
|
||||
.transition {
|
||||
stroke: ${t.transitionColor};
|
||||
stroke-width: ${t.strokeWidth||1};
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.stateGroup .composit {
|
||||
fill: ${t.background};
|
||||
border-bottom: 1px
|
||||
}
|
||||
|
||||
.stateGroup .alt-composit {
|
||||
fill: #e0e0e0;
|
||||
border-bottom: 1px
|
||||
}
|
||||
|
||||
.state-note {
|
||||
stroke: ${t.noteBorderColor};
|
||||
fill: ${t.noteBkgColor};
|
||||
|
||||
text {
|
||||
fill: ${t.noteTextColor};
|
||||
stroke: none;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.stateLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: ${t.mainBkg};
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.edgeLabel .label rect {
|
||||
fill: ${t.labelBackgroundColor};
|
||||
opacity: 0.5;
|
||||
}
|
||||
.edgeLabel {
|
||||
background-color: ${t.edgeLabelBackground};
|
||||
p {
|
||||
background-color: ${t.edgeLabelBackground};
|
||||
}
|
||||
rect {
|
||||
opacity: 0.5;
|
||||
background-color: ${t.edgeLabelBackground};
|
||||
fill: ${t.edgeLabelBackground};
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
.edgeLabel .label text {
|
||||
fill: ${t.transitionLabelColor||t.tertiaryTextColor};
|
||||
}
|
||||
.label div .edgeLabel {
|
||||
color: ${t.transitionLabelColor||t.tertiaryTextColor};
|
||||
}
|
||||
|
||||
.stateLabel text {
|
||||
fill: ${t.stateLabelColor};
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.node circle.state-start {
|
||||
fill: ${t.specialStateColor};
|
||||
stroke: ${t.specialStateColor};
|
||||
}
|
||||
|
||||
.node .fork-join {
|
||||
fill: ${t.specialStateColor};
|
||||
stroke: ${t.specialStateColor};
|
||||
}
|
||||
|
||||
.node circle.state-end {
|
||||
fill: ${t.innerEndBackground};
|
||||
stroke: ${t.background};
|
||||
stroke-width: 1.5
|
||||
}
|
||||
.end-state-inner {
|
||||
fill: ${t.compositeBackground||t.background};
|
||||
// stroke: ${t.background};
|
||||
stroke-width: 1.5
|
||||
}
|
||||
|
||||
.node rect {
|
||||
fill: ${t.stateBkg||t.mainBkg};
|
||||
stroke: ${t.stateBorder||t.nodeBorder};
|
||||
stroke-width: ${t.strokeWidth||1}px;
|
||||
}
|
||||
.node polygon {
|
||||
fill: ${t.mainBkg};
|
||||
stroke: ${t.stateBorder||t.nodeBorder};;
|
||||
stroke-width: ${t.strokeWidth||1}px;
|
||||
}
|
||||
[id$="-barbEnd"] {
|
||||
fill: ${t.lineColor};
|
||||
}
|
||||
|
||||
.statediagram-cluster rect {
|
||||
fill: ${t.compositeTitleBackground};
|
||||
stroke: ${t.stateBorder||t.nodeBorder};
|
||||
stroke-width: ${t.strokeWidth||1}px;
|
||||
}
|
||||
|
||||
.cluster-label, .nodeLabel {
|
||||
color: ${t.stateLabelColor};
|
||||
// line-height: 1;
|
||||
}
|
||||
|
||||
.statediagram-cluster rect.outer {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
.statediagram-state .divider {
|
||||
stroke: ${t.stateBorder||t.nodeBorder};
|
||||
}
|
||||
|
||||
.statediagram-state .title-state {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
.statediagram-cluster.statediagram-cluster .inner {
|
||||
fill: ${t.compositeBackground||t.background};
|
||||
}
|
||||
.statediagram-cluster.statediagram-cluster-alt .inner {
|
||||
fill: ${t.altBackground?t.altBackground:"#efefef"};
|
||||
}
|
||||
|
||||
.statediagram-cluster .inner {
|
||||
rx:0;
|
||||
ry:0;
|
||||
}
|
||||
|
||||
.statediagram-state rect.basic {
|
||||
rx: 5px;
|
||||
ry: 5px;
|
||||
}
|
||||
.statediagram-state rect.divider {
|
||||
stroke-dasharray: 10,10;
|
||||
fill: ${t.altBackground?t.altBackground:"#efefef"};
|
||||
}
|
||||
|
||||
.note-edge {
|
||||
stroke-dasharray: 5;
|
||||
}
|
||||
|
||||
.statediagram-note rect {
|
||||
fill: ${t.noteBkgColor};
|
||||
stroke: ${t.noteBorderColor};
|
||||
stroke-width: 1px;
|
||||
rx: 0;
|
||||
ry: 0;
|
||||
}
|
||||
.statediagram-note rect {
|
||||
fill: ${t.noteBkgColor};
|
||||
stroke: ${t.noteBorderColor};
|
||||
stroke-width: 1px;
|
||||
rx: 0;
|
||||
ry: 0;
|
||||
}
|
||||
|
||||
.statediagram-note text {
|
||||
fill: ${t.noteTextColor};
|
||||
}
|
||||
|
||||
.statediagram-note .nodeLabel {
|
||||
color: ${t.noteTextColor};
|
||||
}
|
||||
.statediagram .edgeLabel {
|
||||
color: red; // ${t.noteTextColor};
|
||||
}
|
||||
|
||||
[id$="-dependencyStart"], [id$="-dependencyEnd"] {
|
||||
fill: ${t.lineColor};
|
||||
stroke: ${t.lineColor};
|
||||
stroke-width: ${t.strokeWidth||1};
|
||||
}
|
||||
|
||||
.statediagramTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${t.textColor};
|
||||
}
|
||||
|
||||
[data-look="neo"].statediagram-cluster rect {
|
||||
fill: ${t.mainBkg};
|
||||
stroke: ${t.useGradient?"url("+t.svgId+"-gradient)":t.stateBorder||t.nodeBorder};
|
||||
stroke-width: ${t.strokeWidth??1};
|
||||
}
|
||||
[data-look="neo"].statediagram-cluster rect.outer {
|
||||
rx: ${t.radius}px;
|
||||
ry: ${t.radius}px;
|
||||
filter: ${t.dropShadow?t.dropShadow.replace("url(#drop-shadow)",`url(${t.svgId}-drop-shadow)`):"none"}
|
||||
}
|
||||
`,"getStyles"),ze=Ye;export{He as S,We as a,je as b,ze as s};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as i}from"./mermaid.core.js";function t(c,e){c.accDescr&&e.setAccDescription?.(c.accDescr),c.accTitle&&e.setAccTitle?.(c.accTitle),c.title&&e.setDiagramTitle?.(c.title)}i(t,"populateCommonDb");export{t as p};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as a,d as w,l as x}from"./mermaid.core.js";var B=a((e,t,i,r)=>{e.attr("class",i);const{width:o,height:h,x:n,y:c}=u(e,t);w(e,h,o,r);const s=d(n,c,o,h,t);e.attr("viewBox",s),x.debug(`viewBox configured: ${s} with padding: ${t}`)},"setupViewPortForSVG"),u=a((e,t)=>{const i=e.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:i.width+t*2,height:i.height+t*2,x:i.x,y:i.y}},"calculateDimensionsWithPadding"),d=a((e,t,i,r,o)=>`${e-o} ${t-o} ${i} ${r}`,"createViewBox");export{B as s};
|
||||
@@ -0,0 +1,88 @@
|
||||
import{_ as p,l as C,D as H,y as B,p as q,E as G,d as U,i as j,c as K}from"./mermaid.core.js";var A="",M="",O="",D=[],b=new Map,v=p(e=>j(e,K()),"sanitizeText"),y=p(e=>{switch(e.type){case"terminal":return{...e,value:v(e.value)};case"nonterminal":return{...e,name:v(e.name)};case"sequence":return{...e,elements:e.elements.map(y)};case"choice":return{...e,alternatives:e.alternatives.map(y)};case"optional":return{...e,element:y(e.element)};case"repetition":return{...e,element:y(e.element),separator:e.separator?y(e.separator):void 0};case"special":return{...e,text:v(e.text)}}},"sanitizeAstNode"),J=p(()=>{A="",M="",O="",D.length=0,b.clear(),q(),C.debug("[Railroad] Database cleared")},"clear"),Y=p(e=>{A=v(e),C.debug("[Railroad] Title set:",e)},"setTitle"),P=p(()=>A,"getTitle"),Q=p(e=>{const i={...e,name:v(e.name),definition:y(e.definition),comment:e.comment?v(e.comment):void 0};C.debug("[Railroad] Adding rule:",i.name),b.has(i.name)&&C.warn(`[Railroad] Rule '${i.name}' is already defined. Overwriting.`),D.push(i),b.set(i.name,i)},"addRule"),Z=p(()=>D,"getRules"),V=p(e=>b.get(e),"getRule"),ee=p(e=>{M=v(e).replace(/^\s+/g,""),C.debug("[Railroad] Accessibility title set:",e)},"setAccTitle"),te=p(()=>M,"getAccTitle"),re=p(e=>{O=v(e).replace(/\n\s+/g,`
|
||||
`),C.debug("[Railroad] Accessibility description set:",e)},"setAccDescription"),ie=p(()=>O,"getAccDescription"),ne=Y,ae=P,oe={clear:J,setTitle:Y,getTitle:P,addRule:Q,getRules:Z,getRule:V,setAccTitle:ee,getAccTitle:te,setAccDescription:re,getAccDescription:ie,setDiagramTitle:ne,getDiagramTitle:ae},f={compactMode:!1,padding:10,verticalSeparation:8,horizontalSeparation:10,arcRadius:10,fontSize:14,fontFamily:"monospace",terminalFill:"#FFFFC0",terminalStroke:"#000000",terminalTextColor:"#000000",nonTerminalFill:"#FFFFFF",nonTerminalStroke:"#000000",nonTerminalTextColor:"#000000",lineColor:"#000000",strokeWidth:2,markerFill:"#000000",commentFill:"#E8E8E8",commentStroke:"#888888",commentTextColor:"#666666",specialFill:"#F0E0FF",specialStroke:"#8800CC",ruleNameColor:"#000066",showMarkers:!0,markerRadius:5},le=/^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$|^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch)\([\d\s%+,./-]+\)$|^[a-z]+$/i,se=/^[\w "',.-]+$/,de=new Set(["compactMode","padding","verticalSeparation","horizontalSeparation","arcRadius","fontSize","fontFamily","terminalFill","terminalStroke","terminalTextColor","nonTerminalFill","nonTerminalStroke","nonTerminalTextColor","lineColor","strokeWidth","markerFill","commentFill","commentStroke","commentTextColor","specialFill","specialStroke","ruleNameColor","showMarkers","markerRadius"]),L=p(e=>e?Object.keys(e).every(i=>i==="railroad"||de.has(i)):!1,"isRailroadStyleOptions"),ce=p(e=>e?"railroad"in e&&e.railroad?e.railroad:L(e)?e:{}:{},"extractRailroadOverrides"),me=p(e=>{if(!e||L(e))return{};const{railroad:i,svgId:a,theme:n,look:t,...r}=e;return r},"extractThemeOverrides"),m=p((e,i)=>{if(typeof e!="string")return i;const a=e.trim();return le.test(a)?a:i},"sanitizeColorValue"),I=p((e,i)=>{if(typeof e!="string")return i;const a=e.trim();return se.test(a)?a:i},"sanitizeFontFamilyValue"),F=p((e,i)=>{const a=typeof e=="number"?e:typeof e=="string"?Number.parseFloat(e):Number.NaN;return Number.isFinite(a)&&a>=0?a:i},"sanitizeNumberValue"),he=p(e=>{const i=typeof e=="number"?e:typeof e=="string"?Number.parseFloat(e):Number.NaN;return Number.isFinite(i)&&i>0?i:void 0},"parseThemeFontSize"),pe=p(e=>{const i=I(e.fontFamily,f.fontFamily),a=he(e.fontSize)??f.fontSize;return{...f,fontFamily:i,fontSize:a,terminalFill:m(e.secondBkg??e.secondaryColor,f.terminalFill),terminalStroke:m(e.secondaryBorderColor??e.lineColor,f.terminalStroke),terminalTextColor:m(e.secondaryTextColor??e.textColor,f.terminalTextColor),nonTerminalFill:m(e.mainBkg??e.background,f.nonTerminalFill),nonTerminalStroke:m(e.primaryBorderColor??e.lineColor,f.nonTerminalStroke),nonTerminalTextColor:m(e.primaryTextColor??e.textColor,f.nonTerminalTextColor),lineColor:m(e.lineColor,f.lineColor),markerFill:m(e.lineColor,f.markerFill),commentFill:m(e.labelBackground??e.tertiaryColor,f.commentFill),commentStroke:m(e.tertiaryBorderColor??e.lineColor,f.commentStroke),commentTextColor:m(e.tertiaryTextColor??e.textColor,f.commentTextColor),specialFill:m(e.tertiaryColor??e.secondaryColor,f.specialFill),specialStroke:m(e.tertiaryBorderColor??e.secondaryBorderColor,f.specialStroke),ruleNameColor:m(e.titleColor??e.textColor,f.ruleNameColor)}},"buildThemeDefaults"),E=p(e=>{const i=B(),a={...G(),...i.themeVariables??{},...me(e)},n=pe(a),t={...i.railroad??{},...ce(e)};return{compactMode:t.compactMode??n.compactMode,padding:F(t.padding,n.padding),verticalSeparation:F(t.verticalSeparation,n.verticalSeparation),horizontalSeparation:F(t.horizontalSeparation,n.horizontalSeparation),arcRadius:F(t.arcRadius,n.arcRadius),fontSize:F(t.fontSize,n.fontSize),fontFamily:I(t.fontFamily,n.fontFamily),terminalFill:m(t.terminalFill,n.terminalFill),terminalStroke:m(t.terminalStroke,n.terminalStroke),terminalTextColor:m(t.terminalTextColor,n.terminalTextColor),nonTerminalFill:m(t.nonTerminalFill,n.nonTerminalFill),nonTerminalStroke:m(t.nonTerminalStroke,n.nonTerminalStroke),nonTerminalTextColor:m(t.nonTerminalTextColor,n.nonTerminalTextColor),lineColor:m(t.lineColor,n.lineColor),strokeWidth:F(t.strokeWidth,n.strokeWidth),markerFill:m(t.markerFill,n.markerFill),commentFill:m(t.commentFill,n.commentFill),commentStroke:m(t.commentStroke,n.commentStroke),commentTextColor:m(t.commentTextColor,n.commentTextColor),specialFill:m(t.specialFill,n.specialFill),specialStroke:m(t.specialStroke,n.specialStroke),ruleNameColor:m(t.ruleNameColor,n.ruleNameColor),showMarkers:t.showMarkers??n.showMarkers,markerRadius:F(t.markerRadius,n.markerRadius)}},"buildRailroadStyleOptions"),Te=p(e=>{const{fontFamily:i,fontSize:a,terminalFill:n,terminalStroke:t,terminalTextColor:r,nonTerminalFill:o,nonTerminalStroke:g,nonTerminalTextColor:l,lineColor:s,strokeWidth:h,markerFill:u,commentFill:c,commentStroke:w,commentTextColor:d,specialFill:T,specialStroke:z,ruleNameColor:S}=E(e);return`
|
||||
.railroad-diagram {
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
}
|
||||
|
||||
.railroad-terminal rect {
|
||||
fill: ${n};
|
||||
stroke: ${t};
|
||||
stroke-width: ${h}px;
|
||||
}
|
||||
|
||||
.railroad-terminal text {
|
||||
fill: ${r};
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
|
||||
.railroad-nonterminal rect {
|
||||
fill: ${o};
|
||||
stroke: ${g};
|
||||
stroke-width: ${h}px;
|
||||
}
|
||||
|
||||
.railroad-nonterminal text {
|
||||
fill: ${l};
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
|
||||
.railroad-line {
|
||||
stroke: ${s};
|
||||
stroke-width: ${h}px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.railroad-start circle,
|
||||
.railroad-end circle {
|
||||
fill: ${u};
|
||||
}
|
||||
|
||||
.railroad-comment ellipse {
|
||||
fill: ${c};
|
||||
stroke: ${w};
|
||||
stroke-width: ${h}px;
|
||||
}
|
||||
|
||||
.railroad-comment text {
|
||||
fill: ${d};
|
||||
font-style: italic;
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
|
||||
.railroad-special rect {
|
||||
fill: ${T};
|
||||
stroke: ${z};
|
||||
stroke-width: ${h}px;
|
||||
stroke-dasharray: 5,3;
|
||||
}
|
||||
|
||||
.railroad-special text {
|
||||
fill: ${l};
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
|
||||
.railroad-rule-name {
|
||||
font-weight: bold;
|
||||
fill: ${S};
|
||||
font-family: ${i};
|
||||
font-size: ${a}px;
|
||||
}
|
||||
|
||||
.railroad-group {
|
||||
/* Grouping container, no specific styles */
|
||||
}
|
||||
`},"getStyles"),R,x=(R=class{constructor(){this.d=""}moveTo(i,a){return this.d+=`M ${i} ${a} `,this}lineTo(i,a){return this.d+=`L ${i} ${a} `,this}horizontalTo(i){return this.d+=`H ${i} `,this}verticalTo(i){return this.d+=`V ${i} `,this}arcTo(i,a,n,t,r,o,g){return this.d+=`A ${i} ${a} ${n} ${t?1:0} ${r?1:0} ${o} ${g} `,this}build(){return this.d.trim()}},p(R,"PathBuilder"),R),$,ue=($=class{constructor(i,a=E()){this.textCache=new Map,this.svg=i,this.config=a}measureText(i){if(this.textCache.has(i))return this.textCache.get(i);const a=this.svg.append("text").attr("font-family",this.config.fontFamily).attr("font-size",this.config.fontSize).text(i),n=a.node().getBBox(),t={width:n.width,height:n.height};return a.remove(),this.textCache.set(i,t),t}renderTerminal(i,a){const n=this.measureText(a),t=n.width+this.config.padding*2,r=n.height+this.config.padding*2,o=i.append("g").attr("class","railroad-terminal");return o.append("rect").attr("x",0).attr("y",0).attr("width",t).attr("height",r).attr("rx",10).attr("ry",10),o.append("text").attr("x",t/2).attr("y",r/2).text(a),{element:o.node(),dimensions:{width:t,height:r,up:r/2,down:r/2}}}renderNonTerminal(i,a){const n=this.measureText(a),t=n.width+this.config.padding*2,r=n.height+this.config.padding*2,o=i.append("g").attr("class","railroad-nonterminal");return o.append("rect").attr("x",0).attr("y",0).attr("width",t).attr("height",r),o.append("text").attr("x",t/2).attr("y",r/2).text(a),{element:o.node(),dimensions:{width:t,height:r,up:r/2,down:r/2}}}renderSequence(i,a){const n=a.map(s=>this.renderExpression(i,s));let t=0,r=0,o=0;for(const s of n)t+=s.dimensions.width,r=Math.max(r,s.dimensions.up),o=Math.max(o,s.dimensions.down);t+=(n.length-1)*this.config.horizontalSeparation;const g=i.append("g").attr("class","railroad-sequence");let l=0;for(let s=0;s<n.length;s++){const h=n[s],u=r-h.dimensions.up;if(g.node().appendChild(h.element).setAttribute("transform",`translate(${l}, ${u})`),s<n.length-1){const w=l+h.dimensions.width,d=w+this.config.horizontalSeparation,T=r;g.append("path").attr("class","railroad-line").attr("d",new x().moveTo(w,T).lineTo(d,T).build())}l+=h.dimensions.width+this.config.horizontalSeparation}return{element:g.node(),dimensions:{width:t,height:r+o,up:r,down:o}}}renderChoice(i,a){const n=a.map(c=>this.renderExpression(i,c));let t=0,r=0;for(const c of n)t=Math.max(t,c.dimensions.width),r+=c.dimensions.height;r+=(n.length-1)*this.config.verticalSeparation;const o=this.config.arcRadius,g=o*4,l=t+g,s=i.append("g").attr("class","railroad-choice");let h=0;const u=r/2;for(const c of n){const w=h,d=w+c.dimensions.up,T=o*2+(t-c.dimensions.width)/2;s.node().appendChild(c.element).setAttribute("transform",`translate(${T}, ${w})`);const S=new x,k=d>u;d===u?S.moveTo(0,u).lineTo(T,d):S.moveTo(0,u).arcTo(o,o,0,!1,k,o,u+(k?o:-o)).lineTo(o,d-(k?o:-o)).arcTo(o,o,0,!1,!k,o*2,d).lineTo(T,d),s.append("path").attr("class","railroad-line").attr("d",S.build());const N=new x,_=T+c.dimensions.width,X=l-o*2;d===u?N.moveTo(_,d).lineTo(l,u):N.moveTo(_,d).lineTo(X,d).arcTo(o,o,0,!1,!k,l-o,d+(k?-o:o)).lineTo(l-o,u+(k?o:-o)).arcTo(o,o,0,!1,k,l,u),s.append("path").attr("class","railroad-line").attr("d",N.build()),h+=c.dimensions.height+this.config.verticalSeparation}return{element:s.node(),dimensions:{width:l,height:r,up:u,down:r-u}}}renderOptional(i,a){const n=this.renderExpression(i,a),t=this.config.arcRadius,r=t*2,o=n.dimensions.width+t*4,g=n.dimensions.height+r,l=i.append("g").attr("class","railroad-optional"),s=t*2,h=r;l.node().appendChild(n.element).setAttribute("transform",`translate(${s}, ${h})`);const c=h+n.dimensions.up,w=new x().moveTo(0,c).lineTo(t*2,c);l.append("path").attr("class","railroad-line").attr("d",w.build());const d=new x().moveTo(s+n.dimensions.width,c).lineTo(o,c);l.append("path").attr("class","railroad-line").attr("d",d.build());const T=new x().moveTo(0,c).arcTo(t,t,0,!1,!1,t,c-t).lineTo(t,t).arcTo(t,t,0,!1,!0,t*2,0).lineTo(o-t*2,0).arcTo(t,t,0,!1,!0,o-t,t).lineTo(o-t,c-t).arcTo(t,t,0,!1,!1,o,c);return l.append("path").attr("class","railroad-line").attr("d",T.build()),{element:l.node(),dimensions:{width:o,height:g,up:c,down:g-c}}}renderRepetition(i,a,n){const t=this.renderExpression(i,a),r=this.config.arcRadius,o=r*2,g=t.dimensions.width+r*4,l=n===0,s=t.dimensions.height+o+(l?o:0),h=i.append("g").attr("class","railroad-repetition"),u=r*2,c=l?o:0;h.node().appendChild(t.element).setAttribute("transform",`translate(${u}, ${c})`);const d=c+t.dimensions.up;h.append("path").attr("class","railroad-line").attr("d",new x().moveTo(0,d).lineTo(r*2,d).build()),h.append("path").attr("class","railroad-line").attr("d",new x().moveTo(u+t.dimensions.width,d).lineTo(g,d).build());const T=c+t.dimensions.height+r,z=new x().moveTo(u+t.dimensions.width,d).arcTo(r,r,0,!1,!0,u+t.dimensions.width+r,d+r).lineTo(u+t.dimensions.width+r,T).arcTo(r,r,0,!1,!0,u+t.dimensions.width,T+r).lineTo(r*2,T+r).arcTo(r,r,0,!1,!0,r,T).lineTo(r,d+r).arcTo(r,r,0,!1,!0,r*2,d);if(h.append("path").attr("cl
Line truncated
|
||||
`+e);try{const n=H(i);n.attr("class","railroad-diagram");const r=B().railroad?.useMaxWidth??!0,o=oe.getRules();if(C.debug(`[Railroad] Rendering ${o.length} rules`),o.length===0){C.warn("[Railroad] No rules to render"),W(n,{height:100,width:200},r);return}const l=new ue(n,E()).renderDiagram(o);W(n,l,r),C.debug("[Railroad] Render complete")}catch(n){throw C.error("[Railroad] Render error:",n),n}},"draw"),xe={draw:ge};export{oe as d,Te as g,xe as r};
|
||||
@@ -0,0 +1,206 @@
|
||||
import{g as st}from"./chunk-5VM5RSS4.js";import{g as it}from"./chunk-XXDRQBXY.js";import{s as at}from"./chunk-POPQ4Y6H.js";import{_ as f,l as we,c as F,u as nt,v as rt,x as Ve,j as Ae,O as ut,b as lt,a as ct,s as ot,g as ht,n as dt,o as pt,k as I,p as At,q as ft,i as gt,P as U}from"./mermaid.core.js";import{f as mt}from"./chunk-F27PBJKO.js";var Pe=(function(){var s=f(function(O,o,h,p){for(h=h||{},p=O.length;p--;h[O[p]]=o);return h},"o"),i=[1,18],a=[1,19],n=[1,20],r=[1,41],c=[1,26],u=[1,42],d=[1,24],m=[1,25],g=[1,32],N=[1,33],ge=[1,34],b=[1,45],me=[1,35],Ce=[1,36],be=[1,37],ke=[1,38],Ee=[1,27],Te=[1,28],ye=[1,29],De=[1,30],Fe=[1,31],k=[1,44],E=[1,46],T=[1,43],y=[1,47],Be=[1,9],A=[1,8,9],ee=[1,58],te=[1,59],se=[1,60],ie=[1,61],ae=[1,62],_e=[1,63],Se=[1,64],_=[1,8,9,41],Me=[1,77],R=[1,8,9,12,13,22,39,41,44,46,68,69,70,71,72,73,74,79,81],ne=[1,8,9,12,13,18,20,22,39,41,44,46,47,60,68,69,70,71,72,73,74,79,81,86,100,102,103],re=[13,60,86,100,102,103],Y=[13,60,73,74,86,100,102,103],Re=[13,60,68,69,70,71,72,86,100,102,103],ue=[1,103],K=[1,121],W=[1,117],Q=[1,113],j=[1,119],X=[1,114],q=[1,115],H=[1,116],J=[1,118],Z=[1,120],Ge=[22,50,60,61,82,86,87,88,89,90],Ue=[1,128],le=[12,39],Ne=[1,8,9,39,41,44,46],ce=[1,8,9,22],ze=[1,153],Ye=[1,8,9,61],x=[1,8,9,22,50,60,61,82,86,87,88,89,90],Le={trace:f(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,statements:5,graphConfig:6,CLASS_DIAGRAM:7,NEWLINE:8,EOF:9,statement:10,classLabel:11,SQS:12,STR:13,SQE:14,namespaceName:15,alphaNumToken:16,classLiteralName:17,DOT:18,className:19,GENERICTYPE:20,relationStatement:21,LABEL:22,namespaceStatement:23,classStatement:24,memberStatement:25,annotationStatement:26,clickStatement:27,styleStatement:28,cssClassStatement:29,noteStatement:30,classDefStatement:31,direction:32,acc_title:33,acc_title_value:34,acc_descr:35,acc_descr_value:36,acc_descr_multiline_value:37,namespaceIdentifier:38,STRUCT_START:39,classStatements:40,STRUCT_STOP:41,NAMESPACE:42,classIdentifier:43,STYLE_SEPARATOR:44,members:45,ANNOTATION_START:46,ANNOTATION_END:47,CLASS:48,emptyBody:49,SPACE:50,MEMBER:51,SEPARATOR:52,relation:53,NOTE_FOR:54,noteText:55,NOTE:56,CLASSDEF:57,classList:58,stylesOpt:59,ALPHA:60,COMMA:61,direction_tb:62,direction_bt:63,direction_rl:64,direction_lr:65,relationType:66,lineType:67,AGGREGATION:68,EXTENSION:69,COMPOSITION:70,DEPENDENCY:71,LOLLIPOP:72,LINE:73,DOTTED_LINE:74,CALLBACK:75,LINK:76,LINK_TARGET:77,CLICK:78,CALLBACK_NAME:79,CALLBACK_ARGS:80,HREF:81,STYLE:82,CSSCLASS:83,style:84,styleComponent:85,NUM:86,COLON:87,UNIT:88,BRKT:89,PCT:90,commentToken:91,textToken:92,graphCodeTokens:93,textNoTagsToken:94,TAGSTART:95,TAGEND:96,"==":97,"--":98,DEFAULT:99,MINUS:100,keywords:101,UNICODE_TEXT:102,BQUOTE_STR:103,$accept:0,$end:1},terminals_:{2:"error",7:"CLASS_DIAGRAM",8:"NEWLINE",9:"EOF",12:"SQS",13:"STR",14:"SQE",18:"DOT",20:"GENERICTYPE",22:"LABEL",33:"acc_title",34:"acc_title_value",35:"acc_descr",36:"acc_descr_value",37:"acc_descr_multiline_value",39:"STRUCT_START",41:"STRUCT_STOP",42:"NAMESPACE",44:"STYLE_SEPARATOR",46:"ANNOTATION_START",47:"ANNOTATION_END",48:"CLASS",50:"SPACE",51:"MEMBER",52:"SEPARATOR",54:"NOTE_FOR",56:"NOTE",57:"CLASSDEF",60:"ALPHA",61:"COMMA",62:"direction_tb",63:"direction_bt",64:"direction_rl",65:"direction_lr",68:"AGGREGATION",69:"EXTENSION",70:"COMPOSITION",71:"DEPENDENCY",72:"LOLLIPOP",73:"LINE",74:"DOTTED_LINE",75:"CALLBACK",76:"LINK",77:"LINK_TARGET",78:"CLICK",79:"CALLBACK_NAME",80:"CALLBACK_ARGS",81:"HREF",82:"STYLE",83:"CSSCLASS",86:"NUM",87:"COLON",88:"UNIT",89:"BRKT",90:"PCT",93:"graphCodeTokens",95:"TAGSTART",96:"TAGEND",97:"==",98:"--",99:"DEFAULT",100:"MINUS",101:"keywords",102:"UNICODE_TEXT",103:"BQUOTE_STR"},productions_:[0,[3,1],[3,1],[4,1],[6,4],[5,1],[5,2],[5,3],[11,3],[15,1],[15,1],[15,3],[15,2],[19,1],[19,3],[19,1],[19,2],[19,2],[19,2],[10,1],[10,2],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,2],[10,1],[23,4],[23,5],[38,2],[38,3],[40,1],[40,2],[40,3],[40,1],[40,2],[40,3],[40,1],[40,2],[40,3],[24,1],[24,3],[24,4],[24,3],[24,6],[24,4],[24,7],[24,6],[43,2],[43,3],[49,0],[49,2],[49,2],[26,4],[45,1],[45,2],[25,1],[25,2],[25,1],[25,1],[21,3],[21,4],[21,4],[21,5],[30,3],[30,2],[31,3],[58,1],[58,3],[32,1],[32,1],[32,1],[32,1],[53,3],[53,2],[53,2],[53,1],[66,1],[66,1],[66,1],[66,1],[66,1],[67,1],[67,1],[27,3],[27,4],[27,3],[27,4],[27,4],[27,5],[27,3],[27,4],[27,4],[27,5],[27,4],[27,5],[27,5],[27,6],[28,3],[29,3],[59,1],[59,3],[84,1],[84,2],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[85,1],[91,1],[91,1],[92,1],[92,1],[92,1],[92,1],[92,1],[92,1],[92,1],[94,1],[94,1],[94,1],[94,1],[16,1],[16,1],[16,1],[16,1],[17,1],[55,1]],performAction:f(function(o,h,p,l,C,e,$){var t=e.length-1;switch(C){case 8:this.$=e[t-1];break;case 9:case 10:case 13:case 15:this.$=e[t];break;case 11:case 14:this.$=e[t-2]+"."+e[t];break;case 12:case 16:this.$=e[t-1]+e[t];break;case 17:case 18:this.$=e[t-1]+"~"+e[t]+"~";break;case 19:l.addRelation(e[t]);break;case 20:e[t-1].titlLine truncated
|
||||
`+D.showPosition()+`
|
||||
Expecting `+pe.join(", ")+", got '"+(this.terminals_[B]||B)+"'":Oe="Parse error on line "+(he+1)+": Unexpected "+(B==We?"end of input":"'"+(this.terminals_[B]||B)+"'"),this.parseError(Oe,{text:D.match,token:this.terminals_[B]||B,line:D.yylineno,loc:ve,expected:pe})}if(L[0]instanceof Array&&L.length>1)throw new Error("Parse Error: multiple actions possible at state: "+P+", token: "+B);switch(L[0]){case 1:p.push(B),C.push(D.yytext),e.push(D.yylloc),p.push(L[1]),B=null,Ke=D.yyleng,t=D.yytext,he=D.yylineno,ve=D.yylloc;break;case 2:if(v=this.productions_[L[1]][1],G.$=C[C.length-v],G._$={first_line:e[e.length-(v||1)].first_line,last_line:e[e.length-1].last_line,first_column:e[e.length-(v||1)].first_column,last_column:e[e.length-1].last_column},et&&(G._$.range=[e[e.length-(v||1)].range[0],e[e.length-1].range[1]]),Ie=this.performAction.apply(G,[t,Ke,he,V.yy,L[1],C,e].concat($e)),typeof Ie<"u")return Ie;v&&(p=p.slice(0,-1*v*2),C=C.slice(0,-1*v),e=e.slice(0,-1*v)),p.push(this.productions_[L[1]][0]),C.push(G.$),e.push(G._$),je=$[p[p.length-2]][p[p.length-1]],p.push(je);break;case 3:return!0}}return!0},"parse")},Je=(function(){var O={EOF:1,parseError:f(function(h,p){if(this.yy.parser)this.yy.parser.parseError(h,p);else throw new Error(h)},"parseError"),setInput:f(function(o,h){return this.yy=h||this.yy||{},this._input=o,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:f(function(){var o=this._input[0];this.yytext+=o,this.yyleng++,this.offset++,this.match+=o,this.matched+=o;var h=o.match(/(?:\r\n?|\n).*/g);return h?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),o},"input"),unput:f(function(o){var h=o.length,p=o.split(/(?:\r\n?|\n)/g);this._input=o+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-h),this.offset-=h;var l=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),p.length-1&&(this.yylineno-=p.length-1);var C=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:p?(p.length===l.length?this.yylloc.first_column:0)+l[l.length-p.length].length-p[0].length:this.yylloc.first_column-h},this.options.ranges&&(this.yylloc.range=[C[0],C[0]+this.yyleng-h]),this.yyleng=this.yytext.length,this},"unput"),more:f(function(){return this._more=!0,this},"more"),reject:f(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:f(function(o){this.unput(this.match.slice(o))},"less"),pastInput:f(function(){var o=this.matched.substr(0,this.matched.length-this.match.length);return(o.length>20?"...":"")+o.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:f(function(){var o=this.match;return o.length<20&&(o+=this._input.substr(0,20-o.length)),(o.substr(0,20)+(o.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:f(function(){var o=this.pastInput(),h=new Array(o.length+1).join("-");return o+this.upcomingInput()+`
|
||||
`+h+"^"},"showPosition"),test_match:f(function(o,h){var p,l,C;if(this.options.backtrack_lexer&&(C={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(C.yylloc.range=this.yylloc.range.slice(0))),l=o[0].match(/(?:\r\n?|\n).*/g),l&&(this.yylineno+=l.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:l?l[l.length-1].length-l[l.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+o[0].length},this.yytext+=o[0],this.match+=o[0],this.matches=o,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(o[0].length),this.matched+=o[0],p=this.performAction.call(this,this.yy,this,h,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),p)return p;if(this._backtrack){for(var e in C)this[e]=C[e];return!1}return!1},"test_match"),next:f(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var o,h,p,l;this._more||(this.yytext="",this.match="");for(var C=this._currentRules(),e=0;e<C.length;e++)if(p=this._input.match(this.rules[C[e]]),p&&(!h||p[0].length>h[0].length)){if(h=p,l=e,this.options.backtrack_lexer){if(o=this.test_match(p,C[e]),o!==!1)return o;if(this._backtrack){h=!1;continue}else return!1}else if(!this.options.flex)break}return h?(o=this.test_match(h,C[l]),o!==!1?o:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
|
||||
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:f(function(){var h=this.next();return h||this.lex()},"lex"),begin:f(function(h){this.conditionStack.push(h)},"begin"),popState:f(function(){var h=this.conditionStack.length-1;return h>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:f(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:f(function(h){return h=this.conditionStack.length-1-Math.abs(h||0),h>=0?this.conditionStack[h]:"INITIAL"},"topState"),pushState:f(function(h){this.begin(h)},"pushState"),stateStackSize:f(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:f(function(h,p,l,C){switch(l){case 0:return 62;case 1:return 63;case 2:return 64;case 3:return 65;case 4:break;case 5:break;case 6:return this.begin("acc_title"),33;case 7:return this.popState(),"acc_title_value";case 8:return this.begin("acc_descr"),35;case 9:return this.popState(),"acc_descr_value";case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:return 8;case 14:break;case 15:return 7;case 16:return 7;case 17:return"EDGE_STATE";case 18:this.begin("callback_name");break;case 19:this.popState();break;case 20:this.popState(),this.begin("callback_args");break;case 21:return 79;case 22:this.popState();break;case 23:return 80;case 24:this.popState();break;case 25:return"STR";case 26:this.begin("string");break;case 27:return 82;case 28:return 57;case 29:return this.begin("namespace"),42;case 30:return this.popState(),8;case 31:break;case 32:return this.begin("namespace-body"),39;case 33:this.popState(),this.less(0);break;case 34:return this.popState(),41;case 35:return"EOF_IN_STRUCT";case 36:return 8;case 37:break;case 38:return"EDGE_STATE";case 39:return this.begin("class"),48;case 40:return this.popState(),8;case 41:break;case 42:return this.popState(),this.popState(),41;case 43:return this.begin("class-body"),39;case 44:return this.popState(),41;case 45:return"EOF_IN_STRUCT";case 46:return"EDGE_STATE";case 47:return"OPEN_IN_STRUCT";case 48:break;case 49:return"MEMBER";case 50:return 83;case 51:return 75;case 52:return 76;case 53:return 78;case 54:return 54;case 55:return 56;case 56:return 46;case 57:return 47;case 58:return 81;case 59:this.popState();break;case 60:return"GENERICTYPE";case 61:this.begin("generic");break;case 62:this.popState();break;case 63:return"BQUOTE_STR";case 64:this.begin("bqstring");break;case 65:return 77;case 66:return 77;case 67:return 77;case 68:return 77;case 69:return 69;case 70:return 69;case 71:return 71;case 72:return 71;case 73:return 70;case 74:return 68;case 75:return 72;case 76:return 73;case 77:return 74;case 78:return 22;case 79:return 44;case 80:return 100;case 81:return 18;case 82:return"PLUS";case 83:return 87;case 84:return 61;case 85:return 89;case 86:return 89;case 87:return 90;case 88:return"EQUALS";case 89:return"EQUALS";case 90:return 60;case 91:return 12;case 92:return 14;case 93:return"PUNCTUATION";case 94:return 86;case 95:return 102;case 96:return 50;case 97:return 50;case 98:return 9}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:\[\*\])/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:["])/,/^(?:[^"]*)/,/^(?:["])/,/^(?:style\b)/,/^(?:classDef\b)/,/^(?:namespace\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[{])/,/^(?:[}])/,/^(?:[}])/,/^(?:$)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:\[\*\])/,/^(?:class\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[}])/,/^(?:[{])/,/^(?:[}])/,/^(?:$)/,/^(?:\[\*\])/,/^(?:[{])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:note for\b)/,/^(?:note\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:href\b)/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:~)/,/^(?:[`])/,/^(?:[^`]+)/,/^(?:[`])/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:\s*\(\))/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?::)/,/^(?:,)/,/^(?:#)/,/^(?:#)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:\[)/,/^(?:\])/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\uLine truncated
|
||||
fill: ${s.nodeBorder||s.classText};
|
||||
stroke: none;
|
||||
font-family: ${s.fontFamily};
|
||||
font-size: 10px;
|
||||
|
||||
.title {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.cluster-label text {
|
||||
fill: ${s.titleColor};
|
||||
}
|
||||
.cluster-label span {
|
||||
color: ${s.titleColor};
|
||||
}
|
||||
.cluster-label span p {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: ${s.clusterBkg};
|
||||
stroke: ${s.clusterBorder};
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.cluster text {
|
||||
fill: ${s.titleColor};
|
||||
}
|
||||
|
||||
.cluster span {
|
||||
color: ${s.titleColor};
|
||||
}
|
||||
|
||||
.nodeLabel, .edgeLabel {
|
||||
color: ${s.classText};
|
||||
}
|
||||
|
||||
.noteLabel .nodeLabel, .noteLabel .edgeLabel {
|
||||
color: ${s.noteTextColor};
|
||||
}
|
||||
.edgeLabel .label rect {
|
||||
fill: ${s.mainBkg};
|
||||
}
|
||||
.label text {
|
||||
fill: ${s.classText};
|
||||
}
|
||||
|
||||
.labelBkg {
|
||||
background: ${s.mainBkg};
|
||||
}
|
||||
.edgeLabel .label span {
|
||||
background: ${s.mainBkg};
|
||||
}
|
||||
|
||||
.classTitle {
|
||||
font-weight: bolder;
|
||||
}
|
||||
.node rect,
|
||||
.node circle,
|
||||
.node ellipse,
|
||||
.node polygon,
|
||||
.node path {
|
||||
fill: ${s.mainBkg};
|
||||
stroke: ${s.nodeBorder};
|
||||
stroke-width: ${s.strokeWidth};
|
||||
}
|
||||
|
||||
|
||||
.divider {
|
||||
stroke: ${s.nodeBorder};
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
g.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
g.classGroup rect {
|
||||
fill: ${s.mainBkg};
|
||||
stroke: ${s.nodeBorder};
|
||||
}
|
||||
|
||||
g.classGroup line {
|
||||
stroke: ${s.nodeBorder};
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.classLabel .box {
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
fill: ${s.mainBkg};
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.classLabel .label {
|
||||
fill: ${s.nodeBorder};
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.relation {
|
||||
stroke: ${s.lineColor};
|
||||
stroke-width: ${s.strokeWidth};
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.dashed-line{
|
||||
stroke-dasharray: 3;
|
||||
}
|
||||
|
||||
.dotted-line{
|
||||
stroke-dasharray: 1 2;
|
||||
}
|
||||
|
||||
[id$="-compositionStart"], .composition {
|
||||
fill: ${s.lineColor} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-compositionEnd"], .composition {
|
||||
fill: ${s.lineColor} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-dependencyStart"], .dependency {
|
||||
fill: ${s.lineColor} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-dependencyEnd"], .dependency {
|
||||
fill: ${s.lineColor} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-extensionStart"], .extension {
|
||||
fill: transparent !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-extensionEnd"], .extension {
|
||||
fill: transparent !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-aggregationStart"], .aggregation {
|
||||
fill: transparent !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-aggregationEnd"], .aggregation {
|
||||
fill: transparent !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-lollipopStart"], .lollipop {
|
||||
fill: ${s.mainBkg} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
[id$="-lollipopEnd"], .lollipop {
|
||||
fill: ${s.mainBkg} !important;
|
||||
stroke: ${s.lineColor} !important;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.edgeTerminals {
|
||||
font-size: 11px;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.classTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${s.textColor};
|
||||
}
|
||||
|
||||
.edgeLabel[data-look="neo"] {
|
||||
background-color: ${s.edgeLabelBackground};
|
||||
p {
|
||||
background-color: ${s.edgeLabelBackground};
|
||||
}
|
||||
rect {
|
||||
opacity: 0.5;
|
||||
background-color: ${s.edgeLabelBackground};
|
||||
fill: ${s.edgeLabelBackground};
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
${st()}
|
||||
`,"getStyles"),Nt=Ct,bt=f((s,i="TB")=>{if(!s.doc)return i;let a=i;for(const n of s.doc)n.stmt==="dir"&&(a=n.value);return a},"getDir"),kt=f(function(s,i){return i.db.getClasses()},"getClasses"),Et=f(async function(s,i,a,n){we.info("REF0:"),we.info("Drawing class diagram (v3)",i);const{securityLevel:r,state:c,layout:u}=F();n.db.setDiagramId(i);const d=n.db.getData(),m=it(i,r);d.type=n.type,d.layoutAlgorithm=nt(u),d.nodeSpacing=c?.nodeSpacing||50,d.rankSpacing=c?.rankSpacing||50,d.markers=["aggregation","extension","composition","dependency","lollipop"],d.diagramId=i,await rt(d,m);const g=8;Ve.insertTitle(m,"classDiagramTitleText",c?.titleTopMargin??25,n.db.getDiagramTitle()),at(m,g,"classDiagram",c?.useMaxWidth??!0)},"draw"),Lt={getClasses:kt,draw:Et,getDir:bt};export{St as C,_t as a,Lt as c,Nt as s};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as a,j as o}from"./mermaid.core.js";var g=a((t,e)=>{let n;return e==="sandbox"&&(n=o("#i"+t)),(e==="sandbox"?o(n.nodes()[0].contentDocument.body):o("body")).select(`[id="${t}"]`)},"getDiagramElement");export{g};
|
||||
@@ -0,0 +1 @@
|
||||
import{s as a,c as s,a as e,C as t}from"./chunk-TICWLB2K.js";import{_ as i}from"./mermaid.core.js";import"./chunk-5VM5RSS4.js";import"./chunk-XXDRQBXY.js";import"./chunk-POPQ4Y6H.js";import"./chunk-F27PBJKO.js";import"./index.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
||||
@@ -0,0 +1 @@
|
||||
import{s as a,c as s,a as e,C as t}from"./chunk-TICWLB2K.js";import{_ as i}from"./mermaid.core.js";import"./chunk-5VM5RSS4.js";import"./chunk-XXDRQBXY.js";import"./chunk-POPQ4Y6H.js";import"./chunk-F27PBJKO.js";import"./index.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
||||
@@ -1 +1 @@
|
||||
var b;(function(s){s.assertEqual=n=>{};function e(n){}s.assertIs=e;function t(n){throw new Error}s.assertNever=t,s.arrayToEnum=n=>{const i={};for(const a of n)i[a]=a;return i},s.getValidEnumValues=n=>{const i=s.objectKeys(n).filter(o=>typeof n[n[o]]!="number"),a={};for(const o of i)a[o]=n[o];return s.objectValues(a)},s.objectValues=n=>s.objectKeys(n).map(function(i){return n[i]}),s.objectKeys=typeof Object.keys=="function"?n=>Object.keys(n):n=>{const i=[];for(const a in n)Object.prototype.hasOwnProperty.call(n,a)&&i.push(a);return i},s.find=(n,i)=>{for(const a of n)if(i(a))return a},s.isInteger=typeof Number.isInteger=="function"?n=>Number.isInteger(n):n=>typeof n=="number"&&Number.isFinite(n)&&Math.floor(n)===n;function r(n,i=" | "){return n.map(a=>typeof a=="string"?`'${a}'`:a).join(i)}s.joinValues=r,s.jsonStringifyReplacer=(n,i)=>typeof i=="bigint"?i.toString():i})(b||(b={}));var Ie;(function(s){s.mergeShapes=(e,t)=>({...e,...t})})(Ie||(Ie={}));const f=b.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),I=s=>{switch(typeof s){case"undefined":return f.undefined;case"string":return f.string;case"number":return Number.isNaN(s)?f.nan:f.number;case"boolean":return f.boolean;case"function":return f.function;case"bigint":return f.bigint;case"symbol":return f.symbol;case"object":return Array.isArray(s)?f.array:s===null?f.null:s.then&&typeof s.then=="function"&&s.catch&&typeof s.catch=="function"?f.promise:typeof Map<"u"&&s instanceof Map?f.map:typeof Set<"u"&&s instanceof Set?f.set:typeof Date<"u"&&s instanceof Date?f.date:f.object;default:return f.unknown}},c=b.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);class T extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}format(e){const t=e||function(i){return i.message},r={_errors:[]},n=i=>{for(const a of i.issues)if(a.code==="invalid_union")a.unionErrors.map(n);else if(a.code==="invalid_return_type")n(a.returnTypeError);else if(a.code==="invalid_arguments")n(a.argumentsError);else if(a.path.length===0)r._errors.push(t(a));else{let o=r,l=0;for(;l<a.path.length;){const d=a.path[l];l===a.path.length-1?(o[d]=o[d]||{_errors:[]},o[d]._errors.push(t(a))):o[d]=o[d]||{_errors:[]},o=o[d],l++}}};return n(this),r}static assert(e){if(!(e instanceof T))throw new Error(`Not a ZodError: ${e}`)}toString(){return this.message}get message(){return JSON.stringify(this.issues,b.jsonStringifyReplacer,2)}get isEmpty(){return this.issues.length===0}flatten(e=t=>t.message){const t={},r=[];for(const n of this.issues)if(n.path.length>0){const i=n.path[0];t[i]=t[i]||[],t[i].push(e(n))}else r.push(e(n));return{formErrors:r,fieldErrors:t}}get formErrors(){return this.flatten()}}T.create=s=>new T(s);const _e=(s,e)=>{let t;switch(s.code){case c.invalid_type:s.received===f.undefined?t="Required":t=`Expected ${s.expected}, received ${s.received}`;break;case c.invalid_literal:t=`Invalid literal value, expected ${JSON.stringify(s.expected,b.jsonStringifyReplacer)}`;break;case c.unrecognized_keys:t=`Unrecognized key(s) in object: ${b.joinValues(s.keys,", ")}`;break;case c.invalid_union:t="Invalid input";break;case c.invalid_union_discriminator:t=`Invalid discriminator value. Expected ${b.joinValues(s.options)}`;break;case c.invalid_enum_value:t=`Invalid enum value. Expected ${b.joinValues(s.options)}, received '${s.received}'`;break;case c.invalid_arguments:t="Invalid function arguments";break;case c.invalid_return_type:t="Invalid function return type";break;case c.invalid_date:t="Invalid date";break;case c.invalid_string:typeof s.validation=="object"?"includes"in s.validation?(t=`Invalid input: must include "${s.validation.includes}"`,typeof s.validation.position=="number"&&(t=`${t} at one or more positions greater than or equal to ${s.validation.position}`)):"startsWith"in s.validation?t=`Invalid input: must start with "${s.validation.startsWith}"`:"endsWith"in s.validation?t=`Invalid input: must end with "${s.validation.endsWith}"`:b.assertNever(s.validation):s.validation!=="regex"?t=`Invalid ${s.validation}`:t="Invalid";break;case c.too_small:s.type==="array"?t=`Array must contain ${s.exact?"exactly":s.inclusive?"at least":"more than"} ${s.minimum} element(s)`:s.type==="string"?t=`String must contain ${s.exact?"exactly":s.inclusive?"at least":"over"} ${s.minimum} character(s)`:s.type==="number"?t=`Number must be ${s.exact?"exactly equal to ":s.iLine truncated
|
||||
var b;(function(s){s.assertEqual=n=>{};function e(n){}s.assertIs=e;function t(n){throw new Error}s.assertNever=t,s.arrayToEnum=n=>{const i={};for(const a of n)i[a]=a;return i},s.getValidEnumValues=n=>{const i=s.objectKeys(n).filter(o=>typeof n[n[o]]!="number"),a={};for(const o of i)a[o]=n[o];return s.objectValues(a)},s.objectValues=n=>s.objectKeys(n).map(function(i){return n[i]}),s.objectKeys=typeof Object.keys=="function"?n=>Object.keys(n):n=>{const i=[];for(const a in n)Object.prototype.hasOwnProperty.call(n,a)&&i.push(a);return i},s.find=(n,i)=>{for(const a of n)if(i(a))return a},s.isInteger=typeof Number.isInteger=="function"?n=>Number.isInteger(n):n=>typeof n=="number"&&Number.isFinite(n)&&Math.floor(n)===n;function r(n,i=" | "){return n.map(a=>typeof a=="string"?`'${a}'`:a).join(i)}s.joinValues=r,s.jsonStringifyReplacer=(n,i)=>typeof i=="bigint"?i.toString():i})(b||(b={}));var Ie;(function(s){s.mergeShapes=(e,t)=>({...e,...t})})(Ie||(Ie={}));const f=b.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),I=s=>{switch(typeof s){case"undefined":return f.undefined;case"string":return f.string;case"number":return Number.isNaN(s)?f.nan:f.number;case"boolean":return f.boolean;case"function":return f.function;case"bigint":return f.bigint;case"symbol":return f.symbol;case"object":return Array.isArray(s)?f.array:s===null?f.null:s.then&&typeof s.then=="function"&&s.catch&&typeof s.catch=="function"?f.promise:typeof Map<"u"&&s instanceof Map?f.map:typeof Set<"u"&&s instanceof Set?f.set:typeof Date<"u"&&s instanceof Date?f.date:f.object;default:return f.unknown}},c=b.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);class T extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}format(e){const t=e||function(i){return i.message},r={_errors:[]},n=i=>{for(const a of i.issues)if(a.code==="invalid_union")a.unionErrors.map(n);else if(a.code==="invalid_return_type")n(a.returnTypeError);else if(a.code==="invalid_arguments")n(a.argumentsError);else if(a.path.length===0)r._errors.push(t(a));else{let o=r,l=0;for(;l<a.path.length;){const d=a.path[l];l===a.path.length-1?(o[d]=o[d]||{_errors:[]},o[d]._errors.push(t(a))):o[d]=o[d]||{_errors:[]},o=o[d],l++}}};return n(this),r}static assert(e){if(!(e instanceof T))throw new Error(`Not a ZodError: ${e}`)}toString(){return this.message}get message(){return JSON.stringify(this.issues,b.jsonStringifyReplacer,2)}get isEmpty(){return this.issues.length===0}flatten(e=t=>t.message){const t={},r=[];for(const n of this.issues)if(n.path.length>0){const i=n.path[0];t[i]=t[i]||[],t[i].push(e(n))}else r.push(e(n));return{formErrors:r,fieldErrors:t}}get formErrors(){return this.flatten()}}T.create=s=>new T(s);const _e=(s,e)=>{let t;switch(s.code){case c.invalid_type:s.received===f.undefined?t="Required":t=`Expected ${s.expected}, received ${s.received}`;break;case c.invalid_literal:t=`Invalid literal value, expected ${JSON.stringify(s.expected,b.jsonStringifyReplacer)}`;break;case c.unrecognized_keys:t=`Unrecognized key(s) in object: ${b.joinValues(s.keys,", ")}`;break;case c.invalid_union:t="Invalid input";break;case c.invalid_union_discriminator:t=`Invalid discriminator value. Expected ${b.joinValues(s.options)}`;break;case c.invalid_enum_value:t=`Invalid enum value. Expected ${b.joinValues(s.options)}, received '${s.received}'`;break;case c.invalid_arguments:t="Invalid function arguments";break;case c.invalid_return_type:t="Invalid function return type";break;case c.invalid_date:t="Invalid date";break;case c.invalid_string:typeof s.validation=="object"?"includes"in s.validation?(t=`Invalid input: must include "${s.validation.includes}"`,typeof s.validation.position=="number"&&(t=`${t} at one or more positions greater than or equal to ${s.validation.position}`)):"startsWith"in s.validation?t=`Invalid input: must start with "${s.validation.startsWith}"`:"endsWith"in s.validation?t=`Invalid input: must end with "${s.validation.endsWith}"`:b.assertNever(s.validation):s.validation!=="regex"?t=`Invalid ${s.validation}`:t="Invalid";break;case c.too_small:s.type==="array"?t=`Array must contain ${s.exact?"exactly":s.inclusive?"at least":"more than"} ${s.minimum} element(s)`:s.type==="string"?t=`String must contain ${s.exact?"exactly":s.inclusive?"at least":"over"} ${s.minimum} character(s)`:s.type==="number"?t=`Number must be ${s.exact?"exactly equal to ":s.iLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as V,l as $,j as lt}from"./mermaid.core.js";import{c as tt}from"./cytoscape.esm.js";import{g as gt}from"./index.js";var k={exports:{}},Z={exports:{}},Q={exports:{}},ut=Q.exports,q;function ft(){return q||(q=1,(function(G,b){(function(I,L){G.exports=L()})(ut,function(){return(function(N){var I={};function L(o){if(I[o])return I[o].exports;var e=I[o]={i:o,l:!1,exports:{}};return N[o].call(e.exports,e,e.exports,L),e.l=!0,e.exports}return L.m=N,L.c=I,L.i=function(o){return o},L.d=function(o,e,t){L.o(o,e)||Object.defineProperty(o,e,{configurable:!1,enumerable:!0,get:t})},L.n=function(o){var e=o&&o.__esModule?function(){return o.default}:function(){return o};return L.d(e,"a",e),e},L.o=function(o,e){return Object.prototype.hasOwnProperty.call(o,e)},L.p="",L(L.s=26)})([(function(N,I,L){function o(){}o.QUALITY=1,o.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,o.DEFAULT_INCREMENTAL=!1,o.DEFAULT_ANIMATION_ON_LAYOUT=!0,o.DEFAULT_ANIMATION_DURING_LAYOUT=!1,o.DEFAULT_ANIMATION_PERIOD=50,o.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,o.DEFAULT_GRAPH_MARGIN=15,o.NODE_DIMENSIONS_INCLUDE_LABELS=!1,o.SIMPLE_NODE_SIZE=40,o.SIMPLE_NODE_HALF_SIZE=o.SIMPLE_NODE_SIZE/2,o.EMPTY_COMPOUND_NODE_SIZE=40,o.MIN_EDGE_LENGTH=1,o.WORLD_BOUNDARY=1e6,o.INITIAL_WORLD_BOUNDARY=o.WORLD_BOUNDARY/1e3,o.WORLD_CENTER_X=1200,o.WORLD_CENTER_Y=900,N.exports=o}),(function(N,I,L){var o=L(2),e=L(8),t=L(9);function i(g,n,d){o.call(this,d),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=d,this.bendpoints=[],this.source=g,this.target=n}i.prototype=Object.create(o.prototype);for(var l in o)i[l]=o[l];i.prototype.getSource=function(){return this.source},i.prototype.getTarget=function(){return this.target},i.prototype.isInterGraph=function(){return this.isInterGraph},i.prototype.getLength=function(){return this.length},i.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},i.prototype.getBendpoints=function(){return this.bendpoints},i.prototype.getLca=function(){return this.lca},i.prototype.getSourceInLca=function(){return this.sourceInLca},i.prototype.getTargetInLca=function(){return this.targetInLca},i.prototype.getOtherEnd=function(g){if(this.source===g)return this.target;if(this.target===g)return this.source;throw"Node is not incident with this edge"},i.prototype.getOtherEndInGraph=function(g,n){for(var d=this.getOtherEnd(g),r=n.getGraphManager().getRoot();;){if(d.getOwner()==n)return d;if(d.getOwner()==r)break;d=d.getOwner().getParent()}return null},i.prototype.updateLength=function(){var g=new Array(4);this.isOverlapingSourceAndTarget=e.getIntersection(this.target.getRect(),this.source.getRect(),g),this.isOverlapingSourceAndTarget||(this.lengthX=g[0]-g[2],this.lengthY=g[1]-g[3],Math.abs(this.lengthX)<1&&(this.lengthX=t.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=t.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},i.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=t.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=t.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},N.exports=i}),(function(N,I,L){function o(e){this.vGraphObject=e}N.exports=o}),(function(N,I,L){var o=L(2),e=L(10),t=L(13),i=L(0),l=L(16),g=L(4);function n(r,h,a,p){a==null&&p==null&&(p=h),o.call(this,p),r.graphManager!=null&&(r=r.graphManager),this.estimatedSize=e.MIN_VALUE,this.inclusionTreeDepth=e.MAX_VALUE,this.vGraphObject=p,this.edges=[],this.graphManager=r,a!=null&&h!=null?this.rect=new t(h.x,h.y,a.width,a.height):this.rect=new t}n.prototype=Object.create(o.prototype);for(var d in o)n[d]=o[d];n.prototype.getEdges=function(){return this.edges},n.prototype.getChild=function(){return this.child},n.prototype.getOwner=function(){return this.owner},n.prototype.getWidth=function(){return this.rect.width},n.prototype.setWidth=function(r){this.rect.width=r},n.prototype.getHeight=function(){return this.rect.height},n.prototype.setHeight=function(r){this.rect.height=r},n.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},n.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},n.prototype.getCenter=function(){return new g(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},n.prototype.getLocation=function(){return new g(this.rect.x,this.rect.y)},n.prototype.getRect=function(){return this.rect},n.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},n.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.rect.width*this.rect.width)/2},n.prototype.setRect=function(r,h){this.rect.x=r.x,this.rect.y=r.y,this.rect.width=h.width,this.rect.height=h.height},n.prototype.setCenter=function(r,h){this.rect.x=r-this.rect.width/2,this.rect.y=h-this.rect.height/2},nLine truncated
|
||||
@@ -0,0 +1,166 @@
|
||||
import{_ as ut}from"./index.js";var fk=Object.create,Lu=Object.defineProperty,dk=Object.getOwnPropertyDescriptor,vh=Object.getOwnPropertyNames,pk=Object.getPrototypeOf,mk=Object.prototype.hasOwnProperty,s=(t,e)=>Lu(t,"name",{value:e,configurable:!0}),hk=(t,e)=>function(){return t&&(e=(0,t[vh(t)[0]])(t=0)),e},X=(t,e)=>function(){return e||(0,t[vh(t)[0]])((e={exports:{}}).exports,e),e.exports},en=(t,e)=>{for(var r in e)Lu(t,r,{get:e[r],enumerable:!0})},Th=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of vh(e))!mk.call(t,a)&&a!==r&&Lu(t,a,{get:()=>e[a],enumerable:!(n=dk(e,a))||n.enumerable});return t},kf=(t,e,r)=>(Th(t,e,"default"),r),$h=(t,e,r)=>(r=t!=null?fk(pk(t)):{},Th(Lu(r,"default",{value:t,enumerable:!0}),t)),Rh=t=>Th(Lu({},"__esModule",{value:!0}),t),Of={};en(Of,{AnnotatedTextEdit:()=>Ar,ChangeAnnotation:()=>pn,ChangeAnnotationIdentifier:()=>et,CodeAction:()=>Zp,CodeActionContext:()=>Jp,CodeActionKind:()=>Xp,CodeActionTriggerKind:()=>Yl,CodeDescription:()=>wp,CodeLens:()=>Qp,Color:()=>Ac,ColorInformation:()=>Ap,ColorPresentation:()=>Ep,Command:()=>dn,CompletionItem:()=>Gp,CompletionItemKind:()=>kp,CompletionItemLabelDetails:()=>Mp,CompletionItemTag:()=>Lp,CompletionList:()=>Fp,CreateFile:()=>ba,DeleteFile:()=>Sa,Diagnostic:()=>Wl,DiagnosticRelatedInformation:()=>Ec,DiagnosticSeverity:()=>_p,DiagnosticTag:()=>Sp,DocumentHighlight:()=>Kp,DocumentHighlightKind:()=>Up,DocumentLink:()=>tm,DocumentSymbol:()=>Yp,DocumentUri:()=>Tp,EOL:()=>x$,FoldingRange:()=>bp,FoldingRangeKind:()=>Cp,FormattingOptions:()=>em,Hover:()=>zp,InlayHint:()=>cm,InlayHintKind:()=>_c,InlayHintLabelPart:()=>Sc,InlineCompletionContext:()=>ym,InlineCompletionItem:()=>dm,InlineCompletionList:()=>pm,InlineCompletionTriggerKind:()=>mm,InlineValueContext:()=>um,InlineValueEvaluatableExpression:()=>lm,InlineValueText:()=>sm,InlineValueVariableLookup:()=>om,InsertReplaceEdit:()=>Dp,InsertTextFormat:()=>Op,InsertTextMode:()=>xp,Location:()=>Kl,LocationLink:()=>Rp,MarkedString:()=>Hl,MarkupContent:()=>wa,MarkupKind:()=>bc,OptionalVersionedTextDocumentIdentifier:()=>ql,ParameterInformation:()=>jp,Position:()=>oe,Range:()=>te,RenameFile:()=>_a,SelectedCompletionInfo:()=>hm,SelectionRange:()=>rm,SemanticTokenModifiers:()=>am,SemanticTokenTypes:()=>nm,SemanticTokens:()=>im,SignatureInformation:()=>Bp,StringValue:()=>fm,SymbolInformation:()=>qp,SymbolKind:()=>Wp,SymbolTag:()=>Vp,TextDocument:()=>vm,TextDocumentEdit:()=>Vl,TextDocumentIdentifier:()=>Ip,TextDocumentItem:()=>Pp,TextEdit:()=>ar,URI:()=>Rc,VersionedTextDocumentIdentifier:()=>Np,WorkspaceChange:()=>D$,WorkspaceEdit:()=>Cc,WorkspaceFolder:()=>gm,WorkspaceSymbol:()=>Hp,integer:()=>$p,uinteger:()=>Ul});var Tp,Rc,$p,Ul,oe,te,Kl,Rp,Ac,Ap,Ep,Cp,bp,Ec,_p,Sp,wp,Wl,dn,ar,pn,et,Ar,Vl,ba,_a,Sa,Cc,Pl,Bd,D$,Ip,Np,ql,Pp,bc,wa,kp,Op,Lp,Dp,xp,Mp,Gp,Fp,Hl,zp,jp,Bp,Up,Kp,Wp,Vp,qp,Hp,Yp,Xp,Yl,Jp,Zp,Qp,em,tm,rm,nm,am,im,sm,om,lm,um,_c,Sc,cm,fm,dm,pm,mm,hm,ym,gm,x$,vm,av,E,Du=hk({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-languageserver-types/lib/esm/main.js"(){var t,e,r,n;(function(a){function i(o){return typeof o=="string"}s(i,"is"),a.is=i})(Tp||(Tp={})),(function(a){function i(o){return typeof o=="string"}s(i,"is"),a.is=i})(Rc||(Rc={})),(function(a){a.MIN_VALUE=-2147483648,a.MAX_VALUE=2147483647;function i(o){return typeof o=="number"&&a.MIN_VALUE<=o&&o<=a.MAX_VALUE}s(i,"is"),a.is=i})($p||($p={})),(function(a){a.MIN_VALUE=0,a.MAX_VALUE=2147483647;function i(o){return typeof o=="number"&&a.MIN_VALUE<=o&&o<=a.MAX_VALUE}s(i,"is"),a.is=i})(Ul||(Ul={})),(function(a){function i(u,l){return u===Number.MAX_VALUE&&(u=Ul.MAX_VALUE),l===Number.MAX_VALUE&&(l=Ul.MAX_VALUE),{line:u,character:l}}s(i,"create"),a.create=i;function o(u){let l=u;return E.objectLiteral(l)&&E.uinteger(l.line)&&E.uinteger(l.character)}s(o,"is"),a.is=o})(oe||(oe={})),(function(a){function i(u,l,c,f){if(E.uinteger(u)&&E.uinteger(l)&&E.uinteger(c)&&E.uinteger(f))return{start:oe.create(u,l),end:oe.create(c,f)};if(oe.is(u)&&oe.is(l))return{start:u,end:l};throw new Error(`Range#create called with invalid arguments[${u}, ${l}, ${c}, ${f}]`)}s(i,"create"),a.create=i;function o(u){let l=u;return E.objectLiteral(l)&&oe.is(l.start)&&oe.is(l.end)}s(o,"is"),a.is=o})(te||(te={})),(function(a){function i(u,l){return{uri:u,range:l}}s(i,"create"),a.create=i;function o(u){let l=u;return E.objectLiteral(l)&&te.is(l.range)&&(E.string(l.uri)||E.undefined(l.uri))}s(o,"is"),a.is=o})(Kl||(Kl={})),(function(a){function i(u,l,c,f){return{targetUri:u,targetRange:l,targetSelectionRange:c,originSelectionRange:f}}s(i,"create"),a.create=i;function o(u){let l=u;return E.objectLiteral(l)&&te.is(l.targetRange)&&E.string(l.targetUri)&&te.is(l.targetSelectionRange)&&(te.is(l.originSelectionRange)||E.undefined(l.originSelectionRange))}s(o,"is"),a.is=o})(Rp||(Rp={})),(function(a){function i(u,l,c,f){return{red:u,green:l,blue:c,alpha:f}}s(i,"create"),a.create=i;function o(u){const l=u;return E.objectLiteral(l)&&E.numberRaLine truncated
|
||||
`,`\r
|
||||
`,"\r"],(function(a){function i(c,f,d,p){return new av(c,f,d,p)}s(i,"create"),a.create=i;function o(c){let f=c;return!!(E.defined(f)&&E.string(f.uri)&&(E.undefined(f.languageId)||E.string(f.languageId))&&E.uinteger(f.lineCount)&&E.func(f.getText)&&E.func(f.positionAt)&&E.func(f.offsetAt))}s(o,"is"),a.is=o;function u(c,f){let d=c.getText(),p=l(f,(h,T)=>{let C=h.range.start.line-T.range.start.line;return C===0?h.range.start.character-T.range.start.character:C}),y=d.length;for(let h=p.length-1;h>=0;h--){let T=p[h],C=c.offsetAt(T.range.start),v=c.offsetAt(T.range.end);if(v<=y)d=d.substring(0,C)+T.newText+d.substring(v,d.length);else throw new Error("Overlapping edit");y=C}return d}s(u,"applyEdits"),a.applyEdits=u;function l(c,f){if(c.length<=1)return c;const d=c.length/2|0,p=c.slice(0,d),y=c.slice(d);l(p,f),l(y,f);let h=0,T=0,C=0;for(;h<p.length&&T<y.length;)f(p[h],y[T])<=0?c[C++]=p[h++]:c[C++]=y[T++];for(;h<p.length;)c[C++]=p[h++];for(;T<y.length;)c[C++]=y[T++];return c}s(l,"mergeSort")})(vm||(vm={})),av=(n=class{constructor(i,o,u,l){this._uri=i,this._languageId=o,this._version=u,this._content=l,this._lineOffsets=void 0}get uri(){return this._uri}get languageId(){return this._languageId}get version(){return this._version}getText(i){if(i){let o=this.offsetAt(i.start),u=this.offsetAt(i.end);return this._content.substring(o,u)}return this._content}update(i,o){this._content=i.text,this._version=o,this._lineOffsets=void 0}getLineOffsets(){if(this._lineOffsets===void 0){let i=[],o=this._content,u=!0;for(let l=0;l<o.length;l++){u&&(i.push(l),u=!1);let c=o.charAt(l);u=c==="\r"||c===`
|
||||
`,c==="\r"&&l+1<o.length&&o.charAt(l+1)===`
|
||||
`&&l++}u&&o.length>0&&i.push(o.length),this._lineOffsets=i}return this._lineOffsets}positionAt(i){i=Math.max(Math.min(i,this._content.length),0);let o=this.getLineOffsets(),u=0,l=o.length;if(l===0)return oe.create(0,i);for(;u<l;){let f=Math.floor((u+l)/2);o[f]>i?l=f:u=f+1}let c=u-1;return oe.create(c,i-o[c])}offsetAt(i){let o=this.getLineOffsets();if(i.line>=o.length)return this._content.length;if(i.line<0)return 0;let u=o[i.line],l=i.line+1<o.length?o[i.line+1]:this._content.length;return Math.max(Math.min(u+i.character,l),u)}get lineCount(){return this.getLineOffsets().length}},s(n,"FullTextDocument"),n),(function(a){const i=Object.prototype.toString;function o(v){return typeof v<"u"}s(o,"defined"),a.defined=o;function u(v){return typeof v>"u"}s(u,"undefined"),a.undefined=u;function l(v){return v===!0||v===!1}s(l,"boolean"),a.boolean=l;function c(v){return i.call(v)==="[object String]"}s(c,"string"),a.string=c;function f(v){return i.call(v)==="[object Number]"}s(f,"number"),a.number=f;function d(v,w,b){return i.call(v)==="[object Number]"&&w<=v&&v<=b}s(d,"numberRange"),a.numberRange=d;function p(v){return i.call(v)==="[object Number]"&&-2147483648<=v&&v<=2147483647}s(p,"integer"),a.integer=p;function y(v){return i.call(v)==="[object Number]"&&0<=v&&v<=2147483647}s(y,"uinteger"),a.uinteger=y;function h(v){return i.call(v)==="[object Function]"}s(h,"func"),a.func=h;function T(v){return v!==null&&typeof v=="object"}s(T,"objectLiteral"),a.objectLiteral=T;function C(v,w){return Array.isArray(v)&&v.every(w)}s(C,"typedArray"),a.typedArray=C})(E||(E={}))}}),Wn=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/ral.js"(t){Object.defineProperty(t,"__esModule",{value:!0});var e;function r(){if(e===void 0)throw new Error("No runtime abstraction layer installed");return e}s(r,"RAL"),(function(n){function a(i){if(i===void 0)throw new Error("No runtime abstraction layer provided");e=i}s(a,"install"),n.install=a})(r||(r={})),t.default=r}}),xu=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/is.js"(t){Object.defineProperty(t,"__esModule",{value:!0}),t.stringArray=t.array=t.func=t.error=t.number=t.string=t.boolean=void 0;function e(l){return l===!0||l===!1}s(e,"boolean"),t.boolean=e;function r(l){return typeof l=="string"||l instanceof String}s(r,"string"),t.string=r;function n(l){return typeof l=="number"||l instanceof Number}s(n,"number"),t.number=n;function a(l){return l instanceof Error}s(a,"error"),t.error=a;function i(l){return typeof l=="function"}s(i,"func"),t.func=i;function o(l){return Array.isArray(l)}s(o,"array"),t.array=o;function u(l){return o(l)&&l.every(c=>r(c))}s(u,"stringArray"),t.stringArray=u}}),nl=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/events.js"(t){var i,o;Object.defineProperty(t,"__esModule",{value:!0}),t.Emitter=t.Event=void 0;var e=Wn(),r;(function(u){const l={dispose(){}};u.None=function(){return l}})(r||(t.Event=r={}));var n=(i=class{add(l,c=null,f){this._callbacks||(this._callbacks=[],this._contexts=[]),this._callbacks.push(l),this._contexts.push(c),Array.isArray(f)&&f.push({dispose:s(()=>this.remove(l,c),"dispose")})}remove(l,c=null){if(!this._callbacks)return;let f=!1;for(let d=0,p=this._callbacks.length;d<p;d++)if(this._callbacks[d]===l)if(this._contexts[d]===c){this._callbacks.splice(d,1),this._contexts.splice(d,1);return}else f=!0;if(f)throw new Error("When adding a listener with a context, you should remove it with the same context")}invoke(...l){if(!this._callbacks)return[];const c=[],f=this._callbacks.slice(0),d=this._contexts.slice(0);for(let p=0,y=f.length;p<y;p++)try{c.push(f[p].apply(d[p],l))}catch(h){(0,e.default)().console.error(h)}return c}isEmpty(){return!this._callbacks||this._callbacks.length===0}dispose(){this._callbacks=void 0,this._contexts=void 0}},s(i,"CallbackList"),i),a=(o=class{constructor(l){this._options=l}get event(){return this._event||(this._event=(l,c,f)=>{this._callbacks||(this._callbacks=new n),this._options&&this._options.onFirstListenerAdd&&this._callbacks.isEmpty()&&this._options.onFirstListenerAdd(this),this._callbacks.add(l,c);const d={dispose:s(()=>{this._callbacks&&(this._callbacks.remove(l,c),d.dispose=o._noop,this._options&&this._options.onLastListenerRemove&&this._callbacks.isEmpty()&&this._options.onLastListenerRemove(this))},"dispose")};return Array.isArray(f)&&f.push(d),d}),this._event}fire(l){this._callbacks&&this._callbacks.invoke.call(this._callbacks,l)}dispose(){this._callbacks&&(this._callbacks.dispose(),this._callbacks=void 0)}},s(o,"Emitter"),o);t.Emitter=a,a._noop=function(){}}}),Lf=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/cancellation.js"(t){var l,c;Object.defineProperty(t,"__esModule",{value:!0}),t.CancellationTokenSource=t.CancellationToken=void 0;var e=Wn(),r=xu(),n=nl(),a;(function(f){f.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:n.Even
Line truncated
|
||||
${JSON.stringify(Object.fromEntries(h))}`));return}const C=parseInt(T);if(isNaN(C)){this.fireError(new Error(`Content-Length value must be a number. Got ${T}`));return}this.nextMessageLength=C}const y=this.buffer.tryReadBody(this.nextMessageLength);if(y===void 0){this.setPartialMessageTimer();return}this.clearPartialMessageTimer(),this.nextMessageLength=-1,this.readSemaphore.lock(async()=>{const h=this.options.contentDecoder!==void 0?await this.options.contentDecoder.decode(y):y,T=await this.options.contentTypeDecoder.decode(h,this.options);this.callback(T)}).catch(h=>{this.fireError(h)})}}catch(y){this.fireError(y)}}clearPartialMessageTimer(){this.partialMessageTimer&&(this.partialMessageTimer.dispose(),this.partialMessageTimer=void 0)}setPartialMessageTimer(){this.clearPartialMessageTimer(),!(this._partialMessageTimeout<=0)&&(this.partialMessageTimer=(0,e.default)().timer.setTimeout((p,y)=>{this.partialMessageTimer=void 0,p===this.messageToken&&(this.firePartialMessage({messageToken:p,waitingTime:y}),this.setPartialMessageTimer())},this._partialMessageTimeout,this.messageToken,this._partialMessageTimeout))}},s(f,"ReadableStreamMessageReader"),f);t.ReadableStreamMessageReader=l}}),Tk=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/messageWriter.js"(t){var d,p;Object.defineProperty(t,"__esModule",{value:!0}),t.WriteableStreamMessageWriter=t.AbstractMessageWriter=t.MessageWriter=void 0;var e=Wn(),r=xu(),n=F$(),a=nl(),i="Content-Length: ",o=`\r
|
||||
`,u;(function(y){function h(T){let C=T;return C&&r.func(C.dispose)&&r.func(C.onClose)&&r.func(C.onError)&&r.func(C.write)}s(h,"is"),y.is=h})(u||(t.MessageWriter=u={}));var l=(d=class{constructor(){this.errorEmitter=new a.Emitter,this.closeEmitter=new a.Emitter}dispose(){this.errorEmitter.dispose(),this.closeEmitter.dispose()}get onError(){return this.errorEmitter.event}fireError(h,T,C){this.errorEmitter.fire([this.asError(h),T,C])}get onClose(){return this.closeEmitter.event}fireClose(){this.closeEmitter.fire(void 0)}asError(h){return h instanceof Error?h:new Error(`Writer received error. Reason: ${r.string(h.message)?h.message:"unknown"}`)}},s(d,"AbstractMessageWriter"),d);t.AbstractMessageWriter=l;var c;(function(y){function h(T){return T===void 0||typeof T=="string"?{charset:T??"utf-8",contentTypeEncoder:(0,e.default)().applicationJson.encoder}:{charset:T.charset??"utf-8",contentEncoder:T.contentEncoder,contentTypeEncoder:T.contentTypeEncoder??(0,e.default)().applicationJson.encoder}}s(h,"fromOptions"),y.fromOptions=h})(c||(c={}));var f=(p=class extends l{constructor(h,T){super(),this.writable=h,this.options=c.fromOptions(T),this.errorCount=0,this.writeSemaphore=new n.Semaphore(1),this.writable.onError(C=>this.fireError(C)),this.writable.onClose(()=>this.fireClose())}async write(h){return this.writeSemaphore.lock(async()=>this.options.contentTypeEncoder.encode(h,this.options).then(C=>this.options.contentEncoder!==void 0?this.options.contentEncoder.encode(C):C).then(C=>{const v=[];return v.push(i,C.byteLength.toString(),o),v.push(o),this.doWrite(h,v,C)},C=>{throw this.fireError(C),C}))}async doWrite(h,T,C){try{return await this.writable.write(T.join(""),"ascii"),this.writable.write(C)}catch(v){return this.handleError(v,h),Promise.reject(v)}}handleError(h,T){this.errorCount++,this.fireError(h,T,this.errorCount)}end(){this.writable.end()}},s(p,"WriteableStreamMessageWriter"),p);t.WriteableStreamMessageWriter=f}}),$k=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/messageBuffer.js"(t){var i;Object.defineProperty(t,"__esModule",{value:!0}),t.AbstractMessageBuffer=void 0;var e=13,r=10,n=`\r
|
||||
`,a=(i=class{constructor(u="utf-8"){this._encoding=u,this._chunks=[],this._totalLength=0}get encoding(){return this._encoding}append(u){const l=typeof u=="string"?this.fromString(u,this._encoding):u;this._chunks.push(l),this._totalLength+=l.byteLength}tryReadHeaders(u=!1){if(this._chunks.length===0)return;let l=0,c=0,f=0,d=0;e:for(;c<this._chunks.length;){const T=this._chunks[c];for(f=0;f<T.length;){switch(T[f]){case e:switch(l){case 0:l=1;break;case 2:l=3;break;default:l=0}break;case r:switch(l){case 1:l=2;break;case 3:l=4,f++;break e;default:l=0}break;default:l=0}f++}d+=T.byteLength,c++}if(l!==4)return;const p=this._read(d+f),y=new Map,h=this.toString(p,"ascii").split(n);if(h.length<2)return y;for(let T=0;T<h.length-2;T++){const C=h[T],v=C.indexOf(":");if(v===-1)throw new Error(`Message header must separate key and value using ':'
|
||||
${C}`);const w=C.substr(0,v),b=C.substr(v+1).trim();y.set(u?w.toLowerCase():w,b)}return y}tryReadBody(u){if(!(this._totalLength<u))return this._read(u)}get numberOfBytes(){return this._totalLength}_read(u){if(u===0)return this.emptyBuffer();if(u>this._totalLength)throw new Error("Cannot read so many bytes!");if(this._chunks[0].byteLength===u){const d=this._chunks[0];return this._chunks.shift(),this._totalLength-=u,this.asNative(d)}if(this._chunks[0].byteLength>u){const d=this._chunks[0],p=this.asNative(d,u);return this._chunks[0]=d.slice(u),this._totalLength-=u,p}const l=this.allocNative(u);let c=0,f=0;for(;u>0;){const d=this._chunks[f];if(d.byteLength>u){const p=d.slice(0,u);l.set(p,c),c+=u,this._chunks[f]=d.slice(u),this._totalLength-=u,u-=u}else l.set(d,c),c+=d.byteLength,this._chunks.shift(),this._totalLength-=d.byteLength,u-=d.byteLength}return l}},s(i,"AbstractMessageBuffer"),i);t.AbstractMessageBuffer=a}}),Rk=X({"../../node_modules/.pnpm/[email protected]/node_modules/vscode-jsonrpc/lib/common/connection.js"(t){var k,_;Object.defineProperty(t,"__esModule",{value:!0}),t.createMessageConnection=t.ConnectionOptions=t.MessageStrategy=t.CancellationStrategy=t.CancellationSenderStrategy=t.CancellationReceiverStrategy=t.RequestCancellationReceiverStrategy=t.IdCancellationReceiverStrategy=t.ConnectionStrategy=t.ConnectionError=t.ConnectionErrors=t.LogTraceNotification=t.SetTraceNotification=t.TraceFormat=t.TraceValues=t.Trace=t.NullLogger=t.ProgressType=t.ProgressToken=void 0;var e=Wn(),r=xu(),n=M$(),a=G$(),i=nl(),o=Lf(),u;(function($){$.type=new n.NotificationType("$/cancelRequest")})(u||(u={}));var l;(function($){function I(R){return typeof R=="string"||typeof R=="number"}s(I,"is"),$.is=I})(l||(t.ProgressToken=l={}));var c;(function($){$.type=new n.NotificationType("$/progress")})(c||(c={}));var f=(k=class{constructor(){}},s(k,"ProgressType"),k);t.ProgressType=f;var d;(function($){function I(R){return r.func(R)}s(I,"is"),$.is=I})(d||(d={})),t.NullLogger=Object.freeze({error:s(()=>{},"error"),warn:s(()=>{},"warn"),info:s(()=>{},"info"),log:s(()=>{},"log")});var p;(function($){$[$.Off=0]="Off",$[$.Messages=1]="Messages",$[$.Compact=2]="Compact",$[$.Verbose=3]="Verbose"})(p||(t.Trace=p={}));var y;(function($){$.Off="off",$.Messages="messages",$.Compact="compact",$.Verbose="verbose"})(y||(t.TraceValues=y={})),(function($){function I(A){if(!r.string(A))return $.Off;switch(A=A.toLowerCase(),A){case"off":return $.Off;case"messages":return $.Messages;case"compact":return $.Compact;case"verbose":return $.Verbose;default:return $.Off}}s(I,"fromString"),$.fromString=I;function R(A){switch(A){case $.Off:return"off";case $.Messages:return"messages";case $.Compact:return"compact";case $.Verbose:return"verbose";default:return"off"}}s(R,"toString"),$.toString=R})(p||(t.Trace=p={}));var h;(function($){$.Text="text",$.JSON="json"})(h||(t.TraceFormat=h={})),(function($){function I(R){return r.string(R)?(R=R.toLowerCase(),R==="json"?$.JSON:$.Text):$.Text}s(I,"fromString"),$.fromString=I})(h||(t.TraceFormat=h={}));var T;(function($){$.type=new n.NotificationType("$/setTrace")})(T||(t.SetTraceNotification=T={}));var C;(function($){$.type=new n.NotificationType("$/logTrace")})(C||(t.LogTraceNotification=C={}));var v;(function($){$[$.Closed=1]="Closed",$[$.Disposed=2]="Disposed",$[$.AlreadyListening=3]="AlreadyListening"})(v||(t.ConnectionErrors=v={}));var w=(_=class extends Error{constructor(I,R){super(R),this.code=I,Object.setPrototypeOf(this,_.prototype)}},s(_,"ConnectionError"),_);t.ConnectionError=w;var b;(function($){function I(R){const A=R;return A&&r.func(A.cancelUndispatched)}s(I,"is"),$.is=I})(b||(t.ConnectionStrategy=b={}));var N;(function($){function I(R){const A=R;return A&&(A.kind===void 0||A.kind==="id")&&r.func(A.createCancellationTokenSource)&&(A.dispose===void 0||r.func(A.dispose))}s(I,"is"),$.is=I})(N||(t.IdCancellationReceiverStrategy=N={}));var B;(function($){function I(R){const A=R;return A&&A.kind==="request"&&r.func(A.createCancellationTokenSource)&&(A.dispose===void 0||r.func(A.dispose))}s(I,"is"),$.is=I})(B||(t.RequestCancellationReceiverStrategy=B={}));var ne;(function($){$.Message=Object.freeze({createCancellationTokenSource(R){return new o.CancellationTokenSource}});function I(R){return N.is(R)||B.is(R)}s(I,"is"),$.is=I})(ne||(t.CancellationReceiverStrategy=ne={}));var J;(function($){$.Message=Object.freeze({sendCancellation(R,A){return R.sendNotification(u.type,{id:A})},cleanup(R){}});function I(R){const A=R;return A&&r.func(A.sendCancellation)&&r.func(A.cleanup)}s(I,"is"),$.is=I})(J||(t.CancellationSenderStrategy=J={}));var he;(function($){$.Message=Object.freeze({receiver:ne.Message,sender:J.Message});function I(R){const A=R;return A&&ne.is(A.receiver)&&J.is(A.sender)}s(I,"is"),$.is=I})(he||(t.CancellationStrategy=he={}));var Ae;(function($){function I(R){const A=R;return A&&r.func(A.handleMessage)}s(I,"is"),$.is=I})(Ae||(t.MessageStrategy=Ae={}));var ye;(function($){function I(R){const A=R;ret
Line truncated
|
||||
${JSON.stringify(g.error,void 0,4)}`):S.error("Received response message without id. No further error information provided.");else{const P=g.id,D=Ee.get(P);if(wl(g,D),D!==void 0){Ee.delete(P);try{if(g.error){const F=g.error;D.reject(new n.ResponseError(F.code,F.message,F.data))}else if(g.result!==void 0)D.resolve(g.result);else throw new Error("Should never happen.")}catch(F){F.message?S.error(`Response handler '${D.method}' failed with message: ${F.message}`):S.error(`Response handler '${D.method}' failed unexpectedly.`)}}}}s(Al,"handleResponse");function El(g){if(er())return;let P,D;if(g.method===u.type.method){const F=g.params.id;Ve.delete(F),sn(g);return}else{const F=Z.get(g.method);F&&(D=F.handler,P=F.type)}if(D||q)try{if(sn(g),D)if(g.params===void 0)P!==void 0&&P.numberOfParams!==0&&P.parameterStructures!==n.ParameterStructures.byName&&S.error(`Notification ${g.method} defines ${P.numberOfParams} params but received none.`),D();else if(Array.isArray(g.params)){const F=g.params;g.method===c.type.method&&F.length===2&&l.is(F[0])?D({token:F[0],value:F[1]}):(P!==void 0&&(P.parameterStructures===n.ParameterStructures.byName&&S.error(`Notification ${g.method} defines parameters by name but received parameters by position`),P.numberOfParams!==g.params.length&&S.error(`Notification ${g.method} defines ${P.numberOfParams} params but received ${F.length} arguments`)),D(...F))}else P!==void 0&&P.parameterStructures===n.ParameterStructures.byPosition&&S.error(`Notification ${g.method} defines parameters by position but received parameters by name`),D(g.params);else q&&q(g.method,g.params)}catch(F){F.message?S.error(`Notification handler '${g.method}' failed with message: ${F.message}`):S.error(`Notification handler '${g.method}' failed unexpectedly.`)}else je.fire(g)}s(El,"handleNotification");function Cl(g){if(!g){S.error("Received empty message.");return}S.error(`Received message which is neither a response nor a notification message:
|
||||
${JSON.stringify(g,null,4)}`);const P=g;if(r.string(P.id)||r.number(P.id)){const D=P.id,F=Ee.get(D);F&&F.reject(new Error("The received response has neither a result nor an error property."))}}s(Cl,"handleInvalidMessage");function xt(g){if(g!=null)switch(ee){case p.Verbose:return JSON.stringify(g,null,4);case p.Compact:return JSON.stringify(g);default:return}}s(xt,"stringifyTrace");function bl(g){if(!(ee===p.Off||!ce))if(Qe===h.Text){let P;(ee===p.Verbose||ee===p.Compact)&&g.params&&(P=`Params: ${xt(g.params)}
|
||||
|
||||
`),ce.log(`Sending request '${g.method} - (${g.id})'.`,P)}else tr("send-request",g)}s(bl,"traceSendingRequest");function _l(g){if(!(ee===p.Off||!ce))if(Qe===h.Text){let P;(ee===p.Verbose||ee===p.Compact)&&(g.params?P=`Params: ${xt(g.params)}
|
||||
|
||||
`:P=`No parameters provided.
|
||||
|
||||
`),ce.log(`Sending notification '${g.method}'.`,P)}else tr("send-notification",g)}s(_l,"traceSendingNotification");function jr(g,P,D){if(!(ee===p.Off||!ce))if(Qe===h.Text){let F;(ee===p.Verbose||ee===p.Compact)&&(g.error&&g.error.data?F=`Error data: ${xt(g.error.data)}
|
||||
|
||||
`:g.result?F=`Result: ${xt(g.result)}
|
||||
|
||||
`:g.error===void 0&&(F=`No result returned.
|
||||
|
||||
`)),ce.log(`Sending response '${P} - (${g.id})'. Processing request took ${Date.now()-D}ms`,F)}else tr("send-response",g)}s(jr,"traceSendingResponse");function Sl(g){if(!(ee===p.Off||!ce))if(Qe===h.Text){let P;(ee===p.Verbose||ee===p.Compact)&&g.params&&(P=`Params: ${xt(g.params)}
|
||||
|
||||
`),ce.log(`Received request '${g.method} - (${g.id})'.`,P)}else tr("receive-request",g)}s(Sl,"traceReceivedRequest");function sn(g){if(!(ee===p.Off||!ce||g.method===C.type.method))if(Qe===h.Text){let P;(ee===p.Verbose||ee===p.Compact)&&(g.params?P=`Params: ${xt(g.params)}
|
||||
|
||||
`:P=`No parameters provided.
|
||||
|
||||
`),ce.log(`Received notification '${g.method}'.`,P)}else tr("receive-notification",g)}s(sn,"traceReceivedNotification");function wl(g,P){if(!(ee===p.Off||!ce))if(Qe===h.Text){let D;if((ee===p.Verbose||ee===p.Compact)&&(g.error&&g.error.data?D=`Error data: ${xt(g.error.data)}
|
||||
|
||||
`:g.result?D=`Result: ${xt(g.result)}
|
||||
|
||||
`:g.error===void 0&&(D=`No result returned.
|
||||
|
||||
`)),P){const F=g.error?` Request failed: ${g.error.message} (${g.error.code}).`:"";ce.log(`Received response '${P.method} - (${g.id})' in ${Date.now()-P.timerStart}ms.${F}`,D)}else ce.log(`Received response ${g.id} without active response promise.`,D)}else tr("receive-response",g)}s(wl,"traceReceivedResponse");function tr(g,P){if(!ce||ee===p.Off)return;const D={isLSPMessage:!0,type:g,message:P,timestamp:Date.now()};ce.log(D)}s(tr,"logLSPMessage");function Tr(){if(ma())throw new w(v.Closed,"Connection is closed.");if(er())throw new w(v.Disposed,"Connection is disposed.")}s(Tr,"throwIfClosedOrDisposed");function Il(){if(pa())throw new w(v.AlreadyListening,"Connection is already listening")}s(Il,"throwIfListening");function Nl(){if(!pa())throw new Error("Call listen() first.")}s(Nl,"throwIfNotListening");function m(g){return g===void 0?null:g}s(m,"undefinedToNull");function le(g){if(g!==null)return g}s(le,"nullToUndefined");function we(g){return g!=null&&!Array.isArray(g)&&typeof g=="object"}s(we,"isNamedParam");function H(g,P){switch(g){case n.ParameterStructures.auto:return we(P)?le(P):[m(P)];case n.ParameterStructures.byName:if(!we(P))throw new Error("Received parameters by name but param is not an object literal.");return le(P);case n.ParameterStructures.byPosition:return[m(P)];default:throw new Error(`Unknown parameter structure ${g.toString()}`)}}s(H,"computeSingleParam");function Ie(g,P){let D;const F=g.numberOfParams;switch(F){case 0:D=void 0;break;case 1:D=H(g.parameterStructures,P[0]);break;default:D=[];for(let Ce=0;Ce<P.length&&Ce<F;Ce++)D.push(m(P[Ce]));if(P.length<F)for(let Ce=P.length;Ce<F;Ce++)D.push(null);break}return D}s(Ie,"computeMessageParams");const va={sendNotification:s((g,...P)=>{Tr();let D,F;if(r.string(g)){D=g;const me=P[0];let De=0,Ge=n.ParameterStructures.auto;n.ParameterStructures.is(me)&&(De=1,Ge=me);let ie=P.length;const Ne=ie-De;switch(Ne){case 0:F=void 0;break;case 1:F=H(Ge,P[De]);break;default:if(Ge===n.ParameterStructures.byName)throw new Error(`Received ${Ne} parameters for 'by Name' notification parameter structure.`);F=P.slice(De,ie).map(fe=>m(fe));break}}else{const me=P;D=g.method,F=Ie(g,me)}const Ce={jsonrpc:z,method:D,params:F};return _l(Ce),I.write(Ce).catch(me=>{throw S.error("Sending notification failed."),me})},"sendNotification"),onNotification:s((g,P)=>{Tr();let D;return r.func(g)?q=g:P&&(r.string(g)?(D=g,Z.set(g,{type:void 0,handler:P})):(D=g.method,Z.set(g.method,{type:g,handler:P}))),{dispose:s(()=>{D!==void 0?Z.delete(D):q=void 0},"dispose")}},"onNotification"),onProgress:s((g,P,D)=>{if(ae.has(P))throw new Error(`Progress handler for token ${P} already registered`);return ae.set(P,D),{dispose:s(()=>{ae.delete(P)},"dispose")}},"onProgress"),sendProgress:s((g,P,D)=>va.sendNotification(c.type,{token:P,value:D}),"sendProgress"),onUnhandledProgress:Qt.event,sendRequest:s((g,...P)=>{Tr(),Nl();let D,F,Ce;if(r.string(g)){D=g;const ie=P[0],Ne=P[P.length-1];let fe=0,Ue=n.ParameterStructures.auto;n.ParameterStructures.is(ie)&&(fe=1,Ue=ie);let lt=P.length;o.CancellationToken.is(Ne)&&(lt=lt-1,Ce=Ne);const rr=lt-fe;switch(rr){case 0:F=void 0;break;case 1:F=H(Ue,P[fe]);break;default:if(Ue===n.ParameterStructures.byName)throw new Error(`Received ${rr} parameters for 'by Name' request parameter structure.`);F=P.slice(fe,lt).map(ck=>m(ck));break}}else{const ie=P;D=g.method,F=Ie(g,ie);const Ne=g.numberOfParams;Ce=o.CancellationToken.is(ie[Ne])?ie[Ne]:void 0}const me=L++;let De;Ce&&(De=Ce.onCancellationRequested(()=>{const ie=ve.sender.sendCancellation(va,me);return ie===void 0?(S.log(`Received no promise from cancellation strategy when cancelling id ${me}`),Promise.resolve()):ie.catch(()=>{S.log(`Sending cancellation messages for id ${me} failed`)})}));const Ge={jsonrpc:z,id:me,method:D,params:F};return bl(Ge),typeof ve.sender.enableCancellation=="function"&&ve.sender.enableCancellation(Ge),new Promise(async(ie,Ne)=>{const fe=s(rr=>{ie(rr),ve.sender.cleanup(me),De?.dispose()},"resolveWithCleanup"),Ue=s(rr=>{Ne(rr),ve.sender.cleanup(me),De?.dispose()},"rejectWithCleanup"),lt={method:D,timerStart:Date.now(),resolve:fe,reject:Ue};try{await I.write(Ge),Ee.set(me,lt)}catch(rr){throw S.error("Sending request failed."),lt.reject(new n.ResponseError(n.ErrorCodes.MessageWriteError,rr.message?rr.message:"Unknown reason")),rr}})},"sendRequest"),onRequest:s((g,P)=>{Tr();let D=null;return d.is(g)?(D=void 0,M=g):r.string(g)?(D=null,P!==void 0&&(D=g,Y.set(g,{handler:P,type:void 0}))):P!==void 0&&(D=g.method,Y.set(g.method,{type:g,handler:P})),{dispose:s(()=>{D!==null&&(D!==void 0?Y.delete(D):M=void 0)},"dispose")}},"onRequest"),hasPendingResponse:s(()=>Ee.size>0,"hasPendingResponse"),trace:s(async(g,P,D)=>{let F=!1,Ce=h.Text;D!==void 0&&(r.boolean(D)?F=D:(F=D.sendNotification||!1,Ce=D.traceFormat||h.Text)),ee=g,Qe=Ce,ee===p.Off?ce=void 0:ce=P,F&&!ma()&&!er()&&await va.sendNotification(T.type,{value:p.toString(g)})},"trace"),onError:ge.event,onClose:G.event,onUnhandledNotification:je.event,onD
Line truncated
|
||||
`),W("\r"),W(" "),W("\v"),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W(" "),W("\u2028"),W("\u2029"),W(" "),W(" "),W(" "),W("\uFEFF")],Yk=/[0-9a-fA-F]/,nc=/[0-9]/,Xk=/[1-9]/,ri,mR=(ri=class{constructor(){this.idx=0,this.input="",this.groupIdx=0}saveState(){return{idx:this.idx,input:this.input,groupIdx:this.groupIdx}}restoreState(e){this.idx=e.idx,this.input=e.input,this.groupIdx=e.groupIdx}pattern(e){this.idx=0,this.input=e,this.groupIdx=0,this.consumeChar("/");const r=this.disjunction();this.consumeChar("/");const n={type:"Flags",loc:{begin:this.idx,end:e.length},global:!1,ignoreCase:!1,multiLine:!1,unicode:!1,sticky:!1};for(;this.isRegExpFlag();)switch(this.popChar()){case"g":Ia(n,"global");break;case"i":Ia(n,"ignoreCase");break;case"m":Ia(n,"multiLine");break;case"u":Ia(n,"unicode");break;case"y":Ia(n,"sticky");break}if(this.idx!==this.input.length)throw Error("Redundant input: "+this.input.substring(this.idx));return{type:"Pattern",flags:n,value:r,loc:this.loc(0)}}disjunction(){const e=[],r=this.idx;for(e.push(this.alternative());this.peekChar()==="|";)this.consumeChar("|"),e.push(this.alternative());return{type:"Disjunction",value:e,loc:this.loc(r)}}alternative(){const e=[],r=this.idx;for(;this.isTerm();)e.push(this.term());return{type:"Alternative",value:e,loc:this.loc(r)}}term(){return this.isAssertion()?this.assertion():this.atom()}assertion(){const e=this.idx;switch(this.popChar()){case"^":return{type:"StartAnchor",loc:this.loc(e)};case"$":return{type:"EndAnchor",loc:this.loc(e)};case"\\":switch(this.popChar()){case"b":return{type:"WordBoundary",loc:this.loc(e)};case"B":return{type:"NonWordBoundary",loc:this.loc(e)}}throw Error("Invalid Assertion Escape");case"(":this.consumeChar("?");let r;switch(this.popChar()){case"=":r="Lookahead";break;case"!":r="NegativeLookahead";break;case"<":{switch(this.popChar()){case"=":r="Lookbehind";break;case"!":r="NegativeLookbehind"}break}}mn(r);const n=this.disjunction();return this.consumeChar(")"),{type:r,value:n,loc:this.loc(e)}}return pR()}quantifier(e=!1){let r;const n=this.idx;switch(this.popChar()){case"*":r={atLeast:0,atMost:1/0};break;case"+":r={atLeast:1,atMost:1/0};break;case"?":r={atLeast:0,atMost:1};break;case"{":const a=this.integerIncludingZero();switch(this.popChar()){case"}":r={atLeast:a,atMost:a};break;case",":let i;this.isDigit()?(i=this.integerIncludingZero(),r={atLeast:a,atMost:i}):r={atLeast:a,atMost:1/0},this.consumeChar("}");break}if(e===!0&&r===void 0)return;mn(r);break}if(!(e===!0&&r===void 0)&&mn(r))return this.peekChar(0)==="?"?(this.consumeChar("?"),r.greedy=!1):r.greedy=!0,r.type="Quantifier",r.loc=this.loc(n),r}atom(){let e;const r=this.idx;switch(this.peekChar()){case".":e=this.dotAll();break;case"\\":e=this.atomEscape();break;case"[":e=this.characterClass();break;case"(":e=this.group();break}if(e===void 0&&this.isPatternCharacter()&&(e=this.patternCharacter()),mn(e))return e.loc=this.loc(r),this.isQuantifier()&&(e.quantifier=this.quantifier()),e}dotAll(){return this.consumeChar("."),{type:"Set",complement:!0,value:[W(`
|
||||
`),W("\r"),W("\u2028"),W("\u2029")]}}atomEscape(){switch(this.consumeChar("\\"),this.peekChar()){case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":return this.decimalEscapeAtom();case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}}decimalEscapeAtom(){return{type:"GroupBackReference",value:this.positiveInteger()}}characterClassEscape(){let e,r=!1;switch(this.popChar()){case"d":e=lf;break;case"D":e=lf,r=!0;break;case"s":e=sv;break;case"S":e=sv,r=!0;break;case"w":e=uf;break;case"W":e=uf,r=!0;break}if(mn(e))return{type:"Set",value:e,complement:r}}controlEscapeAtom(){let e;switch(this.popChar()){case"f":e=W("\f");break;case"n":e=W(`
|
||||
`);break;case"r":e=W("\r");break;case"t":e=W(" ");break;case"v":e=W("\v");break}if(mn(e))return{type:"Character",value:e}}controlLetterEscapeAtom(){this.consumeChar("c");const e=this.popChar();if(/[a-zA-Z]/.test(e)===!1)throw Error("Invalid ");return{type:"Character",value:e.toUpperCase().charCodeAt(0)-64}}nulCharacterAtom(){return this.consumeChar("0"),{type:"Character",value:W("\0")}}hexEscapeSequenceAtom(){return this.consumeChar("x"),this.parseHexDigits(2)}regExpUnicodeEscapeSequenceAtom(){return this.consumeChar("u"),this.parseHexDigits(4)}identityEscapeAtom(){const e=this.popChar();return{type:"Character",value:W(e)}}classPatternCharacterAtom(){switch(this.peekChar()){case`
|
||||
`:case"\r":case"\u2028":case"\u2029":case"\\":case"]":throw Error("TBD");default:const e=this.popChar();return{type:"Character",value:W(e)}}}characterClass(){const e=[];let r=!1;for(this.consumeChar("["),this.peekChar(0)==="^"&&(this.consumeChar("^"),r=!0);this.isClassAtom();){const n=this.classAtom();if(n.type,Rm(n)&&this.isRangeDash()){this.consumeChar("-");const a=this.classAtom();if(a.type,Rm(a)){if(a.value<n.value)throw Error("Range out of order in character class");e.push({from:n.value,to:a.value})}else wc(n.value,e),e.push(W("-")),wc(a.value,e)}else wc(n.value,e)}return this.consumeChar("]"),{type:"Set",complement:r,value:e}}classAtom(){switch(this.peekChar()){case"]":case`
|
||||
`:case"\r":case"\u2028":case"\u2029":throw Error("TBD");case"\\":return this.classEscape();default:return this.classPatternCharacterAtom()}}classEscape(){switch(this.consumeChar("\\"),this.peekChar()){case"b":return this.consumeChar("b"),{type:"Character",value:W("\b")};case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}}group(){let e=!0;this.consumeChar("("),this.peekChar(0)==="?"?(this.consumeChar("?"),this.consumeChar(":"),e=!1):this.groupIdx++;const r=this.disjunction();this.consumeChar(")");const n={type:"Group",capturing:e,value:r};return e&&(n.idx=this.groupIdx),n}positiveInteger(){let e=this.popChar();if(Xk.test(e)===!1)throw Error("Expecting a positive integer");for(;nc.test(this.peekChar(0));)e+=this.popChar();return parseInt(e,10)}integerIncludingZero(){let e=this.popChar();if(nc.test(e)===!1)throw Error("Expecting an integer");for(;nc.test(this.peekChar(0));)e+=this.popChar();return parseInt(e,10)}patternCharacter(){const e=this.popChar();switch(e){case`
|
||||
`:case"\r":case"\u2028":case"\u2029":case"^":case"$":case"\\":case".":case"*":case"+":case"?":case"(":case")":case"[":case"|":throw Error("TBD");default:return{type:"Character",value:W(e)}}}isRegExpFlag(){switch(this.peekChar(0)){case"g":case"i":case"m":case"u":case"y":return!0;default:return!1}}isRangeDash(){return this.peekChar()==="-"&&this.isClassAtom(1)}isDigit(){return nc.test(this.peekChar(0))}isClassAtom(e=0){switch(this.peekChar(e)){case"]":case`
|
||||
`:case"\r":case"\u2028":case"\u2029":return!1;default:return!0}}isTerm(){return this.isAtom()||this.isAssertion()}isAtom(){if(this.isPatternCharacter())return!0;switch(this.peekChar(0)){case".":case"\\":case"[":case"(":return!0;default:return!1}}isAssertion(){switch(this.peekChar(0)){case"^":case"$":return!0;case"\\":switch(this.peekChar(1)){case"b":case"B":return!0;default:return!1}case"(":return this.peekChar(1)==="?"&&(this.peekChar(2)==="="||this.peekChar(2)==="!"||this.peekChar(2)==="<"&&(this.peekChar(3)==="="||this.peekChar(3)==="!"));default:return!1}}isQuantifier(){const e=this.saveState();try{return this.quantifier(!0)!==void 0}catch{return!1}finally{this.restoreState(e)}}isPatternCharacter(){switch(this.peekChar()){case"^":case"$":case"\\":case".":case"*":case"+":case"?":case"(":case")":case"[":case"|":case"/":case`
|
||||
`:case"\r":case"\u2028":case"\u2029":return!1;default:return!0}}parseHexDigits(e){let r="";for(let a=0;a<e;a++){const i=this.popChar();if(Yk.test(i)===!1)throw Error("Expecting a HexDecimal digits");r+=i}return{type:"Character",value:parseInt(r,16)}}peekChar(e=0){return this.input[this.idx+e]}popChar(){const e=this.peekChar(0);return this.consumeChar(void 0),e}consumeChar(e){if(e!==void 0&&this.input[this.idx]!==e)throw Error("Expected: '"+e+"' but found: '"+this.input[this.idx]+"' at offset: "+this.idx);if(this.idx>=this.input.length)throw Error("Unexpected end of input");this.idx++}loc(e){return{begin:e,end:this.idx}}},s(ri,"RegExpParser"),ri),ni,Kf=(ni=class{visitChildren(e){for(const r in e){const n=e[r];e.hasOwnProperty(r)&&(n.type!==void 0?this.visit(n):Array.isArray(n)&&n.forEach(a=>{this.visit(a)},this))}}visit(e){switch(e.type){case"Pattern":this.visitPattern(e);break;case"Flags":this.visitFlags(e);break;case"Disjunction":this.visitDisjunction(e);break;case"Alternative":this.visitAlternative(e);break;case"StartAnchor":this.visitStartAnchor(e);break;case"EndAnchor":this.visitEndAnchor(e);break;case"WordBoundary":this.visitWordBoundary(e);break;case"NonWordBoundary":this.visitNonWordBoundary(e);break;case"Lookahead":this.visitLookahead(e);break;case"NegativeLookahead":this.visitNegativeLookahead(e);break;case"Lookbehind":this.visitLookbehind(e);break;case"NegativeLookbehind":this.visitNegativeLookbehind(e);break;case"Character":this.visitCharacter(e);break;case"Set":this.visitSet(e);break;case"Group":this.visitGroup(e);break;case"GroupBackReference":this.visitGroupBackReference(e);break;case"Quantifier":this.visitQuantifier(e);break}this.visitChildren(e)}visitPattern(e){}visitFlags(e){}visitDisjunction(e){}visitAlternative(e){}visitStartAnchor(e){}visitEndAnchor(e){}visitWordBoundary(e){}visitNonWordBoundary(e){}visitLookahead(e){}visitNegativeLookahead(e){}visitLookbehind(e){}visitNegativeLookbehind(e){}visitCharacter(e){}visitSet(e){}visitGroup(e){}visitGroupBackReference(e){}visitQuantifier(e){}},s(ni,"BaseRegExpVisitor"),ni),hR=/\r?\n/gm,yR=new mR,ai,Jk=(ai=class extends Kf{constructor(){super(...arguments),this.isStarting=!0,this.endRegexpStack=[],this.multiline=!1}get endRegex(){return this.endRegexpStack.join("")}reset(e){this.multiline=!1,this.regex=e,this.startRegexp="",this.isStarting=!0,this.endRegexpStack=[]}visitGroup(e){e.quantifier&&(this.isStarting=!1,this.endRegexpStack=[])}visitCharacter(e){const r=String.fromCharCode(e.value);if(!this.multiline&&r===`
|
||||
`&&(this.multiline=!0),e.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{const n=il(r);this.endRegexpStack.push(n),this.isStarting&&(this.startRegexp+=n)}}visitSet(e){if(!this.multiline){const r=this.regex.substring(e.loc.begin,e.loc.end),n=new RegExp(r);this.multiline=!!`
|
||||
`.match(n)}if(e.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{const r=this.regex.substring(e.loc.begin,e.loc.end);this.endRegexpStack.push(r),this.isStarting&&(this.startRegexp+=r)}}visitChildren(e){e.type==="Group"&&e.quantifier||super.visitChildren(e)}},s(ai,"TerminalRegExpVisitor"),ai),On=new Jk;function gR(t){try{typeof t!="string"&&(t=t.source),t=`/${t}/`;const e=yR.pattern(t),r=[];for(const n of e.value.value)On.reset(t),On.visit(n),r.push({start:On.startRegexp,end:On.endRegex});return r}catch{return[]}}s(gR,"getTerminalParts");function iy(t){try{return typeof t=="string"&&(t=new RegExp(t)),t=t.toString(),On.reset(t),On.visit(yR.pattern(t)),On.multiline}catch{return!1}}s(iy,"isMultilineComment");var vR=`\f
|
||||
\r \v \u2028\u2029 \uFEFF`.split("");function Wf(t){const e=typeof t=="string"?new RegExp(t):t;return vR.some(r=>e.test(r))}s(Wf,"isWhitespace");function il(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}s(il,"escapeRegExp");function sy(t,e){const r=oy(t),n=e.match(r);return!!n&&n[0].length>0}s(sy,"partialMatches");function oy(t){typeof t=="string"&&(t=new RegExp(t));const e=t,r=t.source;let n=0;function a(){let i="",o;function u(c){i+=r.substr(n,c),n+=c}s(u,"appendRaw");function l(c){i+="(?:"+r.substr(n,c)+"|$)",n+=c}for(s(l,"appendOptional");n<r.length;)switch(r[n]){case"\\":switch(r[n+1]){case"c":l(3);break;case"x":l(4);break;case"u":e.unicode?r[n+2]==="{"?l(r.indexOf("}",n)-n+1):l(6):l(2);break;case"p":case"P":e.unicode?l(r.indexOf("}",n)-n+1):l(2);break;case"k":l(r.indexOf(">",n)-n+1);break;default:l(2);break}break;case"[":o=/\[(?:\\.|.)*?\]/g,o.lastIndex=n,o=o.exec(r)||[],l(o[0].length);break;case"|":case"^":case"$":case"*":case"+":case"?":u(1);break;case"{":o=/\{\d+,?\d*\}/g,o.lastIndex=n,o=o.exec(r),o?u(o[0].length):l(1);break;case"(":if(r[n+1]==="?")switch(r[n+2]){case":":i+="(?:",n+=3,i+=a()+"|$)";break;case"=":i+="(?=",n+=3,i+=a()+")";break;case"!":o=n,n+=3,a(),i+=r.substr(o,n-o);break;case"<":switch(r[n+3]){case"=":case"!":o=n,n+=4,a(),i+=r.substr(o,n-o);break;default:u(r.indexOf(">",n)-n+1),i+=a()+"|$)";break}break}else u(1),i+=a()+"|$)";break;case")":return++n,i;default:l(1);break}return i}return s(a,"process"),new RegExp(a(),t.flags)}s(oy,"partialRegExp");function ly(t){return t.rules.find(e=>mt(e)&&e.entry)}s(ly,"getEntryRule");function uy(t){return t.rules.filter(e=>jt(e)&&e.hidden)}s(uy,"getHiddenRules");function Vf(t,e){const r=new Set,n=ly(t);if(!n)return new Set(t.rules);const a=[n].concat(uy(t));for(const o of a)cy(o,r,e);const i=new Set;for(const o of t.rules)(r.has(o.name)||jt(o)&&o.hidden)&&i.add(o);return i}s(Vf,"getAllReachableRules");function cy(t,e,r){e.add(t.name),Mr(t).forEach(n=>{if(Nr(n)||r&&Ff(n)){const a=n.rule.ref;a&&!e.has(a.name)&&cy(a,e,r)}})}s(cy,"ruleDfs");function TR(t){const e=new Set;return Mr(t).forEach(r=>{Yn(r)&&(mt(r.type.ref)&&e.add(r.type.ref),Gu(r.type.ref)&&mt(r.type.ref.$container)&&e.add(r.type.ref.$container))}),e}s(TR,"getAllRulesUsedForCrossReferences");function fy(t){if(t.terminal)return t.terminal;if(t.type.ref)return Xf(t.type.ref)?.terminal}s(fy,"getCrossReferenceTerminal");function dy(t){return t.hidden&&!Wf(ju(t))}s(dy,"isCommentTerminal");function py(t,e){return!t||!e?[]:Hf(t,e,t.astNode,!0)}s(py,"findNodesForProperty");function qf(t,e,r){if(!t||!e)return;const n=Hf(t,e,t.astNode,!0);if(n.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,n.length-1)):r=0,n[r]}s(qf,"findNodeForProperty");function Hf(t,e,r,n){if(!n){const a=qn(t.grammarSource,wr);if(a&&a.feature===e)return[t]}return Sr(t)&&t.astNode===r?t.content.flatMap(a=>Hf(a,e,r,!1)):[]}s(Hf,"findNodesForPropertyInternal");function $R(t,e){return t?Yf(t,e,t?.astNode):[]}s($R,"findNodesForKeyword");function my(t,e,r){if(!t)return;const n=Yf(t,e,t?.astNode);if(n.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,n.length-1)):r=0,n[r]}s(my,"findNodeForKeyword");function Yf(t,e,r){if(t.astNode!==r)return[];if(Ir(t.grammarSource)&&t.grammarSource.value===e)return[t];const n=Jo(t).iterator();let a;const i=[];do if(a=n.next(),!a.done){const o=a.value;o.astNode===r?Ir(o.grammarSource)&&o.grammarSource.value===e&&i.push(o):n.prune()}while(!a.done);return i}s(Yf,"findNodesForKeywordInternal");function hy(t){const e=t.astNode;for(;e===t.container?.astNode;){const r=qn(t.grammarSource,wr);if(r)return r;t=t.container}}s(hy,"findAssignment");function Xf(t){let e=t;return Gu(e)&&(Yr(e.$container)?e=e.$container.$container:Hn(e.$container)?e=e.$container:tn(e.$container)),yy(t,e,new Map)}s(Xf,"findNameAssignment");function yy(t,e,r){function n(a,i){let o;return qn(a,wr)||(o=yy(i,i,r)),r.set(t,o),o}if(s(n,"go"),r.has(t))return r.get(t);r.set(t,void 0);for(const a of Mr(e)){if(wr(a)&&a.feature.toLowerCase()==="name")return r.set(t,a),a;if(Nr(a)&&mt(a.rule.ref))return n(a,a.rule.ref);if(Gf(a)&&a.typeRef?.ref)return n(a,a.typeRef.ref)}}s(yy,"findNameAssignmentInternal");function gy(t){const e=t.$container;if(Xn(e)){const r=e.elements,n=r.indexOf(t);for(let a=n-1;a>=0;a--){const i=r[a];if(Yr(i))return i;{const o=Mr(r[a]).find(Yr);if(o)return o}}}if(xf(e))return gy(e)}s(gy,"getActionAtElement");function RR(t,e){return t==="?"||t==="*"||Xn(e)&&!!e.guardCondition}s(RR,"isOptionalCardinality");function AR(t){return t==="*"||t==="+"}s(AR,"isArrayCardinality");function ER(t){return t==="+="}s(ER,"isArrayOperator");function Fu(t){return vy(t,new Set)}s(Fu,"isDataTypeRule");function vy(t,e){if(e.has(t))return!0;e.add(t);for(const r of Mr(t))if(Nr(r)){if(!r.rule.ref||mt(r.rule.ref)&&!vy(r.rule.ref,e)||Xo(r.rule.ref))return!1}else{if(wr(r))return!1;if(Yr(r))return!1}return!!t.definition}s(vy,"isDataTypeRuleInternal");function CR(t){return cf(t.type,new Set)}s(CR,"isDat
Line truncated
|
||||
`;function Fb(t,e=!1){try{const r=Ju(t);return hf(r.value,{},r.flags.ignoreCase)}catch(r){if(r.message===Gb)e&&Uy(`${mf} Unable to optimize: < ${t.toString()} >
|
||||
Complement Sets cannot be automatically optimized.
|
||||
This will disable the lexer's first char optimizations.
|
||||
See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);else{let n="";e&&(n=`
|
||||
This will disable the lexer's first char optimizations.
|
||||
See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.`),pf(`${mf}
|
||||
Failed parsing: < ${t.toString()} >
|
||||
Using the @chevrotain/regexp-to-ast library
|
||||
Please open an issue at: https://github.com/chevrotain/chevrotain/issues`+n)}}return[]}s(Fb,"getOptimizedStartCodesIndices");function hf(t,e,r){switch(t.type){case"Disjunction":for(let a=0;a<t.value.length;a++)hf(t.value[a],e,r);break;case"Alternative":const n=t.value;for(let a=0;a<n.length;a++){const i=n[a];switch(i.type){case"EndAnchor":case"GroupBackReference":case"Lookahead":case"NegativeLookahead":case"Lookbehind":case"NegativeLookbehind":case"StartAnchor":case"WordBoundary":case"NonWordBoundary":continue}const o=i;switch(o.type){case"Character":Xl(o.value,e,r);break;case"Set":if(o.complement===!0)throw Error(Gb);V(o.value,l=>{if(typeof l=="number")Xl(l,e,r);else{const c=l;if(r===!0)for(let f=c.from;f<=c.to;f++)Xl(f,e,r);else{for(let f=c.from;f<=c.to&&f<Zl;f++)Xl(f,e,r);if(c.to>=Zl){const f=c.from>=Zl?c.from:Zl,d=c.to,p=Or(f),y=Or(d);for(let h=p;h<=y;h++)e[h]=h}}}});break;case"Group":hf(o.value,e,r);break;default:throw Error("Non Exhaustive Match")}const u=o.quantifier!==void 0&&o.quantifier.atLeast===0;if(o.type==="Group"&&yf(o)===!1||o.type!=="Group"&&u===!1)break}break;default:throw Error("non exhaustive match!")}return Ke(e)}s(hf,"firstCharOptimizedIndices");function Xl(t,e,r){const n=Or(t);e[n]=n,r===!0&&zb(t,e)}s(Xl,"addOptimizedIdxToResult");function zb(t,e){const r=String.fromCharCode(t),n=r.toUpperCase();if(n!==r){const a=Or(n.charCodeAt(0));e[a]=a}else{const a=r.toLowerCase();if(a!==r){const i=Or(a.charCodeAt(0));e[i]=i}}}s(zb,"handleIgnoreCase");function Sm(t,e){return el(t.value,r=>{if(typeof r=="number")return vt(e,r);{const n=r;return el(e,a=>n.from<=a&&a<=n.to)!==void 0}})}s(Sm,"findCode");function yf(t){const e=t.quantifier;return e&&e.atLeast===0?!0:t.value?se(t.value)?Yt(t.value,yf):yf(t.value):!1}s(yf,"isWholeOptional");var Ti,v1=(Ti=class extends Kf{constructor(e){super(),this.targetCharCodes=e,this.found=!1}visitChildren(e){if(this.found!==!0){switch(e.type){case"Lookahead":this.visitLookahead(e);return;case"NegativeLookahead":this.visitNegativeLookahead(e);return;case"Lookbehind":this.visitLookbehind(e);return;case"NegativeLookbehind":this.visitNegativeLookbehind(e);return}super.visitChildren(e)}}visitCharacter(e){vt(this.targetCharCodes,e.value)&&(this.found=!0)}visitSet(e){e.complement?Sm(e,this.targetCharCodes)===void 0&&(this.found=!0):Sm(e,this.targetCharCodes)!==void 0&&(this.found=!0)}},s(Ti,"CharCodeFinder"),Ti);function dd(t,e){if(e instanceof RegExp){const r=Ju(e),n=new v1(t);return n.visit(r),n.found}else return el(e,r=>vt(t,r.charCodeAt(0)))!==void 0}s(dd,"canMatchCharCode");var Un="PATTERN",Jl="defaultMode",ic="modes";function jb(t,e){e=jy(e,{debug:!1,safeMode:!1,positionTracking:"full",lineTerminatorCharacters:["\r",`
|
||||
`],tracer:s((w,b)=>b(),"tracer")});const r=e.tracer;r("initCharCodeToOptimizedIndexMap",()=>{l_()});let n;r("Reject Lexer.NA",()=>{n=cd(t,w=>w[Un]===pt.NA)});let a=!1,i;r("Transform Patterns",()=>{a=!1,i=j(n,w=>{const b=w[Un];if(Pr(b)){const N=b.source;return N.length===1&&N!=="^"&&N!=="$"&&N!=="."&&!b.ignoreCase?N:N.length===2&&N[0]==="\\"&&!vt(["d","D","s","S","t","r","n","t","0","c","b","B","f","v","w","W"],N[1])?N[1]:wm(b)}else{if(Gr(b))return a=!0,{exec:b};if(typeof b=="object")return a=!0,b;if(typeof b=="string"){if(b.length===1)return b;{const N=b.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&"),B=new RegExp(N);return wm(B)}}else throw Error("non exhaustive match")}})});let o,u,l,c,f;r("misc mapping",()=>{o=j(n,w=>w.tokenTypeIdx),u=j(n,w=>{const b=w.GROUP;if(b!==pt.SKIPPED){if(Ct(b))return b;if(kr(b))return!1;throw Error("non exhaustive match")}}),l=j(n,w=>{const b=w.LONGER_ALT;if(b)return se(b)?j(b,B=>Fv(n,B)):[Fv(n,b)]}),c=j(n,w=>w.PUSH_MODE),f=j(n,w=>K(w,"POP_MODE"))});let d;r("Line Terminator Handling",()=>{const w=Hy(e.lineTerminatorCharacters);d=j(n,b=>!1),e.positionTracking!=="onlyOffset"&&(d=j(n,b=>K(b,"LINE_BREAKS")?!!b.LINE_BREAKS:qy(b,w)===!1&&dd(w,b.PATTERN)))});let p,y,h,T;r("Misc Mapping #2",()=>{p=j(n,Vy),y=j(i,s_),h=kt(n,(w,b)=>{const N=b.GROUP;return Ct(N)&&N!==pt.SKIPPED&&(w[N]=[]),w},{}),T=j(i,(w,b)=>({pattern:i[b],longerAlt:l[b],canLineTerminator:d[b],isCustom:p[b],short:y[b],group:u[b],push:c[b],pop:f[b],tokenTypeIdx:o[b],tokenType:n[b]}))});let C=!0,v=[];return e.safeMode||r("First Char Optimization",()=>{v=kt(n,(w,b,N)=>{if(typeof b.PATTERN=="string"){const B=b.PATTERN.charCodeAt(0),ne=Or(B);Pc(w,ne,T[N])}else if(se(b.START_CHARS_HINT)){let B;V(b.START_CHARS_HINT,ne=>{const J=typeof ne=="string"?ne.charCodeAt(0):ne,he=Or(J);B!==he&&(B=he,Pc(w,he,T[N]))})}else if(Pr(b.PATTERN))if(b.PATTERN.unicode)C=!1,e.ensureOptimizations&&pf(`${mf} Unable to analyze < ${b.PATTERN.toString()} > pattern.
|
||||
The regexp unicode flag is not currently supported by the regexp-to-ast library.
|
||||
This will disable the lexer's first char optimizations.
|
||||
For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);else{const B=Fb(b.PATTERN,e.ensureOptimizations);Re(B)&&(C=!1),V(B,ne=>{Pc(w,ne,T[N])})}else e.ensureOptimizations&&pf(`${mf} TokenType: <${b.name}> is using a custom token pattern without providing <start_chars_hint> parameter.
|
||||
This will disable the lexer's first char optimizations.
|
||||
For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`),C=!1;return w},[])}),{emptyGroups:h,patternIdxToConfig:T,charCodeToPatternIdxToConfig:v,hasCustom:a,canBeOptimized:C}}s(jb,"analyzeTokenTypes");function Bb(t,e){let r=[];const n=Kb(t);r=r.concat(n.errors);const a=Wb(n.valid),i=a.valid;return r=r.concat(a.errors),r=r.concat(Ub(i)),r=r.concat(Jb(i)),r=r.concat(Zb(i,e)),r=r.concat(Qb(i)),r}s(Bb,"validatePatterns");function Ub(t){let e=[];const r=Bt(t,n=>Pr(n[Un]));return e=e.concat(Vb(r)),e=e.concat(Hb(r)),e=e.concat(Yb(r)),e=e.concat(Xb(r)),e=e.concat(qb(r)),e}s(Ub,"validateRegExpPattern");function Kb(t){const e=Bt(t,a=>!K(a,Un)),r=j(e,a=>({message:"Token Type: ->"+a.name+"<- missing static 'PATTERN' property",type:Me.MISSING_PATTERN,tokenTypes:[a]})),n=ud(t,e);return{errors:r,valid:n}}s(Kb,"findMissingPatterns");function Wb(t){const e=Bt(t,a=>{const i=a[Un];return!Pr(i)&&!Gr(i)&&!K(i,"exec")&&!Ct(i)}),r=j(e,a=>({message:"Token Type: ->"+a.name+"<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",type:Me.INVALID_PATTERN,tokenTypes:[a]})),n=ud(t,e);return{errors:r,valid:n}}s(Wb,"findInvalidPatterns");var T1=/[^\\][$]/;function Vb(t){const a=class a extends Kf{constructor(){super(...arguments),this.found=!1}visitEndAnchor(o){this.found=!0}};s(a,"EndAnchorFinder");let e=a;const r=Bt(t,i=>{const o=i.PATTERN;try{const u=Ju(o),l=new e;return l.visit(u),l.found}catch{return T1.test(o.source)}});return j(r,i=>({message:`Unexpected RegExp Anchor Error:
|
||||
Token Type: ->`+i.name+`<- static 'PATTERN' cannot contain end of input anchor '$'
|
||||
See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:Me.EOI_ANCHOR_FOUND,tokenTypes:[i]}))}s(Vb,"findEndOfInputAnchor");function qb(t){const e=Bt(t,n=>n.PATTERN.test(""));return j(e,n=>({message:"Token Type: ->"+n.name+"<- static 'PATTERN' must not match an empty string",type:Me.EMPTY_MATCH_PATTERN,tokenTypes:[n]}))}s(qb,"findEmptyMatchRegExps");var $1=/[^\\[][\^]|^\^/;function Hb(t){const a=class a extends Kf{constructor(){super(...arguments),this.found=!1}visitStartAnchor(o){this.found=!0}};s(a,"StartAnchorFinder");let e=a;const r=Bt(t,i=>{const o=i.PATTERN;try{const u=Ju(o),l=new e;return l.visit(u),l.found}catch{return $1.test(o.source)}});return j(r,i=>({message:`Unexpected RegExp Anchor Error:
|
||||
Token Type: ->`+i.name+`<- static 'PATTERN' cannot contain start of input anchor '^'
|
||||
See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:Me.SOI_ANCHOR_FOUND,tokenTypes:[i]}))}s(Hb,"findStartOfInputAnchor");function Yb(t){const e=Bt(t,n=>{const a=n[Un];return a instanceof RegExp&&(a.multiline||a.global)});return j(e,n=>({message:"Token Type: ->"+n.name+"<- static 'PATTERN' may NOT contain global('g') or multiline('m')",type:Me.UNSUPPORTED_FLAGS_FOUND,tokenTypes:[n]}))}s(Yb,"findUnsupportedFlags");function Xb(t){const e=[];let r=j(t,i=>kt(t,(o,u)=>(i.PATTERN.source===u.PATTERN.source&&!vt(e,u)&&u.PATTERN!==pt.NA&&(e.push(u),o.push(u)),o),[]));r=Xu(r);const n=Bt(r,i=>i.length>1);return j(n,i=>{const o=j(i,l=>l.name);return{message:`The same RegExp pattern ->${Jt(i).PATTERN}<-has been used in all of the following Token Types: ${o.join(", ")} <-`,type:Me.DUPLICATE_PATTERNS_FOUND,tokenTypes:i}})}s(Xb,"findDuplicatePatterns");function Jb(t){const e=Bt(t,n=>{if(!K(n,"GROUP"))return!1;const a=n.GROUP;return a!==pt.SKIPPED&&a!==pt.NA&&!Ct(a)});return j(e,n=>({message:"Token Type: ->"+n.name+"<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",type:Me.INVALID_GROUP_TYPE_FOUND,tokenTypes:[n]}))}s(Jb,"findInvalidGroupType");function Zb(t,e){const r=Bt(t,a=>a.PUSH_MODE!==void 0&&!vt(e,a.PUSH_MODE));return j(r,a=>({message:`Token Type: ->${a.name}<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->${a.PUSH_MODE}<-which does not exist`,type:Me.PUSH_MODE_DOES_NOT_EXIST,tokenTypes:[a]}))}s(Zb,"findModesThatDoNotExist");function Qb(t){const e=[],r=kt(t,(n,a,i)=>{const o=a.PATTERN;return o===pt.NA||(Ct(o)?n.push({str:o,idx:i,tokenType:a}):Pr(o)&&t_(o)&&n.push({str:o.source,idx:i,tokenType:a})),n},[]);return V(t,(n,a)=>{V(r,({str:i,idx:o,tokenType:u})=>{if(a<o&&e_(i,n.PATTERN)){const l=`Token: ->${u.name}<- can never be matched.
|
||||
Because it appears AFTER the Token Type ->${n.name}<-in the lexer's definition.
|
||||
See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;e.push({message:l,type:Me.UNREACHABLE_PATTERN,tokenTypes:[n,u]})}})}),e}s(Qb,"findUnreachablePatterns");function e_(t,e){if(Pr(e)){if(r_(e))return!1;const r=e.exec(t);return r!==null&&r.index===0}else{if(Gr(e))return e(t,0,[],{});if(K(e,"exec"))return e.exec(t,0,[],{});if(typeof e=="string")return e===t;throw Error("non exhaustive match")}}s(e_,"tryToMatchStrToPattern");function t_(t){return el([".","\\","[","]","|","^","$","(",")","?","*","+","{"],r=>t.source.indexOf(r)!==-1)===void 0}s(t_,"noMetaChar");function r_(t){return/(\(\?=)|(\(\?!)|(\(\?<=)|(\(\?<!)/.test(t.source)}s(r_,"usesLookAheadOrBehind");function wm(t){const e=t.ignoreCase?"iy":"y";return new RegExp(`${t.source}`,e)}s(wm,"addStickyFlag");function n_(t,e,r){const n=[];return K(t,Jl)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+Jl+`> property in its definition
|
||||
`,type:Me.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),K(t,ic)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+ic+`> property in its definition
|
||||
`,type:Me.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),K(t,ic)&&K(t,Jl)&&!K(t.modes,t.defaultMode)&&n.push({message:`A MultiMode Lexer cannot be initialized with a ${Jl}: <${t.defaultMode}>which does not exist
|
||||
`,type:Me.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),K(t,ic)&&V(t.modes,(a,i)=>{V(a,(o,u)=>{if(kr(o))n.push({message:`A Lexer cannot be initialized using an undefined Token Type. Mode:<${i}> at index: <${u}>
|
||||
`,type:Me.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED});else if(K(o,"LONGER_ALT")){const l=se(o.LONGER_ALT)?o.LONGER_ALT:[o.LONGER_ALT];V(l,c=>{!kr(c)&&!vt(a,c)&&n.push({message:`A MultiMode Lexer cannot be initialized with a longer_alt <${c.name}> on token <${o.name}> outside of mode <${i}>
|
||||
`,type:Me.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE})})}})}),n}s(n_,"performRuntimeChecks");function a_(t,e,r){const n=[];let a=!1;const i=Xu(Ht(Ke(t.modes))),o=cd(i,l=>l[Un]===pt.NA),u=Hy(r);return e&&V(o,l=>{const c=qy(l,u);if(c!==!1){const d={message:o_(l,c),type:c.issue,tokenType:l};n.push(d)}else K(l,"LINE_BREAKS")?l.LINE_BREAKS===!0&&(a=!0):dd(u,l.PATTERN)&&(a=!0)}),e&&!a&&n.push({message:`Warning: No LINE_BREAKS Found.
|
||||
This Lexer has been defined to track line and column information,
|
||||
But none of the Token Types can be identified as matching a line terminator.
|
||||
See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS
|
||||
for details.`,type:Me.NO_LINE_BREAKS_FLAGS}),n}s(a_,"performWarningRuntimeChecks");function i_(t){const e={},r=Nt(t);return V(r,n=>{const a=t[n];if(se(a))e[n]=[];else throw Error("non exhaustive match")}),e}s(i_,"cloneEmptyGroups");function Vy(t){const e=t.PATTERN;if(Pr(e))return!1;if(Gr(e))return!0;if(K(e,"exec"))return!0;if(Ct(e))return!1;throw Error("non exhaustive match")}s(Vy,"isCustomPattern");function s_(t){return Ct(t)&&t.length===1?t.charCodeAt(0):!1}s(s_,"isShortPattern");var R1={test:s(function(t){const e=t.length;for(let r=this.lastIndex;r<e;r++){const n=t.charCodeAt(r);if(n===10)return this.lastIndex=r+1,!0;if(n===13)return t.charCodeAt(r+1)===10?this.lastIndex=r+2:this.lastIndex=r+1,!0}return!1},"test"),lastIndex:0};function qy(t,e){if(K(t,"LINE_BREAKS"))return!1;if(Pr(t.PATTERN)){try{dd(e,t.PATTERN)}catch(r){return{issue:Me.IDENTIFY_TERMINATOR,errMsg:r.message}}return!1}else{if(Ct(t.PATTERN))return!1;if(Vy(t))return{issue:Me.CUSTOM_LINE_BREAK};throw Error("non exhaustive match")}}s(qy,"checkLineBreaksIssues");function o_(t,e){if(e.issue===Me.IDENTIFY_TERMINATOR)return`Warning: unable to identify line terminator usage in pattern.
|
||||
The problem is in the <${t.name}> Token Type
|
||||
Root cause: ${e.errMsg}.
|
||||
For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR`;if(e.issue===Me.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the <line_breaks> option.
|
||||
The problem is in the <${t.name}> Token Type
|
||||
For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK`;throw Error("non exhaustive match")}s(o_,"buildLineBreakIssueMessage");function Hy(t){return j(t,r=>Ct(r)?r.charCodeAt(0):r)}s(Hy,"getCharCodes");function Pc(t,e,r){t[e]===void 0?t[e]=[r]:t[e].push(r)}s(Pc,"addToMapOfArrays");var Zl=256,kc=[];function Or(t){return t<Zl?t:kc[t]}s(Or,"charCodeToOptimizedIndex");function l_(){if(Re(kc)){kc=new Array(65536);for(let t=0;t<65536;t++)kc[t]=t>255?255+~~(t/255):t}}s(l_,"initCharCodeToOptimizedIndexMap");function ul(t,e){const r=t.tokenTypeIdx;return r===e.tokenTypeIdx?!0:e.isParent===!0&&e.categoryMatchesMap[r]===!0}s(ul,"tokenStructuredMatcher");function _u(t,e){return t.tokenTypeIdx===e.tokenTypeIdx}s(_u,"tokenStructuredMatcherNoCategories");var jv=1,u_={};function cl(t){const e=c_(t);f_(e),p_(e),d_(e),V(e,r=>{r.isParent=r.categoryMatches.length>0})}s(cl,"augmentTokenTypes");function c_(t){let e=it(t),r=t,n=!0;for(;n;){r=Xu(Ht(j(r,i=>i.CATEGORIES)));const a=ud(r,e);e=e.concat(a),Re(a)?n=!1:r=a}return e}s(c_,"expandCategories");function f_(t){V(t,e=>{Xy(e)||(u_[jv]=e,e.tokenTypeIdx=jv++),Im(e)&&!se(e.CATEGORIES)&&(e.CATEGORIES=[e.CATEGORIES]),Im(e)||(e.CATEGORIES=[]),m_(e)||(e.categoryMatches=[]),h_(e)||(e.categoryMatchesMap={})})}s(f_,"assignTokenDefaultProps");function d_(t){V(t,e=>{e.categoryMatches=[],V(e.categoryMatchesMap,(r,n)=>{e.categoryMatches.push(u_[n].tokenTypeIdx)})})}s(d_,"assignCategoriesTokensProp");function p_(t){V(t,e=>{Yy([],e)})}s(p_,"assignCategoriesMapProp");function Yy(t,e){V(t,r=>{e.categoryMatchesMap[r.tokenTypeIdx]=!0}),V(e.CATEGORIES,r=>{const n=t.concat(e);vt(n,r)||Yy(n,r)})}s(Yy,"singleAssignCategoriesToksMap");function Xy(t){return K(t,"tokenTypeIdx")}s(Xy,"hasShortKeyProperty");function Im(t){return K(t,"CATEGORIES")}s(Im,"hasCategoriesProperty");function m_(t){return K(t,"categoryMatches")}s(m_,"hasExtendingTokensTypesProperty");function h_(t){return K(t,"categoryMatchesMap")}s(h_,"hasExtendingTokensTypesMapProperty");function y_(t){return K(t,"tokenTypeIdx")}s(y_,"isTokenType");var Nm={buildUnableToPopLexerModeMessage(t){return`Unable to pop Lexer Mode after encountering Token ->${t.image}<- The Mode Stack is empty`},buildUnexpectedCharactersMessage(t,e,r,n,a,i){return`unexpected character: ->${t.charAt(e)}<- at offset: ${e}, skipped ${r} characters.`}},Me;(function(t){t[t.MISSING_PATTERN=0]="MISSING_PATTERN",t[t.INVALID_PATTERN=1]="INVALID_PATTERN",t[t.EOI_ANCHOR_FOUND=2]="EOI_ANCHOR_FOUND",t[t.UNSUPPORTED_FLAGS_FOUND=3]="UNSUPPORTED_FLAGS_FOUND",t[t.DUPLICATE_PATTERNS_FOUND=4]="DUPLICATE_PATTERNS_FOUND",t[t.INVALID_GROUP_TYPE_FOUND=5]="INVALID_GROUP_TYPE_FOUND",t[t.PUSH_MODE_DOES_NOT_EXIST=6]="PUSH_MODE_DOES_NOT_EXIST",t[t.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE=7]="MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE",t[t.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY=8]="MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY",t[t.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST=9]="MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST",t[t.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED=10]="LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED",t[t.SOI_ANCHOR_FOUND=11]="SOI_ANCHOR_FOUND",t[t.EMPTY_MATCH_PATTERN=12]="EMPTY_MATCH_PATTERN",t[t.NO_LINE_BREAKS_FLAGS=13]="NO_LINE_BREAKS_FLAGS",t[t.UNREACHABLE_PATTERN=14]="UNREACHABLE_PATTERN",t[t.IDENTIFY_TERMINATOR=15]="IDENTIFY_TERMINATOR",t[t.CUSTOM_LINE_BREAK=16]="CUSTOM_LINE_BREAK",t[t.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE=17]="MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE"})(Me||(Me={}));var Ql={deferDefinitionErrorsHandling:!1,positionTracking:"full",lineTerminatorsPattern:/\n|\r\n?/g,lineTerminatorCharacters:[`
|
||||
`,"\r"],ensureOptimizations:!1,safeMode:!1,errorMessageProvider:Nm,traceInitPerf:!1,skipValidations:!1,recoveryEnabled:!0};Object.freeze(Ql);var $i,pt=($i=class{constructor(e,r=Ql){if(this.lexerDefinition=e,this.lexerDefinitionErrors=[],this.lexerDefinitionWarning=[],this.patternIdxToConfig={},this.charCodeToPatternIdxToConfig={},this.modes=[],this.emptyGroups={},this.trackStartLines=!0,this.trackEndLines=!0,this.hasCustom=!1,this.canModeBeOptimized={},this.TRACE_INIT=(a,i)=>{if(this.traceInitPerf===!0){this.traceInitIndent++;const o=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent<this.traceInitMaxIdent&&console.log(`${o}--> <${a}>`);const{time:u,value:l}=Ky(i),c=u>10?console.warn:console.log;return this.traceInitIndent<this.traceInitMaxIdent&&c(`${o}<-- <${a}> time: ${u}ms`),this.traceInitIndent--,l}else return i()},typeof r=="boolean")throw Error(`The second argument to the Lexer constructor is now an ILexerConfig Object.
|
||||
a boolean 2nd argument is no longer supported`);this.config=Pt({},Ql,r);const n=this.config.traceInitPerf;n===!0?(this.traceInitMaxIdent=1/0,this.traceInitPerf=!0):typeof n=="number"&&(this.traceInitMaxIdent=n,this.traceInitPerf=!0),this.traceInitIndent=-1,this.TRACE_INIT("Lexer Constructor",()=>{let a,i=!0;this.TRACE_INIT("Lexer Config handling",()=>{if(this.config.lineTerminatorsPattern===Ql.lineTerminatorsPattern)this.config.lineTerminatorsPattern=R1;else if(this.config.lineTerminatorCharacters===Ql.lineTerminatorCharacters)throw Error(`Error: Missing <lineTerminatorCharacters> property on the Lexer config.
|
||||
For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS`);if(r.safeMode&&r.ensureOptimizations)throw Error('"safeMode" and "ensureOptimizations" flags are mutually exclusive.');this.trackStartLines=/full|onlyStart/i.test(this.config.positionTracking),this.trackEndLines=/full/i.test(this.config.positionTracking),se(e)?a={modes:{defaultMode:it(e)},defaultMode:Jl}:(i=!1,a=it(e))}),this.config.skipValidations===!1&&(this.TRACE_INIT("performRuntimeChecks",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(n_(a,this.trackStartLines,this.config.lineTerminatorCharacters))}),this.TRACE_INIT("performWarningRuntimeChecks",()=>{this.lexerDefinitionWarning=this.lexerDefinitionWarning.concat(a_(a,this.trackStartLines,this.config.lineTerminatorCharacters))})),a.modes=a.modes?a.modes:{},V(a.modes,(u,l)=>{a.modes[l]=cd(u,c=>kr(c))});const o=Nt(a.modes);if(V(a.modes,(u,l)=>{this.TRACE_INIT(`Mode: <${l}> processing`,()=>{if(this.modes.push(l),this.config.skipValidations===!1&&this.TRACE_INIT("validatePatterns",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(Bb(u,o))}),Re(this.lexerDefinitionErrors)){cl(u);let c;this.TRACE_INIT("analyzeTokenTypes",()=>{c=jb(u,{lineTerminatorCharacters:this.config.lineTerminatorCharacters,positionTracking:r.positionTracking,ensureOptimizations:r.ensureOptimizations,safeMode:r.safeMode,tracer:this.TRACE_INIT})}),this.patternIdxToConfig[l]=c.patternIdxToConfig,this.charCodeToPatternIdxToConfig[l]=c.charCodeToPatternIdxToConfig,this.emptyGroups=Pt({},this.emptyGroups,c.emptyGroups),this.hasCustom=c.hasCustom||this.hasCustom,this.canModeBeOptimized[l]=c.canBeOptimized}})}),this.defaultMode=a.defaultMode,!Re(this.lexerDefinitionErrors)&&!this.config.deferDefinitionErrorsHandling){const l=j(this.lexerDefinitionErrors,c=>c.message).join(`-----------------------
|
||||
`);throw new Error(`Errors detected in definition of Lexer:
|
||||
`+l)}V(this.lexerDefinitionWarning,u=>{Uy(u.message)}),this.TRACE_INIT("Choosing sub-methods implementations",()=>{if(i&&(this.handleModes=He),this.trackStartLines===!1&&(this.computeNewColumn=Ku),this.trackEndLines===!1&&(this.updateTokenEndLineColumnLocation=He),/full/i.test(this.config.positionTracking))this.createTokenInstance=this.createFullToken;else if(/onlyStart/i.test(this.config.positionTracking))this.createTokenInstance=this.createStartOnlyToken;else if(/onlyOffset/i.test(this.config.positionTracking))this.createTokenInstance=this.createOffsetOnlyToken;else throw Error(`Invalid <positionTracking> config option: "${this.config.positionTracking}"`);this.hasCustom?(this.addToken=this.addTokenUsingPush,this.handlePayload=this.handlePayloadWithCustom):(this.addToken=this.addTokenUsingMemberAccess,this.handlePayload=this.handlePayloadNoCustom)}),this.TRACE_INIT("Failed Optimization Warnings",()=>{const u=kt(this.canModeBeOptimized,(l,c,f)=>(c===!1&&l.push(f),l),[]);if(r.ensureOptimizations&&!Re(u))throw Error(`Lexer Modes: < ${u.join(", ")} > cannot be optimized.
|
||||
Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode.
|
||||
Or inspect the console log for details on how to resolve these issues.`)}),this.TRACE_INIT("clearRegExpParserCache",()=>{Mb()}),this.TRACE_INIT("toFastProperties",()=>{Wy(this)})})}tokenize(e,r=this.defaultMode){if(!Re(this.lexerDefinitionErrors)){const a=j(this.lexerDefinitionErrors,i=>i.message).join(`-----------------------
|
||||
`);throw new Error(`Unable to Tokenize because Errors detected in definition of Lexer:
|
||||
`+a)}return this.tokenizeInternal(e,r)}tokenizeInternal(e,r){let n,a,i,o,u,l,c,f,d,p,y,h,T,C,v;const w=e,b=w.length;let N=0,B=0;const ne=this.hasCustom?0:Math.floor(e.length/10),J=new Array(ne),he=[];let Ae=this.trackStartLines?1:void 0,ye=this.trackStartLines?1:void 0;const ue=i_(this.emptyGroups),ot=this.trackStartLines,k=this.config.lineTerminatorsPattern;let _=0,$=[],I=[];const R=[],A=[];Object.freeze(A);let S=!1;const L=s(M=>{if(R.length===1&&M.tokenType.PUSH_MODE===void 0){const Y=this.config.errorMessageProvider.buildUnableToPopLexerModeMessage(M);he.push({offset:M.startOffset,line:M.startLine,column:M.startColumn,length:M.image.length,message:Y})}else{R.pop();const Y=Bn(R);$=this.patternIdxToConfig[Y],I=this.charCodeToPatternIdxToConfig[Y],_=$.length;const q=this.canModeBeOptimized[Y]&&this.config.safeMode===!1;I&&q?S=!0:S=!1}},"pop_mode");function x(M){R.push(M),I=this.charCodeToPatternIdxToConfig[M],$=this.patternIdxToConfig[M],_=$.length,_=$.length;const Y=this.canModeBeOptimized[M]&&this.config.safeMode===!1;I&&Y?S=!0:S=!1}s(x,"push_mode"),x.call(this,r);let O;const z=this.config.recoveryEnabled;for(;N<b;){l=null,d=-1;const M=w.charCodeAt(N);let Y;if(S){const Z=Or(M),ae=I[Z];Y=ae!==void 0?ae:A}else Y=$;const q=Y.length;for(n=0;n<q;n++){O=Y[n];const Z=O.pattern;c=null;const ae=O.short;if(ae!==!1?M===ae&&(d=1,l=Z):O.isCustom===!0?(v=Z.exec(w,N,J,ue),v!==null?(l=v[0],d=l.length,v.payload!==void 0&&(c=v.payload)):l=null):(Z.lastIndex=N,d=this.matchLength(Z,e,N)),d!==-1){if(u=O.longerAlt,u!==void 0){l=e.substring(N,N+d);const Oe=u.length;for(i=0;i<Oe;i++){const pe=$[u[i]],Ee=pe.pattern;if(f=null,pe.isCustom===!0?(v=Ee.exec(w,N,J,ue),v!==null?(o=v[0],v.payload!==void 0&&(f=v.payload)):o=null):(Ee.lastIndex=N,o=this.match(Ee,e,N)),o&&o.length>l.length){l=o,d=o.length,c=f,O=pe;break}}}break}}if(d!==-1){if(p=O.group,p!==void 0&&(l=l!==null?l:e.substring(N,N+d),y=O.tokenTypeIdx,h=this.createTokenInstance(l,N,y,O.tokenType,Ae,ye,d),this.handlePayload(h,c),p===!1?B=this.addToken(J,B,h):ue[p].push(h)),ot===!0&&O.canLineTerminator===!0){let Z=0,ae,Oe;k.lastIndex=0;do l=l!==null?l:e.substring(N,N+d),ae=k.test(l),ae===!0&&(Oe=k.lastIndex-1,Z++);while(ae===!0);Z!==0?(Ae=Ae+Z,ye=d-Oe,this.updateTokenEndLineColumnLocation(h,p,Oe,Z,Ae,ye,d)):ye=this.computeNewColumn(ye,d)}else ye=this.computeNewColumn(ye,d);N=N+d,this.handleModes(O,L,x,h)}else{const Z=N,ae=Ae,Oe=ye;let pe=z===!1;for(;pe===!1&&N<b;)for(N++,a=0;a<_;a++){const Ee=$[a],Ve=Ee.pattern,Le=Ee.short;if(Le!==!1?w.charCodeAt(N)===Le&&(pe=!0):Ee.isCustom===!0?pe=Ve.exec(w,N,J,ue)!==null:(Ve.lastIndex=N,pe=Ve.exec(e)!==null),pe===!0)break}if(T=N-Z,ye=this.computeNewColumn(ye,T),C=this.config.errorMessageProvider.buildUnexpectedCharactersMessage(w,Z,T,ae,Oe,Bn(R)),he.push({offset:Z,line:ae,column:Oe,length:T,message:C}),z===!1)break}}return this.hasCustom||(J.length=B),{tokens:J,groups:ue,errors:he}}handleModes(e,r,n,a){if(e.pop===!0){const i=e.push;r(a),i!==void 0&&n.call(this,i)}else e.push!==void 0&&n.call(this,e.push)}updateTokenEndLineColumnLocation(e,r,n,a,i,o,u){let l,c;r!==void 0&&(l=n===u-1,c=l?-1:0,a===1&&l===!0||(e.endLine=i+c,e.endColumn=o-1+-c))}computeNewColumn(e,r){return e+r}createOffsetOnlyToken(e,r,n,a){return{image:e,startOffset:r,tokenTypeIdx:n,tokenType:a}}createStartOnlyToken(e,r,n,a,i,o){return{image:e,startOffset:r,startLine:i,startColumn:o,tokenTypeIdx:n,tokenType:a}}createFullToken(e,r,n,a,i,o,u){return{image:e,startOffset:r,endOffset:r+u-1,startLine:i,endLine:i,startColumn:o,endColumn:o+u-1,tokenTypeIdx:n,tokenType:a}}addTokenUsingPush(e,r,n){return e.push(n),r}addTokenUsingMemberAccess(e,r,n){return e[r]=n,r++,r}handlePayloadNoCustom(e,r){}handlePayloadWithCustom(e,r){r!==null&&(e.payload=r)}match(e,r,n){return e.test(r)===!0?r.substring(n,e.lastIndex):null}matchLength(e,r,n){return e.test(r)===!0?e.lastIndex-n:-1}},s($i,"Lexer"),$i);pt.SKIPPED="This marks a skipped Token pattern, this means each token identified by it will be consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.";pt.NA=/NOT_APPLICABLE/;function Mn(t){return Jy(t)?t.LABEL:t.name}s(Mn,"tokenLabel");function Jy(t){return Ct(t.LABEL)&&t.LABEL!==""}s(Jy,"hasTokenLabel");var A1="parent",Bv="categories",Uv="label",Kv="group",Wv="push_mode",Vv="pop_mode",qv="longer_alt",Hv="line_breaks",Yv="start_chars_hint";function Xa(t){return g_(t)}s(Xa,"createToken");function g_(t){const e=t.pattern,r={};if(r.name=t.name,kr(e)||(r.PATTERN=e),K(t,A1))throw`The parent property is no longer supported.
|
||||
See: https://github.com/chevrotain/chevrotain/issues/564#issuecomment-349062346 for details.`;return K(t,Bv)&&(r.CATEGORIES=t[Bv]),cl([r]),K(t,Uv)&&(r.LABEL=t[Uv]),K(t,Kv)&&(r.GROUP=t[Kv]),K(t,Vv)&&(r.POP_MODE=t[Vv]),K(t,Wv)&&(r.PUSH_MODE=t[Wv]),K(t,qv)&&(r.LONGER_ALT=t[qv]),K(t,Hv)&&(r.LINE_BREAKS=t[Hv]),K(t,Yv)&&(r.START_CHARS_HINT=t[Yv]),r}s(g_,"createTokenInternal");var Jr=Xa({name:"EOF",pattern:pt.NA});cl([Jr]);function Zu(t,e,r,n,a,i,o,u){return{image:e,startOffset:r,endOffset:n,startLine:a,endLine:i,startColumn:o,endColumn:u,tokenTypeIdx:t.tokenTypeIdx,tokenType:t}}s(Zu,"createTokenInstance");function Zy(t,e){return ul(t,e)}s(Zy,"tokenMatcher");var qa={buildMismatchTokenMessage({expected:t,actual:e,previous:r,ruleName:n}){return`Expecting ${Jy(t)?`--> ${Mn(t)} <--`:`token of type --> ${t.name} <--`} but found --> '${e.image}' <--`},buildNotAllInputParsedMessage({firstRedundant:t,ruleName:e}){return"Redundant input, expecting EOF but found: "+t.image},buildNoViableAltMessage({expectedPathsPerAlt:t,actual:e,previous:r,customUserDescription:n,ruleName:a}){const i="Expecting: ",u=`
|
||||
but found: '`+Jt(e).image+"'";if(n)return i+n+u;{const l=kt(t,(p,y)=>p.concat(y),[]),c=j(l,p=>`[${j(p,y=>Mn(y)).join(", ")}]`),d=`one of these possible Token sequences:
|
||||
${j(c,(p,y)=>` ${y+1}. ${p}`).join(`
|
||||
`)}`;return i+d+u}},buildEarlyExitMessage({expectedIterationPaths:t,actual:e,customUserDescription:r,ruleName:n}){const a="Expecting: ",o=`
|
||||
but found: '`+Jt(e).image+"'";if(r)return a+r+o;{const l=`expecting at least one iteration which starts with one of these possible Token sequences::
|
||||
<${j(t,c=>`[${j(c,f=>Mn(f)).join(",")}]`).join(" ,")}>`;return a+l+o}}};Object.freeze(qa);var E1={buildRuleNotFoundError(t,e){return"Invalid grammar, reference to a rule which is not defined: ->"+e.nonTerminalName+`<-
|
||||
inside top level rule: ->`+t.name+"<-"}},Ln={buildDuplicateFoundError(t,e){function r(f){return f instanceof Se?f.terminalType.name:f instanceof ht?f.nonTerminalName:""}s(r,"getExtraProductionArgument");const n=t.name,a=Jt(e),i=a.idx,o=Kt(a),u=r(a),l=i>0;let c=`->${o}${l?i:""}<- ${u?`with argument: ->${u}<-`:""}
|
||||
appears more than once (${e.length} times) in the top level rule: ->${n}<-.
|
||||
For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES
|
||||
`;return c=c.replace(/[ \t]+/g," "),c=c.replace(/\s\s+/g,`
|
||||
`),c},buildNamespaceConflictError(t){return`Namespace conflict found in grammar.
|
||||
The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <${t.name}>.
|
||||
To resolve this make sure each Terminal and Non-Terminal names are unique
|
||||
This is easy to accomplish by using the convention that Terminal names start with an uppercase letter
|
||||
and Non-Terminal names start with a lower case letter.`},buildAlternationPrefixAmbiguityError(t){const e=j(t.prefixPath,a=>Mn(a)).join(", "),r=t.alternation.idx===0?"":t.alternation.idx;return`Ambiguous alternatives: <${t.ambiguityIndices.join(" ,")}> due to common lookahead prefix
|
||||
in <OR${r}> inside <${t.topLevelRule.name}> Rule,
|
||||
<${e}> may appears as a prefix path in all these alternatives.
|
||||
See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX
|
||||
For Further details.`},buildAlternationAmbiguityError(t){const e=t.alternation.idx===0?"":t.alternation.idx,r=t.prefixPath.length===0;let n=`Ambiguous Alternatives Detected: <${t.ambiguityIndices.join(" ,")}> in <OR${e}> inside <${t.topLevelRule.name}> Rule,
|
||||
`;if(r)n+=`These alternatives are all empty (match no tokens), making them indistinguishable.
|
||||
Only the last alternative may be empty.
|
||||
`;else{const a=j(t.prefixPath,i=>Mn(i)).join(", ");n+=`<${a}> may appears as a prefix path in all these alternatives.
|
||||
`}return n+=`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES
|
||||
For Further details.`,n},buildEmptyRepetitionError(t){let e=Kt(t.repetition);return t.repetition.idx!==0&&(e+=t.repetition.idx),`The repetition <${e}> within Rule <${t.topLevelRule.name}> can never consume any tokens.
|
||||
This could lead to an infinite loop.`},buildTokenNameError(t){return"deprecated"},buildEmptyAlternationError(t){return`Ambiguous empty alternative: <${t.emptyChoiceIdx+1}> in <OR${t.alternation.idx}> inside <${t.topLevelRule.name}> Rule.
|
||||
Only the last alternative may be an empty alternative.`},buildTooManyAlternativesError(t){return`An Alternation cannot have more than 256 alternatives:
|
||||
<OR${t.alternation.idx}> inside <${t.topLevelRule.name}> Rule.
|
||||
has ${t.alternation.definition.length+1} alternatives.`},buildLeftRecursionError(t){const e=t.topLevelRule.name,r=j(t.leftRecursionPath,i=>i.name),n=`${e} --> ${r.concat([e]).join(" --> ")}`;return`Left Recursion found in grammar.
|
||||
rule: <${e}> can be invoked from itself (directly or indirectly)
|
||||
without consuming any Tokens. The grammar path that causes this is:
|
||||
${n}
|
||||
To fix this refactor your grammar to remove the left recursion.
|
||||
see: https://en.wikipedia.org/wiki/LL_parser#Left_factoring.`},buildInvalidRuleNameError(t){return"deprecated"},buildDuplicateRuleNameError(t){let e;return t.topLevelRule instanceof sl?e=t.topLevelRule.name:e=t.topLevelRule,`Duplicate definition, rule: ->${e}<- is already defined in the grammar: ->${t.grammarName}<-`}};function v_(t,e){const r=new C1(t,e);return r.resolveRefs(),r.errors}s(v_,"resolveGrammar");var Ri,C1=(Ri=class extends ol{constructor(e,r){super(),this.nameToTopRule=e,this.errMsgProvider=r,this.errors=[]}resolveRefs(){V(Ke(this.nameToTopRule),e=>{this.currTopLevel=e,e.accept(this)})}visitNonTerminal(e){const r=this.nameToTopRule[e.nonTerminalName];if(r)e.referencedRule=r;else{const n=this.errMsgProvider.buildRuleNotFoundError(this.currTopLevel,e);this.errors.push({message:n,type:yt.UNRESOLVED_SUBRULE_REF,ruleName:this.currTopLevel.name,unresolvedRefName:e.nonTerminalName})}}},s(Ri,"GastRefResolverVisitor"),Ri),Ai,b1=(Ai=class extends fd{constructor(e,r){super(),this.topProd=e,this.path=r,this.possibleTokTypes=[],this.nextProductionName="",this.nextProductionOccurrence=0,this.found=!1,this.isAtEndOfPath=!1}startWalking(){if(this.found=!1,this.path.ruleStack[0]!==this.topProd.name)throw Error("The path does not start with the walker's top Rule!");return this.ruleStack=it(this.path.ruleStack).reverse(),this.occurrenceStack=it(this.path.occurrenceStack).reverse(),this.ruleStack.pop(),this.occurrenceStack.pop(),this.updateExpectedNext(),this.walk(this.topProd),this.possibleTokTypes}walk(e,r=[]){this.found||super.walk(e,r)}walkProdRef(e,r,n){if(e.referencedRule.name===this.nextProductionName&&e.idx===this.nextProductionOccurrence){const a=r.concat(n);this.updateExpectedNext(),this.walk(e.referencedRule,a)}}updateExpectedNext(){Re(this.ruleStack)?(this.nextProductionName="",this.nextProductionOccurrence=0,this.isAtEndOfPath=!0):(this.nextProductionName=this.ruleStack.pop(),this.nextProductionOccurrence=this.occurrenceStack.pop())}},s(Ai,"AbstractNextPossibleTokensWalker"),Ai),Ei,_1=(Ei=class extends b1{constructor(e,r){super(e,r),this.path=r,this.nextTerminalName="",this.nextTerminalOccurrence=0,this.nextTerminalName=this.path.lastTok.name,this.nextTerminalOccurrence=this.path.lastTokOccurrence}walkTerminal(e,r,n){if(this.isAtEndOfPath&&e.terminalType.name===this.nextTerminalName&&e.idx===this.nextTerminalOccurrence&&!this.found){const a=r.concat(n),i=new bt({definition:a});this.possibleTokTypes=ll(i),this.found=!0}}},s(Ei,"NextAfterTokenWalker"),Ei),Ci,pd=(Ci=class extends fd{constructor(e,r){super(),this.topRule=e,this.occurrence=r,this.result={token:void 0,occurrence:void 0,isEndOfRule:void 0}}startWalking(){return this.walk(this.topRule),this.result}},s(Ci,"AbstractNextTerminalAfterProductionWalker"),Ci),bi,S1=(bi=class extends pd{walkMany(e,r,n){if(e.idx===this.occurrence){const a=Jt(r.concat(n));this.result.isEndOfRule=a===void 0,a instanceof Se&&(this.result.token=a.terminalType,this.result.occurrence=a.idx)}else super.walkMany(e,r,n)}},s(bi,"NextTerminalAfterManyWalker"),bi),_i,Xv=(_i=class extends pd{walkManySep(e,r,n){if(e.idx===this.occurrence){const a=Jt(r.concat(n));this.result.isEndOfRule=a===void 0,a instanceof Se&&(this.result.token=a.terminalType,this.result.occurrence=a.idx)}else super.walkManySep(e,r,n)}},s(_i,"NextTerminalAfterManySepWalker"),_i),Si,w1=(Si=class extends pd{walkAtLeastOne(e,r,n){if(e.idx===this.occurrence){const a=Jt(r.concat(n));this.result.isEndOfRule=a===void 0,a instanceof Se&&(this.result.token=a.terminalType,this.result.occurrence=a.idx)}else super.walkAtLeastOne(e,r,n)}},s(Si,"NextTerminalAfterAtLeastOneWalker"),Si),wi,Jv=(wi=class extends pd{walkAtLeastOneSep(e,r,n){if(e.idx===this.occurrence){const a=Jt(r.concat(n));this.result.isEndOfRule=a===void 0,a instanceof Se&&(this.result.token=a.terminalType,this.result.occurrence=a.idx)}else super.walkAtLeastOneSep(e,r,n)}},s(wi,"NextTerminalAfterAtLeastOneSepWalker"),wi);function gf(t,e,r=[]){r=it(r);let n=[],a=0;function i(u){return u.concat(nt(t,a+1))}s(i,"remainingPathWith");function o(u){const l=gf(i(u),e,r);return n.concat(l)}for(s(o,"getAlternativesForProd");r.length<e&&a<t.length;){const u=t[a];if(u instanceof bt)return o(u.definition);if(u instanceof ht)return o(u.definition);if(u instanceof at)n=o(u.definition);else if(u instanceof Ot){const l=u.definition.concat([new xe({definition:u.definition})]);return o(l)}else if(u instanceof Lt){const l=[new bt({definition:u.definition}),new xe({definition:[new Se({terminalType:u.separator})].concat(u.definition)})];return o(l)}else if(u instanceof _t){const l=u.definition.concat([new xe({definition:[new Se({terminalType:u.separator})].concat(u.definition)})]);n=o(l)}else if(u instanceof xe){const l=u.definition.concat([new xe({definition:u.definition})]);n=o(l)}else{if(u instanceof St)return V(u.definition,l=>{Re(l.definition)===!1&&(n=o(l.definition))}),n;if(u instanceof Se)r.push(u.terminalType);else throw Error("non exhaustive match")}a++}return n.push({p
Line truncated
|
||||
${i.join(`
|
||||
|
||||
`).replace(/\n/g,`
|
||||
`)}`)}},"validateVisitor")};return r.prototype=n,r.prototype.constructor=r,r._RULE_NAMES=e,r}s(rS,"createBaseSemanticVisitorConstructor");function nS(t,e,r){const n=s(function(){},"derivedConstructor");sg(n,t+"BaseSemanticsWithDefaults");const a=Object.create(r.prototype);return V(e,i=>{a[i]=tS}),n.prototype=a,n.prototype.constructor=n,n}s(nS,"createBaseVisitorConstructorWithDefaults");var Gm;(function(t){t[t.REDUNDANT_METHOD=0]="REDUNDANT_METHOD",t[t.MISSING_METHOD=1]="MISSING_METHOD"})(Gm||(Gm={}));function aS(t,e){return iS(t,e)}s(aS,"validateVisitor");function iS(t,e){const r=Bt(e,a=>Gr(t[a])===!1),n=j(r,a=>({msg:`Missing visitor method: <${a}> on ${t.constructor.name} CST Visitor.`,type:Gm.MISSING_METHOD,methodName:a}));return Xu(n)}s(iS,"validateMissingCstMethods");var Ki,j1=(Ki=class{initTreeBuilder(e){if(this.CST_STACK=[],this.outputCst=e.outputCst,this.nodeLocationTracking=K(e,"nodeLocationTracking")?e.nodeLocationTracking:Lr.nodeLocationTracking,!this.outputCst)this.cstInvocationStateUpdate=He,this.cstFinallyStateUpdate=He,this.cstPostTerminal=He,this.cstPostNonTerminal=He,this.cstPostRule=He;else if(/full/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=Mm,this.setNodeLocationFromNode=Mm,this.cstPostRule=He,this.setInitialNodeLocation=this.setInitialNodeLocationFullRecovery):(this.setNodeLocationFromToken=He,this.setNodeLocationFromNode=He,this.cstPostRule=this.cstPostRuleFull,this.setInitialNodeLocation=this.setInitialNodeLocationFullRegular);else if(/onlyOffset/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=xm,this.setNodeLocationFromNode=xm,this.cstPostRule=He,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRecovery):(this.setNodeLocationFromToken=He,this.setNodeLocationFromNode=He,this.cstPostRule=this.cstPostRuleOnlyOffset,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRegular);else if(/none/i.test(this.nodeLocationTracking))this.setNodeLocationFromToken=He,this.setNodeLocationFromNode=He,this.cstPostRule=He,this.setInitialNodeLocation=He;else throw Error(`Invalid <nodeLocationTracking> config option: "${e.nodeLocationTracking}"`)}setInitialNodeLocationOnlyOffsetRecovery(e){e.location={startOffset:NaN,endOffset:NaN}}setInitialNodeLocationOnlyOffsetRegular(e){e.location={startOffset:this.LA(1).startOffset,endOffset:NaN}}setInitialNodeLocationFullRecovery(e){e.location={startOffset:NaN,startLine:NaN,startColumn:NaN,endOffset:NaN,endLine:NaN,endColumn:NaN}}setInitialNodeLocationFullRegular(e){const r=this.LA(1);e.location={startOffset:r.startOffset,startLine:r.startLine,startColumn:r.startColumn,endOffset:NaN,endLine:NaN,endColumn:NaN}}cstInvocationStateUpdate(e){const r={name:e,children:Object.create(null)};this.setInitialNodeLocation(r),this.CST_STACK.push(r)}cstFinallyStateUpdate(){this.CST_STACK.pop()}cstPostRuleFull(e){const r=this.LA(0),n=e.location;n.startOffset<=r.startOffset?(n.endOffset=r.endOffset,n.endLine=r.endLine,n.endColumn=r.endColumn):(n.startOffset=NaN,n.startLine=NaN,n.startColumn=NaN)}cstPostRuleOnlyOffset(e){const r=this.LA(0),n=e.location;n.startOffset<=r.startOffset?n.endOffset=r.endOffset:n.startOffset=NaN}cstPostTerminal(e,r){const n=this.CST_STACK[this.CST_STACK.length-1];Q_(n,r,e),this.setNodeLocationFromToken(n.location,r)}cstPostNonTerminal(e,r){const n=this.CST_STACK[this.CST_STACK.length-1];eS(n,r,e),this.setNodeLocationFromNode(n.location,e.location)}getBaseCstVisitorConstructor(){if(kr(this.baseCstVisitorConstructor)){const e=rS(this.className,Nt(this.gastProductionsCache));return this.baseCstVisitorConstructor=e,e}return this.baseCstVisitorConstructor}getBaseCstVisitorConstructorWithDefaults(){if(kr(this.baseCstVisitorWithDefaultsConstructor)){const e=nS(this.className,Nt(this.gastProductionsCache),this.getBaseCstVisitorConstructor());return this.baseCstVisitorWithDefaultsConstructor=e,e}return this.baseCstVisitorWithDefaultsConstructor}getLastExplicitRuleShortName(){const e=this.RULE_STACK;return e[e.length-1]}getPreviousExplicitRuleShortName(){const e=this.RULE_STACK;return e[e.length-2]}getLastExplicitRuleOccurrenceIndex(){const e=this.RULE_OCCURRENCE_STACK;return e[e.length-1]}},s(Ki,"TreeBuilder"),Ki),Wi,B1=(Wi=class{initLexerAdapter(){this.tokVector=[],this.tokVectorLength=0,this.currIdx=-1}set input(e){if(this.selfAnalysisDone!==!0)throw Error("Missing <performSelfAnalysis> invocation at the end of the Parser's constructor.");this.reset(),this.tokVector=e,this.tokVectorLength=e.length}get input(){return this.tokVector}SKIP_TOKEN(){return this.currIdx<=this.tokVector.length-2?(this.consumeToken(),this.LA(1)):Tf}LA(e){const r=this.currIdx+e;return r<0||this.tokVectorLength<=r?Tf:this.tokVector[r]}consumeToken(){this.currIdx++}exportLexerState(){return this.currIdx}importLexerState(e){this.currIdx=e}resetLexerState(){this.currIdx=-1}moveToTerminatedState(){this.currIdx=this.tokVector.length-1}getLexerPosition(){return this.exportLexerState()}},sLine truncated
|
||||
See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_6-0-0
|
||||
For Further details.`);if(se(e)){if(Re(e))throw Error(`A Token Vocabulary cannot be empty.
|
||||
Note that the first argument for the parser constructor
|
||||
is no longer a Token vector (since v4.0).`);if(typeof e[0].startOffset=="number")throw Error(`The Parser constructor no longer accepts a token vector as the first argument.
|
||||
See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_4-0-0
|
||||
For Further details.`)}if(se(e))this.tokensMap=kt(e,(i,o)=>(i[o.name]=o,i),{});else if(K(e,"modes")&&Yt(Ht(Ke(e.modes)),y_)){const i=Ht(Ke(e.modes)),o=By(i);this.tokensMap=kt(o,(u,l)=>(u[l.name]=l,u),{})}else if(zt(e))this.tokensMap=it(e);else throw new Error("<tokensDictionary> argument must be An Array of Token constructors, A dictionary of Token constructors or an IMultiModeLexerDefinition");this.tokensMap.EOF=Jr;const n=K(e,"modes")?Ht(Ke(e.modes)):Ke(e),a=Yt(n,i=>Re(i.categoryMatches));this.tokenMatcher=a?_u:ul,cl(Ke(this.tokensMap))}defineRule(e,r,n){if(this.selfAnalysisDone)throw Error(`Grammar rule <${e}> may not be defined after the 'performSelfAnalysis' method has been called'
|
||||
Make sure that all grammar rule definitions are done before 'performSelfAnalysis' is called.`);const a=K(n,"resyncEnabled")?n.resyncEnabled:$f.resyncEnabled,i=K(n,"recoveryValueFunc")?n.recoveryValueFunc:$f.recoveryValueFunc,o=this.ruleShortNameIdx<<M1+nn;this.ruleShortNameIdx++,this.shortRuleNameToFull[o]=e,this.fullRuleNameToShort[e]=o;let u;return this.outputCst===!0?u=s(function(...f){try{this.ruleInvocationStateUpdate(o,e,this.subruleIdx),r.apply(this,f);const d=this.CST_STACK[this.CST_STACK.length-1];return this.cstPostRule(d),d}catch(d){return this.invokeRuleCatch(d,a,i)}finally{this.ruleFinallyStateUpdate()}},"invokeRuleWithTry"):u=s(function(...f){try{return this.ruleInvocationStateUpdate(o,e,this.subruleIdx),r.apply(this,f)}catch(d){return this.invokeRuleCatch(d,a,i)}finally{this.ruleFinallyStateUpdate()}},"invokeRuleWithTryCst"),Object.assign(u,{ruleName:e,originalGrammarAction:r})}invokeRuleCatch(e,r,n){const a=this.RULE_STACK.length===1,i=r&&!this.isBackTracking()&&this.recoveryEnabled;if(Su(e)){const o=e;if(i){const u=this.findReSyncTokenType();if(this.isInCurrentRuleReSyncSet(u))if(o.resyncedTokens=this.reSyncTo(u),this.outputCst){const l=this.CST_STACK[this.CST_STACK.length-1];return l.recoveredNode=!0,l}else return n(e);else{if(this.outputCst){const l=this.CST_STACK[this.CST_STACK.length-1];l.recoveredNode=!0,o.partialCstResult=l}throw o}}else{if(a)return this.moveToTerminatedState(),n(e);throw o}}else throw e}optionInternal(e,r){const n=this.getKeyForAutomaticLookahead(J_,r);return this.optionInternalLogic(e,r,n)}optionInternalLogic(e,r,n){let a=this.getLaFuncFromCache(n),i;if(typeof e!="function"){i=e.DEF;const o=e.GATE;if(o!==void 0){const u=a;a=s(()=>o.call(this)&&u.call(this),"lookAheadFunc")}}else i=e;if(a.call(this)===!0)return i.call(this)}atLeastOneInternal(e,r){const n=this.getKeyForAutomaticLookahead(Lm,e);return this.atLeastOneInternalLogic(e,r,n)}atLeastOneInternalLogic(e,r,n){let a=this.getLaFuncFromCache(n),i;if(typeof r!="function"){i=r.DEF;const o=r.GATE;if(o!==void 0){const u=a;a=s(()=>o.call(this)&&u.call(this),"lookAheadFunc")}}else i=r;if(a.call(this)===!0){let o=this.doSingleRepetition(i);for(;a.call(this)===!0&&o===!0;)o=this.doSingleRepetition(i)}else throw this.raiseEarlyExitException(e,Pe.REPETITION_MANDATORY,r.ERR_MSG);this.attemptInRepetitionRecovery(this.atLeastOneInternal,[e,r],a,Lm,e,w1)}atLeastOneSepFirstInternal(e,r){const n=this.getKeyForAutomaticLookahead(Lc,e);this.atLeastOneSepFirstInternalLogic(e,r,n)}atLeastOneSepFirstInternalLogic(e,r,n){const a=r.DEF,i=r.SEP;if(this.getLaFuncFromCache(n).call(this)===!0){a.call(this);const u=s(()=>this.tokenMatcher(this.LA(1),i),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),i)===!0;)this.CONSUME(i),a.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,i,u,a,Jv],u,Lc,e,Jv)}else throw this.raiseEarlyExitException(e,Pe.REPETITION_MANDATORY_WITH_SEPARATOR,r.ERR_MSG)}manyInternal(e,r){const n=this.getKeyForAutomaticLookahead(Om,e);return this.manyInternalLogic(e,r,n)}manyInternalLogic(e,r,n){let a=this.getLaFuncFromCache(n),i;if(typeof r!="function"){i=r.DEF;const u=r.GATE;if(u!==void 0){const l=a;a=s(()=>u.call(this)&&l.call(this),"lookaheadFunction")}}else i=r;let o=!0;for(;a.call(this)===!0&&o===!0;)o=this.doSingleRepetition(i);this.attemptInRepetitionRecovery(this.manyInternal,[e,r],a,Om,e,S1,o)}manySepFirstInternal(e,r){const n=this.getKeyForAutomaticLookahead(Dm,e);this.manySepFirstInternalLogic(e,r,n)}manySepFirstInternalLogic(e,r,n){const a=r.DEF,i=r.SEP;if(this.getLaFuncFromCache(n).call(this)===!0){a.call(this);const u=s(()=>this.tokenMatcher(this.LA(1),i),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),i)===!0;)this.CONSUME(i),a.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,i,u,a,Xv],u,Dm,e,Xv)}}repetitionSepSecondInternal(e,r,n,a,i){for(;n();)this.CONSUME(r),a.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,r,n,a,i],n,Lc,e,i)}doSingleRepetition(e){const r=this.getLexerPosition();return e.call(this),this.getLexerPosition()>r}orInternal(e,r){const n=this.getKeyForAutomaticLookahead(X_,r),a=se(e)?e:e.DEF,o=this.getLaFuncFromCache(n).call(this,a);if(o!==void 0)return a[o].ALT.call(this);this.raiseNoAltException(r,e.ERR_MSG)}ruleFinallyStateUpdate(){if(this.RULE_STACK.pop(),this.RULE_OCCURRENCE_STACK.pop(),this.cstFinallyStateUpdate(),this.RULE_STACK.length===0&&this.isAtEndOfInput()===!1){const e=this.LA(1),r=this.errorMessageProvider.buildNotAllInputParsedMessage({firstRedundant:e,ruleName:this.getCurrRuleFullName()});this.SAVE_ERROR(new O1(r,e))}}subruleInternal(e,r,n){let a;try{const i=n!==void 0?n.ARGS:void 0;return this.subruleIdx=r,a=e.apply(this,i),this.cstPostNonTerminal(a,n!==void 0&&n.LABEL!==void 0?n.LABEL:e.ruleName),a}catch(i){throw this.subruleInternalError(i,n,e.ruleName)}}subruleInternalError(e,r,n){throw Su(e)&&e.partialCstResult!==void 0&&(this.cstPostNonTerminal(e.partialCstRe
Line truncated
|
||||
See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,-1,-1,-1,-1,-1,-1);Object.freeze(oS);var q1={name:`This CSTNode indicates the Parser is in Recording Phase
|
||||
See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,children:{}},Xi,H1=(Xi=class{initGastRecorder(e){this.recordingProdStack=[],this.RECORDING_PHASE=!1}enableRecording(){this.RECORDING_PHASE=!0,this.TRACE_INIT("Enable Recording",()=>{for(let e=0;e<10;e++){const r=e>0?e:"";this[`CONSUME${r}`]=function(n,a){return this.consumeInternalRecord(n,e,a)},this[`SUBRULE${r}`]=function(n,a){return this.subruleInternalRecord(n,e,a)},this[`OPTION${r}`]=function(n){return this.optionInternalRecord(n,e)},this[`OR${r}`]=function(n){return this.orInternalRecord(n,e)},this[`MANY${r}`]=function(n){this.manyInternalRecord(e,n)},this[`MANY_SEP${r}`]=function(n){this.manySepFirstInternalRecord(e,n)},this[`AT_LEAST_ONE${r}`]=function(n){this.atLeastOneInternalRecord(e,n)},this[`AT_LEAST_ONE_SEP${r}`]=function(n){this.atLeastOneSepFirstInternalRecord(e,n)}}this.consume=function(e,r,n){return this.consumeInternalRecord(r,e,n)},this.subrule=function(e,r,n){return this.subruleInternalRecord(r,e,n)},this.option=function(e,r){return this.optionInternalRecord(r,e)},this.or=function(e,r){return this.orInternalRecord(r,e)},this.many=function(e,r){this.manyInternalRecord(e,r)},this.atLeastOne=function(e,r){this.atLeastOneInternalRecord(e,r)},this.ACTION=this.ACTION_RECORD,this.BACKTRACK=this.BACKTRACK_RECORD,this.LA=this.LA_RECORD})}disableRecording(){this.RECORDING_PHASE=!1,this.TRACE_INIT("Deleting Recording methods",()=>{const e=this;for(let r=0;r<10;r++){const n=r>0?r:"";delete e[`CONSUME${n}`],delete e[`SUBRULE${n}`],delete e[`OPTION${n}`],delete e[`OR${n}`],delete e[`MANY${n}`],delete e[`MANY_SEP${n}`],delete e[`AT_LEAST_ONE${n}`],delete e[`AT_LEAST_ONE_SEP${n}`]}delete e.consume,delete e.subrule,delete e.option,delete e.or,delete e.many,delete e.atLeastOne,delete e.ACTION,delete e.BACKTRACK,delete e.LA})}ACTION_RECORD(e){}BACKTRACK_RECORD(e,r){return()=>!0}LA_RECORD(e){return Tf}topLevelRuleRecord(e,r){try{const n=new sl({definition:[],name:e});return n.name=e,this.recordingProdStack.push(n),r.call(this),this.recordingProdStack.pop(),n}catch(n){if(n.KNOWN_RECORDER_ERROR!==!0)try{n.message=n.message+`
|
||||
This error was thrown during the "grammar recording phase" For more info see:
|
||||
https://chevrotain.io/docs/guide/internals.html#grammar-recording`}catch{throw n}throw n}}optionInternalRecord(e,r){return Na.call(this,at,e,r)}atLeastOneInternalRecord(e,r){Na.call(this,Ot,r,e)}atLeastOneSepFirstInternalRecord(e,r){Na.call(this,Lt,r,e,Zv)}manyInternalRecord(e,r){Na.call(this,xe,r,e)}manySepFirstInternalRecord(e,r){Na.call(this,_t,r,e,Zv)}orInternalRecord(e,r){return lS.call(this,e,r)}subruleInternalRecord(e,r,n){if(wu(r),!e||K(e,"ruleName")===!1){const u=new Error(`<SUBRULE${Fm(r)}> argument is invalid expecting a Parser method reference but got: <${JSON.stringify(e)}>
|
||||
inside top level rule: <${this.recordingProdStack[0].name}>`);throw u.KNOWN_RECORDER_ERROR=!0,u}const a=Bn(this.recordingProdStack),i=e.ruleName,o=new ht({idx:r,nonTerminalName:i,label:n?.LABEL,referencedRule:void 0});return a.definition.push(o),this.outputCst?q1:yd}consumeInternalRecord(e,r,n){if(wu(r),!Xy(e)){const o=new Error(`<CONSUME${Fm(r)}> argument is invalid expecting a TokenType reference but got: <${JSON.stringify(e)}>
|
||||
inside top level rule: <${this.recordingProdStack[0].name}>`);throw o.KNOWN_RECORDER_ERROR=!0,o}const a=Bn(this.recordingProdStack),i=new Se({idx:r,terminalType:e,label:n?.LABEL});return a.definition.push(i),oS}},s(Xi,"GastRecorder"),Xi);function Na(t,e,r,n=!1){wu(r);const a=Bn(this.recordingProdStack),i=Gr(e)?e:e.DEF,o=new t({definition:[],idx:r});return n&&(o.separator=e.SEP),K(e,"MAX_LOOKAHEAD")&&(o.maxLookahead=e.MAX_LOOKAHEAD),this.recordingProdStack.push(o),i.call(this),a.definition.push(o),this.recordingProdStack.pop(),yd}s(Na,"recordProd");function lS(t,e){wu(e);const r=Bn(this.recordingProdStack),n=se(t)===!1,a=n===!1?t:t.DEF,i=new St({definition:[],idx:e,ignoreAmbiguities:n&&t.IGNORE_AMBIGUITIES===!0});K(t,"MAX_LOOKAHEAD")&&(i.maxLookahead=t.MAX_LOOKAHEAD);const o=Eb(a,u=>Gr(u.GATE));return i.hasPredicates=o,r.definition.push(i),V(a,u=>{const l=new bt({definition:[]});i.definition.push(l),K(u,"IGNORE_AMBIGUITIES")?l.ignoreAmbiguities=u.IGNORE_AMBIGUITIES:K(u,"GATE")&&(l.ignoreAmbiguities=!0),this.recordingProdStack.push(l),u.ALT.call(this),this.recordingProdStack.pop()}),yd}s(lS,"recordOrProd");function Fm(t){return t===0?"":`${t}`}s(Fm,"getIdxSuffix");function wu(t){if(t<0||t>Qv){const e=new Error(`Invalid DSL Method idx value: <${t}>
|
||||
Idx value must be a none negative value smaller than ${Qv+1}`);throw e.KNOWN_RECORDER_ERROR=!0,e}}s(wu,"assertMethodIdxIsValid");var Ji,Y1=(Ji=class{initPerformanceTracer(e){if(K(e,"traceInitPerf")){const r=e.traceInitPerf,n=typeof r=="number";this.traceInitMaxIdent=n?r:1/0,this.traceInitPerf=n?r>0:r}else this.traceInitMaxIdent=0,this.traceInitPerf=Lr.traceInitPerf;this.traceInitIndent=-1}TRACE_INIT(e,r){if(this.traceInitPerf===!0){this.traceInitIndent++;const n=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent<this.traceInitMaxIdent&&console.log(`${n}--> <${e}>`);const{time:a,value:i}=Ky(r),o=a>10?console.warn:console.log;return this.traceInitIndent<this.traceInitMaxIdent&&o(`${n}<-- <${e}> time: ${a}ms`),this.traceInitIndent--,i}else return r()}},s(Ji,"PerformanceTracer"),Ji);function uS(t,e){e.forEach(r=>{const n=r.prototype;Object.getOwnPropertyNames(n).forEach(a=>{if(a==="constructor")return;const i=Object.getOwnPropertyDescriptor(n,a);i&&(i.get||i.set)?Object.defineProperty(t.prototype,a,i):t.prototype[a]=r.prototype[a]})})}s(uS,"applyMixins");var Tf=Zu(Jr,"",NaN,NaN,NaN,NaN,NaN,NaN);Object.freeze(Tf);var Lr=Object.freeze({recoveryEnabled:!1,maxLookahead:3,dynamicTokensEnabled:!1,outputCst:!0,errorMessageProvider:qa,nodeLocationTracking:"none",traceInitPerf:!1,skipValidations:!1}),$f=Object.freeze({recoveryValueFunc:s(()=>{},"recoveryValueFunc"),resyncEnabled:!0}),yt;(function(t){t[t.INVALID_RULE_NAME=0]="INVALID_RULE_NAME",t[t.DUPLICATE_RULE_NAME=1]="DUPLICATE_RULE_NAME",t[t.INVALID_RULE_OVERRIDE=2]="INVALID_RULE_OVERRIDE",t[t.DUPLICATE_PRODUCTIONS=3]="DUPLICATE_PRODUCTIONS",t[t.UNRESOLVED_SUBRULE_REF=4]="UNRESOLVED_SUBRULE_REF",t[t.LEFT_RECURSION=5]="LEFT_RECURSION",t[t.NONE_LAST_EMPTY_ALT=6]="NONE_LAST_EMPTY_ALT",t[t.AMBIGUOUS_ALTS=7]="AMBIGUOUS_ALTS",t[t.CONFLICT_TOKENS_RULES_NAMESPACE=8]="CONFLICT_TOKENS_RULES_NAMESPACE",t[t.INVALID_TOKEN_NAME=9]="INVALID_TOKEN_NAME",t[t.NO_NON_EMPTY_LOOKAHEAD=10]="NO_NON_EMPTY_LOOKAHEAD",t[t.AMBIGUOUS_PREFIX_ALTS=11]="AMBIGUOUS_PREFIX_ALTS",t[t.TOO_MANY_ALTS=12]="TOO_MANY_ALTS",t[t.CUSTOM_LOOKAHEAD_VALIDATION=13]="CUSTOM_LOOKAHEAD_VALIDATION"})(yt||(yt={}));function zm(t=void 0){return function(){return t}}s(zm,"EMPTY_ALT");var Fn,og=(Fn=class{static performSelfAnalysis(e){throw Error("The **static** `performSelfAnalysis` method has been deprecated. \nUse the **instance** method with the same name instead.")}performSelfAnalysis(){this.TRACE_INIT("performSelfAnalysis",()=>{let e;this.selfAnalysisDone=!0;const r=this.className;this.TRACE_INIT("toFastProps",()=>{Wy(this)}),this.TRACE_INIT("Grammar Recording",()=>{try{this.enableRecording(),V(this.definedRulesNames,a=>{const o=this[a].originalGrammarAction;let u;this.TRACE_INIT(`${a} Rule`,()=>{u=this.topLevelRuleRecord(a,o)}),this.gastProductionsCache[a]=u})}finally{this.disableRecording()}});let n=[];if(this.TRACE_INIT("Grammar Resolving",()=>{n=z_({rules:Ke(this.gastProductionsCache)}),this.definitionErrors=this.definitionErrors.concat(n)}),this.TRACE_INIT("Grammar Validations",()=>{if(Re(n)&&this.skipValidations===!1){const a=j_({rules:Ke(this.gastProductionsCache),tokenTypes:Ke(this.tokensMap),errMsgProvider:Ln,grammarName:r}),i=S_({lookaheadStrategy:this.lookaheadStrategy,rules:Ke(this.gastProductionsCache),tokenTypes:Ke(this.tokensMap),grammarName:r});this.definitionErrors=this.definitionErrors.concat(a,i)}}),Re(this.definitionErrors)&&(this.recoveryEnabled&&this.TRACE_INIT("computeAllProdsFollows",()=>{const a=Db(Ke(this.gastProductionsCache));this.resyncFollows=a}),this.TRACE_INIT("ComputeLookaheadFunctions",()=>{var a,i;(i=(a=this.lookaheadStrategy).initialize)===null||i===void 0||i.call(a,{rules:Ke(this.gastProductionsCache)}),this.preComputeLookaheadFunctions(Ke(this.gastProductionsCache))})),!Fn.DEFER_DEFINITION_ERRORS_HANDLING&&!Re(this.definitionErrors))throw e=j(this.definitionErrors,a=>a.message),new Error(`Parser Definition Errors detected:
|
||||
${e.join(`
|
||||
-------------------------------
|
||||
`)}`)})}constructor(e,r){this.definitionErrors=[],this.selfAnalysisDone=!1;const n=this;if(n.initErrorHandler(r),n.initLexerAdapter(),n.initLooksAhead(r),n.initRecognizerEngine(e,r),n.initRecoverable(r),n.initTreeBuilder(r),n.initContentAssist(),n.initGastRecorder(r),n.initPerformanceTracer(r),K(r,"ignoredIssues"))throw new Error(`The <ignoredIssues> IParserConfig property has been deprecated.
|
||||
Please use the <IGNORE_AMBIGUITIES> flag on the relevant DSL method instead.
|
||||
See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES
|
||||
For further details.`);this.skipValidations=K(r,"skipValidations")?r.skipValidations:Lr.skipValidations}},s(Fn,"Parser"),Fn);og.DEFER_DEFINITION_ERRORS_HANDLING=!1;uS(og,[x1,G1,j1,B1,K1,U1,W1,V1,H1,Y1]);var Zi,X1=(Zi=class extends og{constructor(e,r=Lr){const n=it(r);n.outputCst=!1,super(e,n)}},s(Zi,"EmbeddedActionsParser"),Zi);function cS(t,e){for(var r=-1,n=t==null?0:t.length,a=Array(n);++r<n;)a[r]=e(t[r],r,t);return a}s(cS,"arrayMap");var fS=cS;function dS(){this.__data__=[],this.size=0}s(dS,"listCacheClear");var J1=dS;function pS(t,e){return t===e||t!==t&&e!==e}s(pS,"eq");var mS=pS;function hS(t,e){for(var r=t.length;r--;)if(mS(t[r][0],e))return r;return-1}s(hS,"assocIndexOf");var gd=hS,Z1=Array.prototype,Q1=Z1.splice;function yS(t){var e=this.__data__,r=gd(e,t);if(r<0)return!1;var n=e.length-1;return r==n?e.pop():Q1.call(e,r,1),--this.size,!0}s(yS,"listCacheDelete");var eG=yS;function gS(t){var e=this.__data__,r=gd(e,t);return r<0?void 0:e[r][1]}s(gS,"listCacheGet");var tG=gS;function vS(t){return gd(this.__data__,t)>-1}s(vS,"listCacheHas");var rG=vS;function TS(t,e){var r=this.__data__,n=gd(r,t);return n<0?(++this.size,r.push([t,e])):r[n][1]=e,this}s(TS,"listCacheSet");var nG=TS;function ia(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}s(ia,"ListCache");ia.prototype.clear=J1;ia.prototype.delete=eG;ia.prototype.get=tG;ia.prototype.has=rG;ia.prototype.set=nG;var vd=ia;function $S(){this.__data__=new vd,this.size=0}s($S,"stackClear");var aG=$S;function RS(t){var e=this.__data__,r=e.delete(t);return this.size=e.size,r}s(RS,"stackDelete");var iG=RS;function AS(t){return this.__data__.get(t)}s(AS,"stackGet");var sG=AS;function ES(t){return this.__data__.has(t)}s(ES,"stackHas");var oG=ES,lG=typeof global=="object"&&global&&global.Object===Object&&global,CS=lG,uG=typeof self=="object"&&self&&self.Object===Object&&self,cG=CS||uG||Function("return this")(),Fr=cG,fG=Fr.Symbol,pr=fG,bS=Object.prototype,dG=bS.hasOwnProperty,pG=bS.toString,Ol=pr?pr.toStringTag:void 0;function _S(t){var e=dG.call(t,Ol),r=t[Ol];try{t[Ol]=void 0;var n=!0}catch{}var a=pG.call(t);return n&&(e?t[Ol]=r:delete t[Ol]),a}s(_S,"getRawTag");var mG=_S,hG=Object.prototype,yG=hG.toString;function SS(t){return yG.call(t)}s(SS,"objectToString");var gG=SS,vG="[object Null]",TG="[object Undefined]",eT=pr?pr.toStringTag:void 0;function wS(t){return t==null?t===void 0?TG:vG:eT&&eT in Object(t)?mG(t):gG(t)}s(wS,"baseGetTag");var fl=wS;function IS(t){var e=typeof t;return t!=null&&(e=="object"||e=="function")}s(IS,"isObject");var lg=IS,$G="[object AsyncFunction]",RG="[object Function]",AG="[object GeneratorFunction]",EG="[object Proxy]";function NS(t){if(!lg(t))return!1;var e=fl(t);return e==RG||e==AG||e==$G||e==EG}s(NS,"isFunction");var PS=NS,CG=Fr["__core-js_shared__"],qd=CG,tT=(function(){var t=/[^.]+$/.exec(qd&&qd.keys&&qd.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""})();function kS(t){return!!tT&&tT in t}s(kS,"isMasked");var bG=kS,_G=Function.prototype,SG=_G.toString;function OS(t){if(t!=null){try{return SG.call(t)}catch{}try{return t+""}catch{}}return""}s(OS,"toSource");var sa=OS,wG=/[\\^$.*+?()[\]{}|]/g,IG=/^\[object .+?Constructor\]$/,NG=Function.prototype,PG=Object.prototype,kG=NG.toString,OG=PG.hasOwnProperty,LG=RegExp("^"+kG.call(OG).replace(wG,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function LS(t){if(!lg(t)||bG(t))return!1;var e=PS(t)?LG:IG;return e.test(sa(t))}s(LS,"baseIsNative");var DG=LS;function DS(t,e){return t?.[e]}s(DS,"getValue");var xG=DS;function xS(t,e){var r=xG(t,e);return DG(r)?r:void 0}s(xS,"getNative");var dl=xS,MG=dl(Fr,"Map"),Iu=MG,GG=dl(Object,"create"),Nu=GG;function MS(){this.__data__=Nu?Nu(null):{},this.size=0}s(MS,"hashClear");var FG=MS;function GS(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}s(GS,"hashDelete");var zG=GS,jG="__lodash_hash_undefined__",BG=Object.prototype,UG=BG.hasOwnProperty;function FS(t){var e=this.__data__;if(Nu){var r=e[t];return r===jG?void 0:r}return UG.call(e,t)?e[t]:void 0}s(FS,"hashGet");var KG=FS,WG=Object.prototype,VG=WG.hasOwnProperty;function zS(t){var e=this.__data__;return Nu?e[t]!==void 0:VG.call(e,t)}s(zS,"hashHas");var qG=zS,HG="__lodash_hash_undefined__";function jS(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=Nu&&e===void 0?HG:e,this}s(jS,"hashSet");var YG=jS;function oa(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}s(oa,"Hash");oa.prototype.clear=FG;oa.prototype.delete=zG;oa.prototype.get=KG;oa.prototype.has=qG;oa.prototype.set=YG;var rT=oa;function BS(){this.size=0,this.__data__={hash:new rT,map:new(Iu||vd),string:new rT}}s(BS,"mapCacheClear");var XG=BS;function US(t){var e=typeof t;return e=="string"||e=="number"||e=="symbol"||e=="boolean"?t!=="__proto__":t===null}s(US,"isKeyable");var JG=US;function KS(t,e){var r=t.__data__;return JG(e)?r[typeof e=="string"?"string":"hash"]:r.map}s(KS,"getMapDat
Line truncated
|
||||
<${e}> may appears as a prefix path in all these alternatives.
|
||||
`;return n=n+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES
|
||||
For Further details.`,n}s(uN,"buildAmbiguityError");function cN(t){if(t instanceof ht)return"SUBRULE";if(t instanceof at)return"OPTION";if(t instanceof St)return"OR";if(t instanceof Ot)return"AT_LEAST_ONE";if(t instanceof Lt)return"AT_LEAST_ONE_SEP";if(t instanceof _t)return"MANY_SEP";if(t instanceof xe)return"MANY";if(t instanceof Se)return"CONSUME";throw Error("non exhaustive match")}s(cN,"getProductionDslName");function fN(t,e,r){const n=Gj(e.configs.elements,i=>i.state.transitions),a=Jj(n.filter(i=>i instanceof vg).map(i=>i.tokenType),i=>i.tokenTypeIdx);return{actualToken:r,possibleTokenTypes:a,tokenPath:t}}s(fN,"buildAdaptivePredictError");function dN(t,e){return t.edges[e.tokenTypeIdx]}s(dN,"getExistingTargetState");function pN(t,e,r){const n=new Wm,a=[];for(const o of t.elements){if(r.is(o.alt)===!1)continue;if(o.state.type===tc){a.push(o);continue}const u=o.state.transitions.length;for(let l=0;l<u;l++){const c=o.state.transitions[l],f=mN(c,e);f!==void 0&&n.add({state:f,alt:o.alt,stack:o.stack})}}let i;if(a.length===0&&n.size===1&&(i=n),i===void 0){i=new Wm;for(const o of n.elements)ku(o,i)}if(a.length>0&&!vN(i))for(const o of a)i.add(o);return i}s(pN,"computeReachSet");function mN(t,e){if(t instanceof vg&&Zy(e,t.tokenType))return t.target}s(mN,"getReachableTarget");function hN(t,e){let r;for(const n of t.elements)if(e.is(n.alt)===!0){if(r===void 0)r=n.alt;else if(r!==n.alt)return}return r}s(hN,"getUniqueAlt");function bg(t){return{configs:t,edges:{},isAcceptState:!1,prediction:-1}}s(bg,"newDFAState");function qm(t,e,r,n){return n=_g(t,n),e.edges[r.tokenTypeIdx]=n,n}s(qm,"addDFAEdge");function _g(t,e){if(e===Af)return e;const r=e.configs.key,n=t.states[r];return n!==void 0?n:(e.configs.finalize(),t.states[r]=e,e)}s(_g,"addDFAState");function yN(t){const e=new Wm,r=t.transitions.length;for(let n=0;n<r;n++){const i={state:t.transitions[n].target,alt:n,stack:[]};ku(i,e)}return e}s(yN,"computeStartState");function ku(t,e){const r=t.state;if(r.type===tc){if(t.stack.length>0){const a=[...t.stack],o={state:a.pop(),alt:t.alt,stack:a};ku(o,e)}else e.add(t);return}r.epsilonOnlyTransitions||e.add(t);const n=r.transitions.length;for(let a=0;a<n;a++){const i=r.transitions[a],o=gN(t,i);o!==void 0&&ku(o,e)}}s(ku,"closure");function gN(t,e){if(e instanceof TI)return{state:e.target,alt:t.alt,stack:t.stack};if(e instanceof Tg){const r=[...t.stack,e.followState];return{state:e.target,alt:t.alt,stack:r}}}s(gN,"getEpsilonTarget");function vN(t){for(const e of t.elements)if(e.state.type===tc)return!0;return!1}s(vN,"hasConfigInRuleStopState");function TN(t){for(const e of t.elements)if(e.state.type!==tc)return!1;return!0}s(TN,"allConfigsInRuleStopStates");function $N(t){if(TN(t))return!0;const e=RN(t.elements);return AN(e)&&!EN(e)}s($N,"hasConflictTerminatingPrediction");function RN(t){const e=new Map;for(const r of t){const n=Eg(r,!1);let a=e.get(n);a===void 0&&(a={},e.set(n,a)),a[r.alt]=!0}return e}s(RN,"getConflictingAltSets");function AN(t){for(const e of Array.from(t.values()))if(Object.keys(e).length>1)return!0;return!1}s(AN,"hasConflictingAltSet");function EN(t){for(const e of Array.from(t.values()))if(Object.keys(e).length===1)return!0;return!1}s(EN,"hasStateAssociatedWithOneAlt");Du();var ss,CN=(ss=class{constructor(){this.nodeStack=[]}get current(){return this.nodeStack[this.nodeStack.length-1]??this.rootNode}buildRootNode(e){return this.rootNode=new wg(e),this.rootNode.root=this.rootNode,this.nodeStack=[this.rootNode],this.rootNode}buildCompositeNode(e){const r=new Nd;return r.grammarSource=e,r.root=this.rootNode,this.current.content.push(r),this.nodeStack.push(r),r}buildLeafNode(e,r){const n=new Ef(e.startOffset,e.image.length,Tu(e),e.tokenType,!r);return n.grammarSource=r,n.root=this.rootNode,this.current.content.push(n),n}removeNode(e){const r=e.container;if(r){const n=r.content.indexOf(e);n>=0&&r.content.splice(n,1)}}addHiddenNodes(e){const r=[];for(const i of e){const o=new Ef(i.startOffset,i.image.length,Tu(i),i.tokenType,!0);o.root=this.rootNode,r.push(o)}let n=this.current,a=!1;if(n.content.length>0){n.content.push(...r);return}for(;n.container;){const i=n.container.content.indexOf(n);if(i>0){n.container.content.splice(i,0,...r),a=!0;break}n=n.container}a||this.rootNode.content.unshift(...r)}construct(e){const r=this.current;typeof e.$type=="string"&&!e.$infixName&&(this.current.astNode=e),e.$cstNode=r;const n=this.nodeStack.pop();n?.content.length===0&&this.removeNode(n)}},s(ss,"CstNodeBuilder"),ss),os,Sg=(os=class{get hidden(){return!1}get astNode(){const e=typeof this._astNode?.$type=="string"?this._astNode:this.container?.astNode;if(!e)throw new Error("This node has no associated AST element");return e}set astNode(e){this._astNode=e}get text(){return this.root.fullText.substring(this.offset,this.end)}},s(os,"AbstractCstNode"),os),ls,Ef=(ls=class extends Sg{get offset(){return this._offset}get length(){return this._length}get end(){return this._offset+this._length}get hidden(){return this._hidden}get
Line truncated
|
||||
`;case"r":return"\r";case"t":return" ";case"v":return"\v";case"0":return"\0";default:return c}}s(r,"convertEscapeCharacter");function n(c){return c.charAt(0)==="^"?c.substring(1):c}s(n,"convertID"),t.convertID=n;function a(c){return parseInt(c)}s(a,"convertInt"),t.convertInt=a;function i(c){return BigInt(c)}s(i,"convertBigint"),t.convertBigint=i;function o(c){return new Date(c)}s(o,"convertDate"),t.convertDate=o;function u(c){return Number(c)}s(u,"convertNumber"),t.convertNumber=u;function l(c){return c.toLowerCase()==="true"}s(l,"convertBoolean"),t.convertBoolean=l})(or||(or={}));var $e={};kf($e,$h(Lf()));function Ld(){return new Promise(t=>{typeof setImmediate>"u"?setTimeout(t,0):setImmediate(t)})}s(Ld,"delayNextTick");var Fc=0,zN=10;function Dd(){return Fc=performance.now(),new $e.CancellationTokenSource}s(Dd,"startCancelableOperation");function Mg(t){zN=t}s(Mg,"setInterruptionPeriod");var cr=Symbol("OperationCancelled");function fa(t){return t===cr}s(fa,"isOperationCancelled");async function Ye(t){if(t===$e.CancellationToken.None)return;const e=performance.now();if(e-Fc>=zN&&(Fc=e,await Ld(),Fc=performance.now()),t.isCancellationRequested)throw cr}s(Ye,"interruptAndCheck");var $s,Dr=($s=class{constructor(){this.promise=new Promise((e,r)=>{this.resolve=n=>(e(n),this),this.reject=n=>(r(n),this)})}},s($s,"Deferred"),$s),Hr,_T=(Hr=class{constructor(e,r,n,a){this._uri=e,this._languageId=r,this._version=n,this._content=a,this._lineOffsets=void 0}get uri(){return this._uri}get languageId(){return this._languageId}get version(){return this._version}getText(e){if(e){const r=this.offsetAt(e.start),n=this.offsetAt(e.end);return this._content.substring(r,n)}return this._content}update(e,r){for(const n of e)if(Hr.isIncremental(n)){const a=Fg(n.range),i=this.offsetAt(a.start),o=this.offsetAt(a.end);this._content=this._content.substring(0,i)+n.text+this._content.substring(o,this._content.length);const u=Math.max(a.start.line,0),l=Math.max(a.end.line,0);let c=this._lineOffsets;const f=Hm(n.text,!1,i);if(l-u===f.length)for(let p=0,y=f.length;p<y;p++)c[p+u+1]=f[p];else f.length<1e4?c.splice(u+1,l-u,...f):this._lineOffsets=c=c.slice(0,u+1).concat(f,c.slice(l+1));const d=n.text.length-(o-i);if(d!==0)for(let p=u+1+f.length,y=c.length;p<y;p++)c[p]=c[p]+d}else if(Hr.isFull(n))this._content=n.text,this._lineOffsets=void 0;else throw new Error("Unknown change event received");this._version=r}getLineOffsets(){return this._lineOffsets===void 0&&(this._lineOffsets=Hm(this._content,!0)),this._lineOffsets}positionAt(e){e=Math.max(Math.min(e,this._content.length),0);const r=this.getLineOffsets();let n=0,a=r.length;if(a===0)return{line:0,character:e};for(;n<a;){const o=Math.floor((n+a)/2);r[o]>e?a=o:n=o+1}const i=n-1;return e=this.ensureBeforeEOL(e,r[i]),{line:i,character:e-r[i]}}offsetAt(e){const r=this.getLineOffsets();if(e.line>=r.length)return this._content.length;if(e.line<0)return 0;const n=r[e.line];if(e.character<=0)return n;const a=e.line+1<r.length?r[e.line+1]:this._content.length,i=Math.min(n+e.character,a);return this.ensureBeforeEOL(i,n)}ensureBeforeEOL(e,r){for(;e>r&&Gg(this._content.charCodeAt(e-1));)e--;return e}get lineCount(){return this.getLineOffsets().length}static isIncremental(e){const r=e;return r!=null&&typeof r.text=="string"&&r.range!==void 0&&(r.rangeLength===void 0||typeof r.rangeLength=="number")}static isFull(e){const r=e;return r!=null&&typeof r.text=="string"&&r.range===void 0&&r.rangeLength===void 0}},s(Hr,"FullTextDocument"),Hr),_f;(function(t){function e(a,i,o,u){return new _T(a,i,o,u)}s(e,"create"),t.create=e;function r(a,i,o){if(a instanceof _T)return a.update(i,o),a;throw new Error("TextDocument.update: document must be created by TextDocument.create")}s(r,"update"),t.update=r;function n(a,i){const o=a.getText(),u=Sf(i.map(jN),(f,d)=>{const p=f.range.start.line-d.range.start.line;return p===0?f.range.start.character-d.range.start.character:p});let l=0;const c=[];for(const f of u){const d=a.offsetAt(f.range.start);if(d<l)throw new Error("Overlapping edit");d>l&&c.push(o.substring(l,d)),f.newText.length&&c.push(f.newText),l=a.offsetAt(f.range.end)}return c.push(o.substr(l)),c.join("")}s(n,"applyEdits"),t.applyEdits=n})(_f||(_f={}));function Sf(t,e){if(t.length<=1)return t;const r=t.length/2|0,n=t.slice(0,r),a=t.slice(r);Sf(n,e),Sf(a,e);let i=0,o=0,u=0;for(;i<n.length&&o<a.length;)e(n[i],a[o])<=0?t[u++]=n[i++]:t[u++]=a[o++];for(;i<n.length;)t[u++]=n[i++];for(;o<a.length;)t[u++]=a[o++];return t}s(Sf,"mergeSort");function Hm(t,e,r=0){const n=e?[r]:[];for(let a=0;a<t.length;a++){const i=t.charCodeAt(a);Gg(i)&&(i===13&&a+1<t.length&&t.charCodeAt(a+1)===10&&a++,n.push(r+a+1))}return n}s(Hm,"computeLineOffsets");function Gg(t){return t===13||t===10}s(Gg,"isEOL");function Fg(t){const e=t.start,r=t.end;return e.line>r.line||e.line===r.line&&e.character>r.character?{start:r,end:e}:t}s(Fg,"getWellformedRange");function jN(t){const e=Fg(t.range);return e!==t.range?{newText:t.newText,range:e}:t}s(jN,"getWellfo
Line truncated
|
||||
${r}`),this.inline?`{${e}}`:e}toMarkdown(e){return e?.renderTag?.(this)??this.toMarkdownDefault(e)}toMarkdownDefault(e){const r=this.content.toMarkdown(e);if(this.inline){const i=vP(this.name,r,e??{});if(typeof i=="string")return i}let n="";e?.tag==="italic"||e?.tag===void 0?n="*":e?.tag==="bold"?n="**":e?.tag==="bold-italic"&&(n="***");let a=`${n}@${this.name}${n}`;return this.content.inlines.length===1?a=`${a} — ${r}`:this.content.inlines.length>1&&(a=`${a}
|
||||
${r}`),this.inline?`{${a}}`:a}},s(Qs,"JSDocTagImpl"),Qs);function vP(t,e,r){if(t==="linkplain"||t==="linkcode"||t==="link"){const n=e.indexOf(" ");let a=e;if(n>0){const o=Pf(e,n);a=e.substring(o),e=e.substring(0,n)}return(t==="linkcode"||t==="link"&&r.link==="code")&&(a=`\`${a}\``),r.renderLink?.(e,a)??TP(e,a)}}s(vP,"renderInlineTag");function TP(t,e){try{return It.parse(t,!0),`[${e}](${t})`}catch{return t}}s(TP,"renderLinkDefault");var eo,Jm=(eo=class{constructor(e,r){this.inlines=e,this.range=r}toString(){let e="";for(let r=0;r<this.inlines.length;r++){const n=this.inlines[r],a=this.inlines[r+1];e+=n.toString(),a&&a.range.start.line>n.range.start.line&&(e+=`
|
||||
`)}return e}toMarkdown(e){let r="";for(let n=0;n<this.inlines.length;n++){const a=this.inlines[n],i=this.inlines[n+1];r+=a.toMarkdown(e),i&&i.range.start.line>a.range.start.line&&(r+=`
|
||||
`)}return r}},s(eo,"JSDocTextImpl"),eo),to,$P=(to=class{constructor(e,r){this.text=e,this.range=r}toString(){return this.text}toMarkdown(){return this.text}},s(to,"JSDocLineImpl"),to);function Zm(t){return t.endsWith(`
|
||||
`)?`
|
||||
`:`
|
||||
|
||||
`}s(Zm,"fillNewlines");var ro,RP=(ro=class{constructor(e){this.indexManager=e.shared.workspace.IndexManager,this.commentProvider=e.documentation.CommentProvider}getDocumentation(e){const r=this.commentProvider.getComment(e);if(r&&Xg(r))return Yg(r).toMarkdown({renderLink:s((a,i)=>this.documentationLinkRenderer(e,a,i),"renderLink"),renderTag:s(a=>this.documentationTagRenderer(e,a),"renderTag")})}documentationLinkRenderer(e,r,n){const a=this.findNameInLocalSymbols(e,r)??this.findNameInGlobalScope(e,r);if(a&&a.nameSegment){const i=a.nameSegment.range.start.line+1,o=a.nameSegment.range.start.character+1,u=a.documentUri.with({fragment:`L${i},${o}`});return`[${n}](${u.toString()})`}else return}documentationTagRenderer(e,r){}findNameInLocalSymbols(e,r){const a=Vt(e).localSymbols;if(!a)return;let i=e;do{const u=a.getStream(i).find(l=>l.name===r);if(u)return u;i=i.$container}while(i)}findNameInGlobalScope(e,r){return this.indexManager.allElements().find(a=>a.name===r)}},s(ro,"JSDocDocumentationProvider"),ro),no,AP=(no=class{constructor(e){this.grammarConfig=()=>e.parser.GrammarConfig}getComment(e){return Kg(e)?e.$comment:Zh(e.$cstNode,this.grammarConfig().multilineCommentRules)?.text}},s(no,"DefaultCommentProvider"),no),ao,EP=(ao=class{constructor(e){this.syncParser=e.parser.LangiumParser}parse(e,r){return Promise.resolve(this.syncParser.parse(e))}},s(ao,"DefaultAsyncParser"),ao),io,gB=(io=class{constructor(e){this.threadCount=8,this.terminationDelay=200,this.workerPool=[],this.queue=[],this.hydrator=e.serializer.Hydrator}initializeWorkers(){for(;this.workerPool.length<this.threadCount;){const e=this.createWorker();e.onReady(()=>{if(this.queue.length>0){const r=this.queue.shift();r&&(e.lock(),r.resolve(e))}}),this.workerPool.push(e)}}async parse(e,r){const n=await this.acquireParserWorker(r),a=new Dr;let i;const o=r.onCancellationRequested(()=>{i=setTimeout(()=>{this.terminateWorker(n)},this.terminationDelay)});return n.parse(e).then(u=>{const l=this.hydrator.hydrate(u);a.resolve(l)}).catch(u=>{a.reject(u)}).finally(()=>{o.dispose(),clearTimeout(i)}),a.promise}terminateWorker(e){e.terminate();const r=this.workerPool.indexOf(e);r>=0&&this.workerPool.splice(r,1)}async acquireParserWorker(e){this.initializeWorkers();for(const n of this.workerPool)if(n.ready)return n.lock(),n;const r=new Dr;return e.onCancellationRequested(()=>{const n=this.queue.indexOf(r);n>=0&&this.queue.splice(n,1),r.reject(cr)}),this.queue.push(r),r.promise}},s(io,"AbstractThreadedAsyncParser"),io),so,vB=(so=class{get ready(){return this._ready}get onReady(){return this.onReadyEmitter.event}constructor(e,r,n,a){this.onReadyEmitter=new Gd.Emitter,this.deferred=new Dr,this._ready=!0,this._parsing=!1,this.sendMessage=e,this._terminate=a,r(i=>{const o=i;this.deferred.resolve(o),this.unlock()}),n(i=>{this.deferred.reject(i),this.unlock()})}terminate(){this.deferred.reject(cr),this._terminate()}lock(){this._ready=!1}unlock(){this._parsing=!1,this._ready=!0,this.onReadyEmitter.fire()}parse(e){if(this._parsing)throw new Error("Parser worker is busy");return this._parsing=!0,this.deferred=new Dr,this.sendMessage(e),this.deferred.promise}},s(so,"ParserWorker"),so),oo,CP=(oo=class{constructor(){this.previousTokenSource=new $e.CancellationTokenSource,this.writeQueue=[],this.readQueue=[],this.done=!0}write(e){this.cancelWrite();const r=Dd();return this.previousTokenSource=r,this.enqueue(this.writeQueue,e,r.token)}read(e){return this.enqueue(this.readQueue,e)}enqueue(e,r,n=$e.CancellationToken.None){const a=new Dr,i={action:r,deferred:a,cancellationToken:n};return e.push(i),this.performNextOperation(),a.promise}async performNextOperation(){if(!this.done)return;const e=[];if(this.writeQueue.length>0)e.push(this.writeQueue.shift());else if(this.readQueue.length>0)e.push(...this.readQueue.splice(0,this.readQueue.length));else return;this.done=!1,await Promise.all(e.map(async({action:r,deferred:n,cancellationToken:a})=>{try{const i=await Promise.resolve().then(()=>r(a));n.resolve(i)}catch(i){fa(i)?n.resolve(void 0):n.reject(i)}})),this.done=!0,this.performNextOperation()}cancelWrite(){this.previousTokenSource.cancel()}},s(oo,"DefaultWorkspaceLock"),oo),lo,bP=(lo=class{constructor(e){this.grammarElementIdMap=new wf,this.tokenTypeIdMap=new wf,this.grammar=e.Grammar,this.lexer=e.parser.Lexer,this.linker=e.references.Linker}dehydrate(e){return{lexerErrors:e.lexerErrors,lexerReport:e.lexerReport?this.dehydrateLexerReport(e.lexerReport):void 0,parserErrors:e.parserErrors.map(r=>({...r,message:r.message})),value:this.dehydrateAstNode(e.value,this.createDehyrationContext(e.value))}}dehydrateLexerReport(e){return e}createDehyrationContext(e){const r=new Map,n=new Map;for(const a of qt(e))r.set(a,{});if(e.$cstNode)for(const a of Jo(e.$cstNode))n.set(a,{});return{astNodes:r,cstNodes:n}}dehydrateAstNode(e,r){const n=r.astNodes.get(e);n.$type=e.$type,n.$containerIndex=e.$containerIndex,n.$containerProperty=e.$containerProperty,e.$cstNode!==void 0&&(n.$cstNode=this.dehydrateCstN
Line truncated
|
||||
`.includes(e[r-1])}matchWhitespace(e,r,n,a){this.whitespaceRegExp.lastIndex=r;const i=this.whitespaceRegExp.exec(e);return{currIndentLevel:i?.[0].length??0,prevIndentLevel:this.indentationStack.at(-1),match:i}}createIndentationTokenInstance(e,r,n,a){const i=this.getLineNumber(r,a);return Zu(e,n,a,a+n.length,i,i,1,n.length)}getLineNumber(e,r){return e.substring(0,r).split(/\r\n|\r|\n/).length}indentMatcher(e,r,n,a){if(!this.isStartOfLine(e,r))return null;const{currIndentLevel:i,prevIndentLevel:o,match:u}=this.matchWhitespace(e,r,n,a);return i<=o?null:(this.indentationStack.push(i),u)}dedentMatcher(e,r,n,a){if(!this.isStartOfLine(e,r))return null;const{currIndentLevel:i,prevIndentLevel:o,match:u}=this.matchWhitespace(e,r,n,a);if(i>=o)return null;const l=this.indentationStack.lastIndexOf(i);if(l===-1)return this.diagnostics.push({severity:"error",message:`Invalid dedent level ${i} at offset: ${r}. Current indentation stack: ${this.indentationStack}`,offset:r,length:u?.[0]?.length??0,line:this.getLineNumber(e,r),column:1}),null;const c=this.indentationStack.length-l-1,f=e.substring(0,r).match(/[\r\n]+$/)?.[0].length??1;for(let d=0;d<c;d++){const p=this.createIndentationTokenInstance(this.dedentTokenType,e,"",r-(f-1));n.push(p),this.indentationStack.pop()}return null}buildTerminalToken(e){const r=super.buildTerminalToken(e),{indentTokenName:n,dedentTokenName:a,whitespaceTokenName:i}=this.options;return r.name===n?this.indentTokenType:r.name===a?this.dedentTokenType:r.name===i?Xa({name:i,pattern:this.whitespaceRegExp,group:pt.SKIPPED}):r}flushRemainingDedents(e){const r=[];for(;this.indentationStack.length>1;)r.push(this.createIndentationTokenInstance(this.dedentTokenType,e,"",e.length)),this.indentationStack.pop();return this.indentationStack=[0],r}},s(uo,"IndentationAwareTokenBuilder"),uo),co,TB=(co=class extends Hg{constructor(e){if(super(e),e.parser.TokenBuilder instanceof SP)this.indentationTokenBuilder=e.parser.TokenBuilder;else throw new Error("IndentationAwareLexer requires an accompanying IndentationAwareTokenBuilder")}tokenize(e,r=qg){const n=super.tokenize(e),a=n.report;r?.mode==="full"&&n.tokens.push(...a.remainingDedents),a.remainingDedents=[];const{indentTokenType:i,dedentTokenType:o}=this.indentationTokenBuilder,u=i.tokenTypeIdx,l=o.tokenTypeIdx,c=[],f=n.tokens.length-1;for(let d=0;d<f;d++){const p=n.tokens[d],y=n.tokens[d+1];if(p.tokenTypeIdx===u&&y.tokenTypeIdx===l){d++;continue}c.push(p)}return f>=0&&c.push(n.tokens[f]),n.tokens=c,n}},s(co,"IndentationAwareLexer"),co),nv={};en(nv,{AstUtils:()=>_h,BiMap:()=>wf,Cancellation:()=>$e,ContextCache:()=>Md,CstUtils:()=>Eh,DONE_RESULT:()=>ct,Deferred:()=>Dr,Disposable:()=>Gn,DisposableCache:()=>xd,DocumentCache:()=>XN,EMPTY_STREAM:()=>Vo,ErrorWithLocation:()=>Uf,GrammarUtils:()=>ry,MultiMap:()=>xr,OperationCancelled:()=>cr,Reduction:()=>vu,RegExpUtils:()=>ay,SimpleCache:()=>Bg,StreamImpl:()=>ur,TreeStreamImpl:()=>qo,URI:()=>It,UriTrie:()=>zg,UriUtils:()=>dt,WorkspaceCache:()=>Ug,assertCondition:()=>ny,assertUnreachable:()=>tn,delayNextTick:()=>Ld,interruptAndCheck:()=>Ye,isOperationCancelled:()=>fa,loadGrammarFromJson:()=>Ze,setInterruptionPeriod:()=>Mg,startCancelableOperation:()=>Dd,stream:()=>de});kf(nv,Gd);var fo,wP=(fo=class{stat(e){throw new Error("No file system is available.")}statSync(e){throw new Error("No file system is available.")}async exists(){return!1}existsSync(){return!1}readBinary(){throw new Error("No file system is available.")}readBinarySync(){throw new Error("No file system is available.")}readFile(){throw new Error("No file system is available.")}readFileSync(){throw new Error("No file system is available.")}async readDirectory(){return[]}readDirectorySync(){return[]}},s(fo,"EmptyFileSystemProvider"),fo),st={fileSystemProvider:s(()=>new wP,"fileSystemProvider")},$B={Grammar:s(()=>{},"Grammar"),LanguageMetaData:s(()=>({caseInsensitive:!1,fileExtensions:[".langium"],languageId:"langium"}),"LanguageMetaData")},RB={AstReflection:s(()=>new qh,"AstReflection")};function IP(){const t=re(Je(st),RB),e=re(Xe({shared:t}),$B);return t.ServiceRegistry.register(e),e}s(IP,"createMinimalGrammarServices");function Ze(t){const e=IP(),r=e.serializer.JsonSerializer.deserialize(t);return e.shared.workspace.LangiumDocumentFactory.fromModel(r,It.parse(`memory:/${r.name??"grammar"}.langium`)),r}s(Ze,"loadGrammarFromJson");kf(z$,nv);var po,AB=(po=class{constructor(e){this.activeCategories=new Set,this.allCategories=new Set(["validating","parsing","linking"]),this.activeCategories=e??new Set(this.allCategories),this.records=new xr}isActive(e){return this.activeCategories.has(e)}start(...e){e?e.forEach(r=>this.activeCategories.add(r)):this.activeCategories=new Set(this.allCategories)}stop(...e){e?e.forEach(r=>this.activeCategories.delete(r)):this.activeCategories.clear()}createTask(e,r){if(!this.isActive(e))throw new Error(`Category "${e}" is not active.`);return console.log(`Creating profiling task for '${e}.${r}'.`),new NP(n=>this.records.add(e,this.dumpLine truncated
|
||||
`)}}},s(yo,"AbstractMermaidValueConverter"),yo),go,pl=(go=class extends vr{runCustomConverter(e,r,n){}},s(go,"CommonValueConverter"),go),vo,$t=(vo=class extends Od{constructor(e){super(),this.keywords=new Set(e)}buildKeywordTokens(e,r,n){const a=super.buildKeywordTokens(e,r,n);return a.forEach(i=>{this.keywords.has(i.name)&&i.PATTERN!==void 0&&(i.PATTERN=new RegExp(i.PATTERN.toString()+"(?:(?=%%)|(?!\\S))"))}),a}},s(vo,"AbstractMermaidTokenBuilder"),vo),To;To=class extends $t{},s(To,"CommonTokenBuilder");var $o,zU=($o=class extends $t{constructor(){super(["radar-beta"])}},s($o,"RadarTokenBuilder"),$o),kP={parser:{TokenBuilder:s(()=>new zU,"TokenBuilder"),ValueConverter:s(()=>new pl,"ValueConverter")}};function OP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),wU,kP);return e.ServiceRegistry.register(r),{shared:e,Radar:r}}s(OP,"createRadarServices");var Ro,jU=(Ro=class extends $t{constructor(){super(["railroad-beta"])}},s(Ro,"RailroadTokenBuilder"),Ro),w$=s(t=>{const e=t.slice(1,-1);let r="";for(let n=0;n<e.length;n++){const a=e[n];if(a==="\\"&&n+1<e.length){n++;const i=e[n];switch(i){case"n":r+=`
|
||||
`;break;case"r":r+="\r";break;case"t":r+=" ";break;default:r+=i}continue}r+=a}return r},"decodeEscapedString"),Ao,BU=(Ao=class extends vr{runConverter(e,r,n){const a=super.runConverter(e,r,n);if(e.name==="TITLE"&&typeof a=="string"){const i=a.trim();if(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))return w$(i)}return a}runCustomConverter(e,r,n){if(e.name==="RR_STRING")return w$(r)}},s(Ao,"RailroadValueConverter"),Ao),LP={parser:{TokenBuilder:s(()=>new jU,"TokenBuilder"),ValueConverter:s(()=>new BU,"ValueConverter")}};function DP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),PU,LP);return e.ServiceRegistry.register(r),{shared:e,Railroad:r}}s(DP,"createRailroadServices");var Eo,UU=(Eo=class extends $t{constructor(){super(["railroad-ebnf-beta"])}},s(Eo,"RailroadEbnfTokenBuilder"),Eo),I$=s(t=>{const e=t.slice(1,-1);let r="";for(let n=0;n<e.length;n++){const a=e[n];if(a==="\\"&&n+1<e.length){n++;const i=e[n];switch(i){case"n":r+=`
|
||||
`;break;case"r":r+="\r";break;case"t":r+=" ";break;default:r+=i}continue}r+=a}return r},"decodeEscapedString"),Co,KU=(Co=class extends vr{runConverter(e,r,n){const a=super.runConverter(e,r,n);if(e.name==="TITLE"&&typeof a=="string"){const i=a.trim();if(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))return I$(i)}return a}runCustomConverter(e,r,n){if(e.name==="EBNF_STRING")return I$(r);if(e.name==="EBNF_SPECIAL_SEQUENCE")return r.slice(1,-1).trim()}},s(Co,"RailroadEbnfValueConverter"),Co),xP={parser:{TokenBuilder:s(()=>new UU,"TokenBuilder"),ValueConverter:s(()=>new KU,"ValueConverter")}};function MP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),NU,xP);return e.ServiceRegistry.register(r),{shared:e,RailroadEbnf:r}}s(MP,"createRailroadEbnfServices");var bo,WU=(bo=class extends $t{constructor(){super(["railroad-abnf-beta"])}},s(bo,"RailroadAbnfTokenBuilder"),bo),_o,VU=(_o=class extends vr{runConverter(e,r,n){const a=super.runConverter(e,r,n);if(e.name==="TITLE"&&typeof a=="string"){const i=a.trim();if(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))return i.slice(1,-1)}return a}runCustomConverter(e,r,n){if(e.name==="ABNF_STRING")return r.slice(1,-1)}},s(_o,"RailroadAbnfValueConverter"),_o),GP={parser:{TokenBuilder:s(()=>new WU,"TokenBuilder"),ValueConverter:s(()=>new VU,"ValueConverter")}};function FP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),IU,GP);return e.ServiceRegistry.register(r),{shared:e,RailroadAbnf:r}}s(FP,"createRailroadAbnfServices");var So,qU=(So=class extends $t{constructor(){super(["railroad-peg-beta"])}},s(So,"RailroadPegTokenBuilder"),So),N$=s(t=>{const e=t.slice(1,-1);let r="";for(let n=0;n<e.length;n++){const a=e[n];if(a==="\\"&&n+1<e.length){n++;const i=e[n];switch(i){case"n":r+=`
|
||||
`;break;case"r":r+="\r";break;case"t":r+=" ";break;default:r+=i}continue}r+=a}return r},"decodeEscapedString"),wo,HU=(wo=class extends vr{runConverter(e,r,n){const a=super.runConverter(e,r,n);if(e.name==="TITLE"&&typeof a=="string"){const i=a.trim();if(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))return N$(i)}return a}runCustomConverter(e,r,n){if(e.name==="PEG_STRING")return N$(r)}},s(wo,"RailroadPegValueConverter"),wo),zP={parser:{TokenBuilder:s(()=>new qU,"TokenBuilder"),ValueConverter:s(()=>new HU,"ValueConverter")}};function jP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),kU,zP);return e.ServiceRegistry.register(r),{shared:e,RailroadPeg:r}}s(jP,"createRailroadPegServices");var Io,YU=(Io=class extends $t{constructor(){super(["treemap"])}},s(Io,"TreemapTokenBuilder"),Io),XU=/classDef\s+([A-Z_a-z]\w+)(?:\s+([^\n\r;]*))?;?/,No,JU=(No=class extends vr{runCustomConverter(e,r,n){if(e.name==="NUMBER2")return parseFloat(r.replace(/,/g,""));if(e.name==="SEPARATOR")return r.substring(1,r.length-1);if(e.name==="STRING2")return r.substring(1,r.length-1);if(e.name==="INDENTATION")return r.length;if(e.name==="ClassDef"){if(typeof r!="string")return r;const a=XU.exec(r);if(a)return{$type:"ClassDefStatement",className:a[1],styleText:a[2]||void 0}}}},s(No,"TreemapValueConverter"),No);function BP(t){const e=t.validation.TreemapValidator,r=t.validation.ValidationRegistry;if(r){const n={Treemap:e.checkSingleRoot.bind(e)};r.register(n,e)}}s(BP,"registerValidationChecks");var Po,ZU=(Po=class{checkSingleRoot(e,r){let n;for(const a of e.TreemapRows)a.item&&(n===void 0&&a.indent===void 0?n=0:a.indent===void 0?r("error","Multiple root nodes are not allowed in a treemap.",{node:a,property:"item"}):n!==void 0&&n>=parseInt(a.indent,10)&&r("error","Multiple root nodes are not allowed in a treemap.",{node:a,property:"item"}))}},s(Po,"TreemapValidator"),Po),UP={parser:{TokenBuilder:s(()=>new YU,"TokenBuilder"),ValueConverter:s(()=>new JU,"ValueConverter")},validation:{TreemapValidator:s(()=>new ZU,"TreemapValidator")}};function KP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),OU,UP);return e.ServiceRegistry.register(r),BP(r),{shared:e,Treemap:r}}s(KP,"createTreemapServices");var ko,QU=(ko=class extends vr{runCustomConverter(e,r,n){if(e.name.toUpperCase()==="LINK_LABEL")return r.substring(1).trim()}},s(ko,"WardleyValueConverter"),ko),WP={parser:{ValueConverter:s(()=>new QU,"ValueConverter")}};function VP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),DU,WP);return e.ServiceRegistry.register(r),{shared:e,Wardley:r}}s(VP,"createWardleyServices");var Oo,eK=(Oo=class extends $t{constructor(){super(["cynefin-beta"])}},s(Oo,"CynefinTokenBuilder"),Oo),qP={parser:{TokenBuilder:s(()=>new eK,"TokenBuilder"),ValueConverter:s(()=>new pl,"ValueConverter")}};function HP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),AU,qP);return e.ServiceRegistry.register(r),{shared:e,Cynefin:r}}s(HP,"createCynefinServices");var Lo,tK=(Lo=class extends $t{constructor(){super(["gitGraph"])}},s(Lo,"GitGraphTokenBuilder"),Lo),YP={parser:{TokenBuilder:s(()=>new tK,"TokenBuilder"),ValueConverter:s(()=>new pl,"ValueConverter")}};function XP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),CU,YP);return e.ServiceRegistry.register(r),{shared:e,GitGraph:r}}s(XP,"createGitGraphServices");var Do,rK=(Do=class extends $t{constructor(){super(["info","showInfo"])}},s(Do,"InfoTokenBuilder"),Do),JP={parser:{TokenBuilder:s(()=>new rK,"TokenBuilder"),ValueConverter:s(()=>new pl,"ValueConverter")}};function ZP(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),bU,JP);return e.ServiceRegistry.register(r),{shared:e,Info:r}}s(ZP,"createInfoServices");var xo,nK=(xo=class extends $t{constructor(){super(["packet"])}},s(xo,"PacketTokenBuilder"),xo),QP={parser:{TokenBuilder:s(()=>new nK,"TokenBuilder"),ValueConverter:s(()=>new pl,"ValueConverter")}};function ek(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),_U,QP);return e.ServiceRegistry.register(r),{shared:e,Packet:r}}s(ek,"createPacketServices");var Mo,aK=(Mo=class extends $t{constructor(){super(["pie","showData"])}},s(Mo,"PieTokenBuilder"),Mo),Go,iK=(Go=class extends vr{runCustomConverter(e,r,n){if(e.name==="PIE_SECTION_LABEL")return r.replace(/"/g,"").trim()}},s(Go,"PieValueConverter"),Go),tk={parser:{TokenBuilder:s(()=>new aK,"TokenBuilder"),ValueConverter:s(()=>new iK,"ValueConverter")}};function rk(t=st){const e=re(Je(t),Tt),r=re(Xe({shared:e}),SU,tk);return e.ServiceRegistry.register(r),{shared:e,Pie:r}}s(rk,"createPieServices");var Fo,sK=(Fo=class extends vr{runCustomConverter(e,r,n){if(e.name==="INDENTATION")return r?.length||0;if(e.name==="QUOTED_NAME")return r.substring(1,r.length-1);if(e.name==="BARE_NAME")return r.replace(/[\t ]+$/,"");if(e.name==="CLASS_ANNOTATION")return r.trim().substring(3).trim();if(e.name==="ICON_ANNOTATION"){const a=r.trim();return a.substring(5,a.length-1)}if(e.name==="DESC_ANNOTATION")return r.trim().substring(2).trim()}},s(Fo,"TreeViewValueConverter"),Fo),zo,oK=(zo=class extends
Line truncated
|
||||
`),n=e.parserErrors.map(a=>{const i=a.token.startLine!==void 0&&!isNaN(a.token.startLine)?a.token.startLine:"?",o=a.token.startColumn!==void 0&&!isNaN(a.token.startColumn)?a.token.startColumn:"?";return`Parse error on line ${i}, column ${o}: ${a.message}`}).join(`
|
||||
`);super(`Parsing failed: ${r} ${n}`),this.result=e}},s(Wo,"MermaidParseError"),Wo);const hK=Object.freeze(Object.defineProperty({__proto__:null,InfoModule:JP,createInfoServices:ZP},Symbol.toStringTag,{value:"Module"})),yK=Object.freeze(Object.defineProperty({__proto__:null,PacketModule:QP,createPacketServices:ek},Symbol.toStringTag,{value:"Module"})),gK=Object.freeze(Object.defineProperty({__proto__:null,PieModule:tk,createPieServices:rk},Symbol.toStringTag,{value:"Module"})),vK=Object.freeze(Object.defineProperty({__proto__:null,TreeViewModule:nk,createTreeViewServices:ak},Symbol.toStringTag,{value:"Module"})),TK=Object.freeze(Object.defineProperty({__proto__:null,ArchitectureModule:ik,createArchitectureServices:sk},Symbol.toStringTag,{value:"Module"})),$K=Object.freeze(Object.defineProperty({__proto__:null,GitGraphModule:YP,createGitGraphServices:XP},Symbol.toStringTag,{value:"Module"})),RK=Object.freeze(Object.defineProperty({__proto__:null,EventModelingModule:lk,createEventModelingServices:uk},Symbol.toStringTag,{value:"Module"})),AK=Object.freeze(Object.defineProperty({__proto__:null,RadarModule:kP,createRadarServices:OP},Symbol.toStringTag,{value:"Module"})),EK=Object.freeze(Object.defineProperty({__proto__:null,RailroadModule:LP,createRailroadServices:DP},Symbol.toStringTag,{value:"Module"})),CK=Object.freeze(Object.defineProperty({__proto__:null,RailroadEbnfModule:xP,createRailroadEbnfServices:MP},Symbol.toStringTag,{value:"Module"})),bK=Object.freeze(Object.defineProperty({__proto__:null,RailroadAbnfModule:GP,createRailroadAbnfServices:FP},Symbol.toStringTag,{value:"Module"})),_K=Object.freeze(Object.defineProperty({__proto__:null,RailroadPegModule:zP,createRailroadPegServices:jP},Symbol.toStringTag,{value:"Module"})),SK=Object.freeze(Object.defineProperty({__proto__:null,TreemapModule:UP,createTreemapServices:KP},Symbol.toStringTag,{value:"Module"})),wK=Object.freeze(Object.defineProperty({__proto__:null,WardleyModule:WP,createWardleyServices:VP},Symbol.toStringTag,{value:"Module"})),IK=Object.freeze(Object.defineProperty({__proto__:null,CynefinModule:qP,createCynefinServices:HP},Symbol.toStringTag,{value:"Module"}));export{mK as M,MP as a,FP as b,DP as c,jP as d,NB as i,pK as p};
|
||||
@@ -0,0 +1,62 @@
|
||||
import{p as xt}from"./chunk-JWPE2WC7.js";import{s as gt,g as $t,o as bt,n as wt,a as Ct,b as vt,_ as s,l as O,D as Dt,d as kt,p as At,A as U,y as Q,B as Tt,E as ot}from"./mermaid.core.js";import{p as Bt}from"./cynefin-OW5HDTMX.js";import"./index.js";var rt=s(()=>({domains:new Map,transitions:[]}),"createDefaultData"),H=rt(),St=s(()=>H.domains,"getDomains"),Mt=s(()=>H.transitions,"getTransitions"),zt=s(t=>{if(t)for(const e of t){const n=e.domain,a=(e.items??[]).map(c=>({label:c.label}));H.domains.set(n,{name:n,items:a})}},"setDomains"),Lt=s(t=>{t&&(H.transitions=t.filter(e=>e.from===e.to?(O.warn(`Cynefin: self-loop transition on domain "${e.from}" is not meaningful and will be skipped.`),!1):!0).map(e=>({from:e.from,to:e.to,label:e.label||void 0})))},"setTransitions"),Nt=s(()=>U({...Tt.cynefin,...Q().cynefin}),"getConfig"),Pt=s(()=>{At(),H=rt()},"clear"),Y={getDomains:St,getTransitions:Mt,setDomains:zt,setTransitions:Lt,getConfig:Nt,clear:Pt,setAccTitle:vt,getAccTitle:Ct,setDiagramTitle:wt,getDiagramTitle:bt,getAccDescription:$t,setAccDescription:gt},It=s(t=>{xt(t,Y),Y.setDomains(t.domains),Y.setTransitions(t.transitions)},"populate"),Wt={parse:s(async t=>{const e=await Bt("cynefin",t);O.debug(e),It(e)},"parse")};function V(t){let e=t+1831565813|0;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}s(V,"seededRandom");function st(t){let e=0;for(let n=0;n<t.length;n++){const a=t.charCodeAt(n);e=(e<<5)-e+a,e|=0}return e}s(st,"hashString");function it(t,e){return typeof t=="number"&&Number.isFinite(t)&&t!==0?t:st(e)}s(it,"resolveSeed");function ct(t,e,n,a){const c=t/2,m=a??t*.015,v=7,W=e/v,d=[];for(let o=0;o<=v;o++){const p=V(n+o*17)*m*2-m;d.push({x:c+p,y:o*W})}let D=`M${d[0].x},${d[0].y}`;for(let o=0;o<d.length-1;o++){const p=d[o],i=d[o+1],f=(p.y+i.y)/2,b=o%2===0?1:-1,h=m*1.5*b*V(n+o*31+7),R=p.x+h,_=f,E=i.x-h;D+=` C${R},${_} ${E},${f} ${i.x},${i.y}`}return D}s(ct,"generateFoldPath");function lt(t,e,n,a){const c=e/2,m=a??e*.015,v=7,W=t/v,d=[];for(let o=0;o<=v;o++){const p=V(n+o*23)*m*2-m;d.push({x:o*W,y:c+p})}let D=`M${d[0].x},${d[0].y}`;for(let o=0;o<d.length-1;o++){const p=d[o],i=d[o+1],f=(p.x+i.x)/2,b=o%2===0?1:-1,h=m*1.5*b*V(n+o*37+11),R=f,_=p.y+h,E=f,z=i.y-h;D+=` C${R},${_} ${E},${z} ${i.x},${i.y}`}return D}s(lt,"generateHorizontalBoundary");function dt(t,e){const n=t/2,a=e*.5,c=e,m=t*.03;return[`M${n},${a}`,`C${n+m},${a+(c-a)*.2}`,`${n-m*1.5},${a+(c-a)*.55}`,`${n+m*.5},${a+(c-a)*.75}`,`C${n-m},${a+(c-a)*.85}`,`${n+m*.3},${a+(c-a)*.95}`,`${n},${c}`].join(" ")}s(dt,"generateCliffPath");function ft(t,e,n,a){return[`M${t-n},${e}`,`A${n},${a} 0 1,1 ${t+n},${e}`,`A${n},${a} 0 1,1 ${t-n},${e}`,"Z"].join(" ")}s(ft,"generateConfusionPath");var at={complex:{model:"Probe → Sense → Respond",practice:"Emergent Practices"},complicated:{model:"Sense → Analyse → Respond",practice:"Good Practices"},clear:{model:"Sense → Categorise → Respond",practice:"Best Practices"},chaotic:{model:"Act → Sense → Respond",practice:"Novel Practices"},confusion:{model:"",practice:"Disorder"}},Rt=s((t,e)=>{const n=t/2,a=e/2;return{complex:{cx:n/2,cy:a/2,x:0,y:0,w:n,h:a},complicated:{cx:n+n/2,cy:a/2,x:n,y:0,w:n,h:a},chaotic:{cx:n/2,cy:a+a/2,x:0,y:a,w:n,h:a},clear:{cx:n+n/2,cy:a+a/2,x:n,y:a,w:n,h:a},confusion:{cx:n,cy:a,x:n*.7,y:a*.7,w:n*.6,h:a*.6}}},"getDomainLayouts"),_t=s(()=>{const t=ot(),e=Q();return U(t,e.themeVariables).cynefin},"getCynefinDomainColors"),q=3,Et=s((t,e,n,a)=>{const c=a.db,m=c.getDomains(),v=c.getTransitions(),W=c.getDiagramTitle(),d=c.getAccTitle(),D=c.getAccDescription(),o=c.getConfig(),p=_t();O.debug("Rendering Cynefin diagram");const i=o.width,f=o.height,b=o.padding,h=o.showDomainDescriptions,R=o.boundaryAmplitude,_=i+b*2,E=f+b*2,z={complex:p.complexBg,complicated:p.complicatedBg,clear:p.clearBg,chaotic:p.chaoticBg,confusion:p.confusionBg},k=Dt(e);kt(k,E,_,o.useMaxWidth??!0),k.attr("viewBox",`0 0 ${_} ${E}`),d&&k.append("title").text(d),D&&k.append("desc").text(D);const A=k.append("g").attr("transform",`translate(${b}, ${b})`),F=Rt(i,f),Z=it(o.seed,e),mt=A.append("g").attr("class","cynefin-backgrounds"),X=["complex","complicated","chaotic","clear"];for(const l of X){const r=F[l];mt.append("rect").attr("class","cynefinDomain").attr("x",r.x).attr("y",r.y).attr("width",r.w).attr("height",r.h).attr("fill",z[l]).attr("fill-opacity",.4).attr("stroke","none")}const j=A.append("g").attr("class","cynefin-boundaries");j.append("path").attr("class","cynefinBoundary").attr("d",ct(i,f,Z,R)).attr("fill","none"),j.append("path").attr("class","cynefinBoundary").attr("d",lt(i,f,Z+100,R)).attr("fill","none"),j.append("path").attr("class","cynefinCliff").attr("d",dt(i,f)).attr("fill","none");const pt=i*.15,yt=f*.15;A.append("path").attr("class","cynefinConfusion").attr("d",ft(i/2,f/2,pt,yt)).attr("fill",z.confusion).attr("fill-opacity",.5);const J=A.append("g").attr("class","cynefin-labels");for(const l of X){const r=F[l];J.append("text").attr("class","cynefinDomainLabel").attr("x"Line truncated
|
||||
.cynefinDomain {
|
||||
stroke: none;
|
||||
}
|
||||
.cynefinDomainLabel {
|
||||
font-size: ${t.domainFontSize}px;
|
||||
font-weight: bold;
|
||||
fill: ${t.labelColor};
|
||||
}
|
||||
.cynefinSubtitle {
|
||||
font-size: ${t.itemFontSize-1}px;
|
||||
fill: ${t.textColor};
|
||||
font-style: italic;
|
||||
}
|
||||
.cynefinItem {
|
||||
fill-opacity: 0.95;
|
||||
stroke: ${t.boundaryColor};
|
||||
stroke-width: 1;
|
||||
}
|
||||
.cynefinItemText {
|
||||
font-size: ${t.itemFontSize}px;
|
||||
fill: ${t.textColor};
|
||||
}
|
||||
.cynefinItemOverflow {
|
||||
fill-opacity: 0.6;
|
||||
stroke: ${t.boundaryColor};
|
||||
stroke-width: 1;
|
||||
stroke-dasharray: 3 2;
|
||||
}
|
||||
.cynefinBoundary {
|
||||
stroke: ${t.boundaryColor};
|
||||
stroke-width: ${t.boundaryWidth};
|
||||
stroke-dasharray: 6 3;
|
||||
}
|
||||
.cynefinCliff {
|
||||
stroke: ${t.cliffColor};
|
||||
stroke-width: ${t.cliffWidth};
|
||||
}
|
||||
.cynefinConfusion {
|
||||
stroke: ${t.boundaryColor};
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: 4 2;
|
||||
}
|
||||
.cynefinArrowLine {
|
||||
stroke: ${t.arrowColor};
|
||||
stroke-width: ${t.arrowWidth};
|
||||
fill: none;
|
||||
}
|
||||
.cynefinArrowHead {
|
||||
fill: ${t.arrowColor};
|
||||
stroke: none;
|
||||
}
|
||||
.cynefinArrowLabel {
|
||||
font-size: ${t.itemFontSize-1}px;
|
||||
fill: ${t.textColor};
|
||||
}
|
||||
.cynefinTitle {
|
||||
font-size: ${t.domainFontSize+2}px;
|
||||
font-weight: bold;
|
||||
fill: ${t.labelColor};
|
||||
}
|
||||
`},"styles"),Gt=Ht,qt={parser:Wt,db:Y,renderer:Ft,styles:Gt};export{qt as diagram};
|
||||
@@ -0,0 +1,321 @@
|
||||
function Ds(r,e){(e==null||e>r.length)&&(e=r.length);for(var t=0,a=Array(e);t<e;t++)a[t]=r[t];return a}function tc(r){if(Array.isArray(r))return r}function ac(r){if(Array.isArray(r))return Ds(r)}function ct(r,e){if(!(r instanceof e))throw new TypeError("Cannot call a class as a function")}function nc(r,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(r,Jl(a.key),a)}}function dt(r,e,t){return e&&nc(r.prototype,e),Object.defineProperty(r,"prototype",{writable:!1}),r}function Cr(r,e){var t=typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(!t){if(Array.isArray(r)||(t=Ks(r))||e){t&&(r=t);var a=0,n=function(){};return{s:n,n:function(){return a>=r.length?{done:!0}:{done:!1,value:r[a++]}},e:function(u){throw u},f:n}}throw new TypeError(`Invalid attempt to iterate non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var i,s=!0,o=!1;return{s:function(){t=t.call(r)},n:function(){var u=t.next();return s=u.done,u},e:function(u){o=!0,i=u},f:function(){try{s||t.return==null||t.return()}finally{if(o)throw i}}}}function Ql(r,e,t){return(e=Jl(e))in r?Object.defineProperty(r,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):r[e]=t,r}function ic(r){if(typeof Symbol<"u"&&r[Symbol.iterator]!=null||r["@@iterator"]!=null)return Array.from(r)}function sc(r,e){var t=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(t!=null){var a,n,i,s,o=[],u=!0,l=!1;try{if(i=(t=t.call(r)).next,e===0){if(Object(t)!==t)return;u=!1}else for(;!(u=(a=i.call(t)).done)&&(o.push(a.value),o.length!==e);u=!0);}catch(v){l=!0,n=v}finally{try{if(!u&&t.return!=null&&(s=t.return(),Object(s)!==s))return}finally{if(l)throw n}}return o}}function oc(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function uc(){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 Xe(r,e){return tc(r)||sc(r,e)||Ks(r,e)||oc()}function yn(r){return ac(r)||ic(r)||Ks(r)||uc()}function lc(r,e){if(typeof r!="object"||!r)return r;var t=r[Symbol.toPrimitive];if(t!==void 0){var a=t.call(r,e);if(typeof a!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function Jl(r){var e=lc(r,"string");return typeof e=="symbol"?e:e+""}function je(r){"@babel/helpers - typeof";return je=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},je(r)}function Ks(r,e){if(r){if(typeof r=="string")return Ds(r,e);var t={}.toString.call(r).slice(8,-1);return t==="Object"&&r.constructor&&(t=r.constructor.name),t==="Map"||t==="Set"?Array.from(r):t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?Ds(r,e):void 0}}var Qe=typeof window>"u"?null:window,Co=Qe?Qe.navigator:null;Qe&&Qe.document;var vc=je(""),jl=je({}),fc=je(function(){}),cc=typeof HTMLElement>"u"?"undefined":je(HTMLElement),La=function(e){return e&&e.instanceString&&He(e.instanceString)?e.instanceString():null},he=function(e){return e!=null&&je(e)==vc},He=function(e){return e!=null&&je(e)===fc},Fe=function(e){return!Tr(e)&&(Array.isArray?Array.isArray(e):e!=null&&e instanceof Array)},Me=function(e){return e!=null&&je(e)===jl&&!Fe(e)&&e.constructor===Object},dc=function(e){return e!=null&&je(e)===jl},ae=function(e){return e!=null&&je(e)===je(1)&&!isNaN(e)},ev=function(e){return ae(e)&&Math.floor(e)===e},mn=function(e){if(cc!=="undefined")return e!=null&&e instanceof HTMLElement},Tr=function(e){return Ia(e)||rv(e)},Ia=function(e){return La(e)==="collection"&&e._private.single},rv=function(e){return La(e)==="collection"&&!e._private.single},Xs=function(e){return La(e)==="core"},tv=function(e){return La(e)==="stylesheet"},hc=function(e){return La(e)==="event"},st=function(e){return e==null?!0:!!(e===""||e.match(/^\s+$/))},gc=function(e){return typeof HTMLElement>"u"?!1:e instanceof HTMLElement},pc=function(e){return Me(e)&&ae(e.x1)&&ae(e.x2)&&ae(e.y1)&&ae(e.y2)},yc=function(e){return dc(e)&&He(e.then)},mc=function(){return Co&&Co.userAgent.match(/msie|trident|edge/i)},Yt=function(e,t){t||(t=function(){if(arguments.length===1)return arguments[0];if(arguments.length===0)return"undefined";for(var i=[],s=0;s<arguments.length;s++)i.push(arguments[s]);return i.join("$")});var a=function(){var i=this,s=arguments,o,u=t.apply(i,s),l=a.cache;return(o=l[u])||(o=l[u]=e.apply(i,s)),o};return a.cache={},a},Ys=Yt(function(r){return r.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),Rn=Yt(function(r){return r.replace(/(-\w)/g,function(e){return e[1].toUpperCase()})}),av=Yt(function(r,e){return r+e[0].toUpperCase()+e.substring(1)},function(r,e){return r+"$"+e}),To=function(e){return st(e)?e:e.charAt(0).toUpperCase()+e.substring(1)},rt=function(e,t){return e.slice(-1*t.length)===t},Je="(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))",bc="rgb[a]?\\(("+Je+"[%]?)\\s*,\\s*("+Je+"[%]?)\\s*,\\s*("+Je+"[%]?)(?:\\s*,\\s*("+Je+"))?\\)",wc="rgb[a]?\\((?:"+Je+"[%]?)\\s*,\\s*(?:"+Je+"[%]?)\\s*,\\s*(?:"+Je+"[%]?)(?:\\s*,\\s*(?:"+Je+"))?\\)",xc="hsl[a]?\\(("+Je+")\\s*,\\s*("+Je+"[%])\\s*,\\s*("+Je+"[%])(?:\\s*,\\s*("+Je+"))?\\)",Ec="hsl[a]?\\((?:"+Je+")\\s*,\\s*(?:"+Je+"[%])\\s*,\\s*(?:"+Je+"[%])(?:\\s*,\\s*(?:"+Je+"))?\\)",Cc="\\#[0-9a-fA-F]{3}",Tc="\\#[0-9a-fA-F]{6}",nv=function(e,t){return e<t?-1:e>t?1:0},Sc=function(e,t){return-1*nv(e,t)},me=Object.assign!=null?Object.assign.bind(Object):function(r){for(var e=arguments,t=1;t<e.length;t++){var a=e[t];if(a!=null)for(var n=Object.keys(a),i=0;i<n.length;i++){var s=n[i];r[s]=a[s]}}return r},kc=function(e){if(!(!(e.length===4||e.length===7)||e[0]!=="#")){var t=e.length===4,a,n,i,s=16;return t?(a=parseInt(e[1]+e[1],s),n=parseInt(e[2]+e[2],s),i=parseInt(e[3]+e[3],s)):(a=parseInt(e[1]+e[2],s),n=parseInt(e[3]+e[4],s),i=parseInt(e[5]+e[6],s)),[a,n,i]}},Dc=function(e){var t,a,n,i,s,o,u,l;function v(d,y,g){return g<0&&(g+=1),g>1&&(g-=1),g<1/6?d+(y-d)*6*g:g<1/2?y:g<2/3?d+(y-d)*(2/3-g)*6:d}var f=new RegExp("^"+xc+"$").exec(e);if(f){if(a=parseInt(f[1]),a<0?a=(360- -1*a%360)%360:a>360&&(a=a%360),a/=360,n=parseFloat(f[2]),n<0||n>100||(n=n/100,i=parseFloat(f[3]),i<0||i>100)||(i=i/100,s=f[4],s!==void 0&&(s=parseFloat(s),s<0||s>1)))return;if(n===0)o=u=l=Math.round(i*255);else{var c=i<.5?i*(1+n):i+n-i*n,h=2*i-c;o=Math.round(255*v(h,c,a+1/3)),u=Math.round(255*v(h,c,a)),l=Math.round(255*v(h,c,a-1/3))}t=[o,u,l,s]}return t},Bc=function(e){var t,a=new RegExp("^"+bc+"$").exec(e);if(a){t=[];for(var n=[],i=1;i<=3;i++){var s=a[i];if(s[s.length-1]==="%"&&(n[i]=!0),s=parseFloat(s),n[i]&&(s=s/100*255),s<0||s>255)return;t.push(Math.floor(s))}var o=n[1]||n[2]||n[3],u=n[1]&&n[2]&&n[3];if(o&&!u)return;var l=a[4];if(l!==void 0){if(l=parseFloat(l),l<0||l>1)re
Line truncated
|
||||
`),f=r.pstyle("text-max-width").pfValue,c=r.pstyle("text-overflow-wrap").value,h=c==="anywhere",d=[],y=/[\s\u200b]+|$/g,g=0;g<v.length;g++){var p=v[g],m=this.calculateLabelDimensions(r,p),b=m.width;if(h){var w=p.split("").join(l);p=w}if(b>f){var E=p.matchAll(y),C="",x=0,T=Cr(E),k;try{for(T.s();!(k=T.n()).done;){var B=k.value,D=B[0],P=p.substring(x,B.index);x=B.index+D.length;var A=C.length===0?P:C+P+D,R=this.calculateLabelDimensions(r,A),L=R.width;L<=f?C+=P+D:(C&&d.push(C),C=P+D)}}catch(q){T.e(q)}finally{T.f()}C.match(/^[\s\u200b]+$/)||d.push(C)}else d.push(p)}s("labelWrapCachedLines",d),n=s("labelWrapCachedText",d.join(`
|
||||
`)),s("labelWrapKey",u)}else if(o==="ellipsis"){var I=r.pstyle("text-max-width").pfValue,M="",N="…",F=!1;if(this.calculateLabelDimensions(r,n).width<I)return n;for(var _=0;_<n.length;_++){var z=this.calculateLabelDimensions(r,M+n[_]+N).width;if(z>I)break;M+=n[_],_===n.length-1&&(F=!0)}return F||(M+=N),M}return n};Hr.getLabelJustification=function(r){var e=r.pstyle("text-justification").strValue,t=r.pstyle("text-halign").strValue;return e==="auto"?r.isNode()?Ug(t):"center":e};Hr.calculateLabelDimensions=function(r,e){var t=this,a=t.cy.window(),n=a.document,i=0,s=r.pstyle("font-style").strValue,o=r.pstyle("font-size").pfValue,u=r.pstyle("font-family").strValue,l=r.pstyle("font-weight").strValue,v=r.pstyle("text-metrics").strValue||"font",f=this.labelCalcCanvas,c=this.labelCalcCanvasContext;if(!f){f=this.labelCalcCanvas=n.createElement("canvas"),c=this.labelCalcCanvasContext=f.getContext("2d");var h=f.style;h.position="absolute",h.left="-9999px",h.top="-9999px",h.zIndex="-1",h.visibility="hidden",h.pointerEvents="none"}c.font="".concat(s," ").concat(l," ").concat(o,"px ").concat(u);for(var d=0,y=0,g=e.split(`
|
||||
`),p=g.length,m=0,b=0,w=0;w<p;w++){var E=g[w],C=c.measureText(E),x=Math.ceil(C.width),T=o;v==="glyph"&&(w===0&&(b=C.actualBoundingBoxAscent),w===p-1&&(m=C.actualBoundingBoxDescent)),d=Math.max(x,d),y+=T}return v==="glyph"&&(y-=o-b-m),d+=i,y+=i,{width:d,height:y,labelActualAscent:b,labelActualDescent:m}};Hr.calculateLabelAngle=function(r,e){var t=r._private,a=t.rscratch,n=r.isEdge(),i=e?e+"-":"",s=r.pstyle(i+"text-rotation"),o=s.strValue;return o==="none"?0:n&&o==="autorotate"?a.labelAutoAngle:o==="autorotate"?0:s.pfValue};Hr.calculateLabelAngles=function(r){var e=this,t=r.isEdge(),a=r._private,n=a.rscratch;n.labelAngle=e.calculateLabelAngle(r),t&&(n.sourceLabelAngle=e.calculateLabelAngle(r,"source"),n.targetLabelAngle=e.calculateLabelAngle(r,"target"))};var xf={},Ml=28,Ll=!1;xf.getNodeShape=function(r){var e=this,t=r.pstyle("shape").value;if(t==="cutrectangle"&&(r.width()<Ml||r.height()<Ml))return Ll||(Ne("The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead"),Ll=!0),"rectangle";if(r.isParent())return t==="rectangle"||t==="roundrectangle"||t==="round-rectangle"||t==="cutrectangle"||t==="cut-rectangle"||t==="barrel"?t:"rectangle";if(t==="polygon"){var a=r.pstyle("shape-polygon-points").value;return e.nodeShapes.makePolygon(a).name}return t};var Kn={};Kn.registerCalculationListeners=function(){var r=this.cy,e=r.collection(),t=this,a=function(s){var o=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;if(e.merge(s),o)for(var u=0;u<s.length;u++){var l=s[u],v=l._private,f=v.rstyle;f.clean=!1,f.cleanConnected=!1}};t.binder(r).on("bounds.* dirty.*",function(s){var o=s.target;a(o)}).on("style.* background.*",function(s){var o=s.target;a(o,!1)});var n=function(s){if(s){var o=t.onUpdateEleCalcsFns;e.cleanStyle();for(var u=0;u<e.length;u++){var l=e[u],v=l._private.rstyle;l.isNode()&&!v.cleanConnected&&(a(l.connectedEdges()),v.cleanConnected=!0)}if(o)for(var f=0;f<o.length;f++){var c=o[f];c(s,e)}t.recalculateRenderedStyle(e),e=r.collection()}};t.flushRenderedStyleQueue=function(){n(!0)},t.beforeRender(n,t.beforeRenderPriorities.eleCalcs)};Kn.onUpdateEleCalcs=function(r){var e=this.onUpdateEleCalcsFns=this.onUpdateEleCalcsFns||[];e.push(r)};Kn.recalculateRenderedStyle=function(r,e){var t=function(w){return w._private.rstyle.cleanConnected};if(r.length!==0){var a=[],n=[];if(!this.destroyed){e===void 0&&(e=!0);for(var i=0;i<r.length;i++){var s=r[i],o=s._private,u=o.rstyle;s.isEdge()&&(!t(s.source())||!t(s.target()))&&(u.clean=!1),s.isEdge()&&s.isBundledBezier()&&s.parallelEdges().some(function(b){return!b._private.rstyle.clean&&b.isBundledBezier()})&&(u.clean=!1),!(e&&u.clean||s.removed())&&s.pstyle("display").value!=="none"&&(o.group==="nodes"?n.push(s):a.push(s),u.clean=!0)}for(var l=0;l<n.length;l++){var v=n[l],f=v._private,c=f.rstyle,h=v.position();this.recalculateNodeLabelProjection(v),c.nodeX=h.x,c.nodeY=h.y,c.nodeW=v.pstyle("width").pfValue,c.nodeH=v.pstyle("height").pfValue}this.recalculateEdgeProjections(a);for(var d=0;d<a.length;d++){var y=a[d],g=y._private,p=g.rstyle,m=g.rscratch;p.srcX=m.arrowStartX,p.srcY=m.arrowStartY,p.tgtX=m.arrowEndX,p.tgtY=m.arrowEndY,p.midX=m.midX,p.midY=m.midY,p.labelAngle=m.labelAngle,p.sourceLabelAngle=m.sourceLabelAngle,p.targetLabelAngle=m.targetLabelAngle}}}};var Xn={};Xn.updateCachedGrabbedEles=function(){var r=this.cachedZSortedEles;if(r){r.drag=[],r.nondrag=[];for(var e=[],t=0;t<r.length;t++){var a=r[t],n=a._private.rscratch;a.grabbed()&&!a.isParent()?e.push(a):n.inDragLayer?r.drag.push(a):r.nondrag.push(a)}for(var t=0;t<e.length;t++){var a=e[t];r.drag.push(a)}}};Xn.invalidateCachedZSortedEles=function(){this.cachedZSortedEles=null};Xn.getCachedZSortedEles=function(r){if(r||!this.cachedZSortedEles){var e=this.cy.mutableElements().toArray();e.sort(rf),e.interactive=e.filter(function(t){return t.interactive()}),this.cachedZSortedEles=e,this.updateCachedGrabbedEles()}else e=this.cachedZSortedEles;return e};var Ef={};[At,Dn,hr,Ga,po,Hr,xf,Kn,Xn].forEach(function(r){me(Ef,r)});var Cf={};Cf.getCachedImage=function(r,e,t){var a=this,n=a.imageCache=a.imageCache||{},i=n[r];if(i)return i.image.complete||i.image.addEventListener("load",t),i.image;i=n[r]=n[r]||{};var s=i.image=new Image;s.addEventListener("load",t),s.addEventListener("error",function(){s.error=!0});var o="data:",u=r.substring(0,o.length).toLowerCase()===o;return u||(e=e==="null"?null:e,s.crossOrigin=e),s.src=r,s};var Tf=function(e,t){var a=e[0];!a||a._private.grabbed===t||(a._private.grabbed=t,e.updateStyle(!1))},sy=function(e){Tf(e,!0)},oy=function(e){Tf(e,!1)},ia={};ia.registerBinding=function(r,e,t,a){var n=Array.prototype.slice.apply(arguments,[1]);if(Array.isArray(r)){for(var i=[],s=0;s<r.length;s++){var o=r[s];if(o!==void 0){var u=this.binder(o);i.push(u.on.apply(u,n))}}return i}var u=this.binder(r);return u.on.apply(u,n)};ia.binder=function(r){var e=this,t=e.cy.window(),a=r===t||r===t.document||r===t.document.body||gc(r);if(e.supportsPassiveEvents==null){var n=!1;try{va
Line truncated
|
||||
float circleSD(vec2 p, float r) {
|
||||
return distance(vec2(0), p) - r; // signed distance
|
||||
}
|
||||
`,dm=`
|
||||
float rectangleSD(vec2 p, vec2 b) {
|
||||
vec2 d = abs(p)-b;
|
||||
return distance(vec2(0),max(d,0.0)) + min(max(d.x,d.y),0.0);
|
||||
}
|
||||
`,hm=`
|
||||
float roundRectangleSD(vec2 p, vec2 b, vec4 cr) {
|
||||
cr.xy = (p.x > 0.0) ? cr.xy : cr.zw;
|
||||
cr.x = (p.y > 0.0) ? cr.x : cr.y;
|
||||
vec2 q = abs(p) - b + cr.x;
|
||||
return min(max(q.x, q.y), 0.0) + distance(vec2(0), max(q, 0.0)) - cr.x;
|
||||
}
|
||||
`,gm=`
|
||||
float ellipseSD(vec2 p, vec2 ab) {
|
||||
p = abs( p ); // symmetry
|
||||
|
||||
// find root with Newton solver
|
||||
vec2 q = ab*(p-ab);
|
||||
float w = (q.x<q.y)? 1.570796327 : 0.0;
|
||||
for( int i=0; i<5; i++ ) {
|
||||
vec2 cs = vec2(cos(w),sin(w));
|
||||
vec2 u = ab*vec2( cs.x,cs.y);
|
||||
vec2 v = ab*vec2(-cs.y,cs.x);
|
||||
w = w + dot(p-u,v)/(dot(p-u,u)+dot(v,v));
|
||||
}
|
||||
|
||||
// compute final point and distance
|
||||
float d = length(p-ab*vec2(cos(w),sin(w)));
|
||||
|
||||
// return signed distance
|
||||
return (dot(p/ab,p/ab)>1.0) ? d : -d;
|
||||
}
|
||||
`,Ea={SCREEN:{name:"screen",screen:!0},PICKING:{name:"picking",picking:!0}},Pn={IGNORE:1,USE_BB:2},Es=0,Ul=1,Kl=2,Cs=3,Vt=4,nn=5,ga=6,pa=7,pm=(function(){function r(e,t,a){ct(this,r),this.r=e,this.gl=t,this.maxInstances=a.webglBatchSize,this.atlasSize=a.webglTexSize,this.bgColor=a.bgColor,this.debug=a.webglDebug,this.batchDebugInfo=[],a.enableWrapping=!0,a.createTextureCanvas=Xy,this.atlasManager=new vm(e,a),this.batchManager=new fm(a),this.simpleShapeOptions=new Map,this.program=this._createShaderProgram(Ea.SCREEN),this.pickingProgram=this._createShaderProgram(Ea.PICKING),this.vao=this._createVAO()}return dt(r,[{key:"addAtlasCollection",value:function(t,a){this.atlasManager.addAtlasCollection(t,a)}},{key:"addTextureAtlasRenderType",value:function(t,a){this.atlasManager.addRenderType(t,a)}},{key:"addSimpleShapeRenderType",value:function(t,a){this.simpleShapeOptions.set(t,a)}},{key:"invalidate",value:function(t){var a=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=a.type,i=this.atlasManager;return n?i.invalidate(t,{filterType:function(o){return o===n},forceRedraw:!0}):i.invalidate(t)}},{key:"gc",value:function(){this.atlasManager.gc()}},{key:"_createShaderProgram",value:function(t){var a=this.gl,n=`#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform mat3 uPanZoomMatrix;
|
||||
uniform int uAtlasSize;
|
||||
|
||||
// instanced
|
||||
in vec2 aPosition; // a vertex from the unit square
|
||||
|
||||
in mat3 aTransform; // used to transform verticies, eg into a bounding box
|
||||
in int aVertType; // the type of thing we are rendering
|
||||
|
||||
// the z-index that is output when using picking mode
|
||||
in vec4 aIndex;
|
||||
|
||||
// For textures
|
||||
in int aAtlasId; // which shader unit/atlas to use
|
||||
in vec4 aTex; // x/y/w/h of texture in atlas
|
||||
|
||||
// for edges
|
||||
in vec4 aPointAPointB;
|
||||
in vec4 aPointCPointD;
|
||||
in vec2 aLineWidth; // also used for node border width
|
||||
|
||||
// simple shapes
|
||||
in vec4 aCornerRadius; // for round-rectangle [top-right, bottom-right, top-left, bottom-left]
|
||||
in vec4 aColor; // also used for edges
|
||||
in vec4 aBorderColor; // aLineWidth is used for border width
|
||||
|
||||
// output values passed to the fragment shader
|
||||
out vec2 vTexCoord;
|
||||
out vec4 vColor;
|
||||
out vec2 vPosition;
|
||||
// flat values are not interpolated
|
||||
flat out int vAtlasId;
|
||||
flat out int vVertType;
|
||||
flat out vec2 vTopRight;
|
||||
flat out vec2 vBotLeft;
|
||||
flat out vec4 vCornerRadius;
|
||||
flat out vec4 vBorderColor;
|
||||
flat out vec2 vBorderWidth;
|
||||
flat out vec4 vIndex;
|
||||
|
||||
void main(void) {
|
||||
int vid = gl_VertexID;
|
||||
vec2 position = aPosition; // TODO make this a vec3, simplifies some code below
|
||||
|
||||
if(aVertType == `.concat(Es,`) {
|
||||
float texX = aTex.x; // texture coordinates
|
||||
float texY = aTex.y;
|
||||
float texW = aTex.z;
|
||||
float texH = aTex.w;
|
||||
|
||||
if(vid == 1 || vid == 2 || vid == 4) {
|
||||
texX += texW;
|
||||
}
|
||||
if(vid == 2 || vid == 4 || vid == 5) {
|
||||
texY += texH;
|
||||
}
|
||||
|
||||
float d = float(uAtlasSize);
|
||||
vTexCoord = vec2(texX / d, texY / d); // tex coords must be between 0 and 1
|
||||
|
||||
gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0);
|
||||
}
|
||||
else if(aVertType == `).concat(Vt," || aVertType == ").concat(pa,`
|
||||
|| aVertType == `).concat(nn," || aVertType == ").concat(ga,`) { // simple shapes
|
||||
|
||||
// the bounding box is needed by the fragment shader
|
||||
vBotLeft = (aTransform * vec3(0, 0, 1)).xy; // flat
|
||||
vTopRight = (aTransform * vec3(1, 1, 1)).xy; // flat
|
||||
vPosition = (aTransform * vec3(position, 1)).xy; // will be interpolated
|
||||
|
||||
// calculations are done in the fragment shader, just pass these along
|
||||
vColor = aColor;
|
||||
vCornerRadius = aCornerRadius;
|
||||
vBorderColor = aBorderColor;
|
||||
vBorderWidth = aLineWidth;
|
||||
|
||||
gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0);
|
||||
}
|
||||
else if(aVertType == `).concat(Ul,`) {
|
||||
vec2 source = aPointAPointB.xy;
|
||||
vec2 target = aPointAPointB.zw;
|
||||
|
||||
// adjust the geometry so that the line is centered on the edge
|
||||
position.y = position.y - 0.5;
|
||||
|
||||
// stretch the unit square into a long skinny rectangle
|
||||
vec2 xBasis = target - source;
|
||||
vec2 yBasis = normalize(vec2(-xBasis.y, xBasis.x));
|
||||
vec2 point = source + xBasis * position.x + yBasis * aLineWidth[0] * position.y;
|
||||
|
||||
gl_Position = vec4(uPanZoomMatrix * vec3(point, 1.0), 1.0);
|
||||
vColor = aColor;
|
||||
}
|
||||
else if(aVertType == `).concat(Kl,`) {
|
||||
vec2 pointA = aPointAPointB.xy;
|
||||
vec2 pointB = aPointAPointB.zw;
|
||||
vec2 pointC = aPointCPointD.xy;
|
||||
vec2 pointD = aPointCPointD.zw;
|
||||
|
||||
// adjust the geometry so that the line is centered on the edge
|
||||
position.y = position.y - 0.5;
|
||||
|
||||
vec2 p0, p1, p2, pos;
|
||||
if(position.x == 0.0) { // The left side of the unit square
|
||||
p0 = pointA;
|
||||
p1 = pointB;
|
||||
p2 = pointC;
|
||||
pos = position;
|
||||
} else { // The right side of the unit square, use same approach but flip the geometry upside down
|
||||
p0 = pointD;
|
||||
p1 = pointC;
|
||||
p2 = pointB;
|
||||
pos = vec2(0.0, -position.y);
|
||||
}
|
||||
|
||||
vec2 p01 = p1 - p0;
|
||||
vec2 p12 = p2 - p1;
|
||||
vec2 p21 = p1 - p2;
|
||||
|
||||
// Find the normal vector.
|
||||
vec2 tangent = normalize(normalize(p12) + normalize(p01));
|
||||
vec2 normal = vec2(-tangent.y, tangent.x);
|
||||
|
||||
// Find the vector perpendicular to p0 -> p1.
|
||||
vec2 p01Norm = normalize(vec2(-p01.y, p01.x));
|
||||
|
||||
// Determine the bend direction.
|
||||
float sigma = sign(dot(p01 + p21, normal));
|
||||
float width = aLineWidth[0];
|
||||
|
||||
if(sign(pos.y) == -sigma) {
|
||||
// This is an intersecting vertex. Adjust the position so that there's no overlap.
|
||||
vec2 point = 0.5 * width * normal * -sigma / dot(normal, p01Norm);
|
||||
gl_Position = vec4(uPanZoomMatrix * vec3(p1 + point, 1.0), 1.0);
|
||||
} else {
|
||||
// This is a non-intersecting vertex. Treat it like a mitre join.
|
||||
vec2 point = 0.5 * width * normal * sigma * dot(normal, p01Norm);
|
||||
gl_Position = vec4(uPanZoomMatrix * vec3(p1 + point, 1.0), 1.0);
|
||||
}
|
||||
|
||||
vColor = aColor;
|
||||
}
|
||||
else if(aVertType == `).concat(Cs,` && vid < 3) {
|
||||
// massage the first triangle into an edge arrow
|
||||
if(vid == 0)
|
||||
position = vec2(-0.15, -0.3);
|
||||
if(vid == 1)
|
||||
position = vec2( 0.0, 0.0);
|
||||
if(vid == 2)
|
||||
position = vec2( 0.15, -0.3);
|
||||
|
||||
gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0);
|
||||
vColor = aColor;
|
||||
}
|
||||
else {
|
||||
gl_Position = vec4(2.0, 0.0, 0.0, 1.0); // discard vertex by putting it outside webgl clip space
|
||||
}
|
||||
|
||||
vAtlasId = aAtlasId;
|
||||
vVertType = aVertType;
|
||||
vIndex = aIndex;
|
||||
}
|
||||
`),i=this.batchManager.getIndexArray(),s=`#version 300 es
|
||||
precision highp float;
|
||||
|
||||
// declare texture unit for each texture atlas in the batch
|
||||
`.concat(i.map(function(l){return"uniform sampler2D uTexture".concat(l,";")}).join(`
|
||||
`),`
|
||||
|
||||
uniform vec4 uBGColor;
|
||||
uniform float uZoom;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
in vec4 vColor;
|
||||
in vec2 vPosition; // model coordinates
|
||||
|
||||
flat in int vAtlasId;
|
||||
flat in vec4 vIndex;
|
||||
flat in int vVertType;
|
||||
flat in vec2 vTopRight;
|
||||
flat in vec2 vBotLeft;
|
||||
flat in vec4 vCornerRadius;
|
||||
flat in vec4 vBorderColor;
|
||||
flat in vec2 vBorderWidth;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
`).concat(cm,`
|
||||
`).concat(dm,`
|
||||
`).concat(hm,`
|
||||
`).concat(gm,`
|
||||
|
||||
vec4 blend(vec4 top, vec4 bot) { // blend colors with premultiplied alpha
|
||||
return vec4(
|
||||
top.rgb + (bot.rgb * (1.0 - top.a)),
|
||||
top.a + (bot.a * (1.0 - top.a))
|
||||
);
|
||||
}
|
||||
|
||||
vec4 distInterp(vec4 cA, vec4 cB, float d) { // interpolate color using Signed Distance
|
||||
// scale to the zoom level so that borders don't look blurry when zoomed in
|
||||
// note 1.5 is an aribitrary value chosen because it looks good
|
||||
return mix(cA, cB, 1.0 - smoothstep(0.0, 1.5 / uZoom, abs(d)));
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
if(vVertType == `).concat(Es,`) {
|
||||
// look up the texel from the texture unit
|
||||
`).concat(i.map(function(l){return"if(vAtlasId == ".concat(l,") outColor = texture(uTexture").concat(l,", vTexCoord);")}).join(`
|
||||
else `),`
|
||||
}
|
||||
else if(vVertType == `).concat(Cs,`) {
|
||||
// mimics how canvas renderer uses context.globalCompositeOperation = 'destination-out';
|
||||
outColor = blend(vColor, uBGColor);
|
||||
outColor.a = 1.0; // make opaque, masks out line under arrow
|
||||
}
|
||||
else if(vVertType == `).concat(Vt,` && vBorderWidth == vec2(0.0)) { // simple rectangle with no border
|
||||
outColor = vColor; // unit square is already transformed to the rectangle, nothing else needs to be done
|
||||
}
|
||||
else if(vVertType == `).concat(Vt," || vVertType == ").concat(pa,`
|
||||
|| vVertType == `).concat(nn," || vVertType == ").concat(ga,`) { // use SDF
|
||||
|
||||
float outerBorder = vBorderWidth[0];
|
||||
float innerBorder = vBorderWidth[1];
|
||||
float borderPadding = outerBorder * 2.0;
|
||||
float w = vTopRight.x - vBotLeft.x - borderPadding;
|
||||
float h = vTopRight.y - vBotLeft.y - borderPadding;
|
||||
vec2 b = vec2(w/2.0, h/2.0); // half width, half height
|
||||
vec2 p = vPosition - vec2(vTopRight.x - b[0] - outerBorder, vTopRight.y - b[1] - outerBorder); // translate to center
|
||||
|
||||
float d; // signed distance
|
||||
if(vVertType == `).concat(Vt,`) {
|
||||
d = rectangleSD(p, b);
|
||||
} else if(vVertType == `).concat(pa,` && w == h) {
|
||||
d = circleSD(p, b.x); // faster than ellipse
|
||||
} else if(vVertType == `).concat(pa,`) {
|
||||
d = ellipseSD(p, b);
|
||||
} else {
|
||||
d = roundRectangleSD(p, b, vCornerRadius.wzyx);
|
||||
}
|
||||
|
||||
// use the distance to interpolate a color to smooth the edges of the shape, doesn't need multisampling
|
||||
// we must smooth colors inwards, because we can't change pixels outside the shape's bounding box
|
||||
if(d > 0.0) {
|
||||
if(d > outerBorder) {
|
||||
discard;
|
||||
} else {
|
||||
outColor = distInterp(vBorderColor, vec4(0), d - outerBorder);
|
||||
}
|
||||
} else {
|
||||
if(d > innerBorder) {
|
||||
vec4 outerColor = outerBorder == 0.0 ? vec4(0) : vBorderColor;
|
||||
vec4 innerBorderColor = blend(vBorderColor, vColor);
|
||||
outColor = distInterp(innerBorderColor, outerColor, d);
|
||||
}
|
||||
else {
|
||||
vec4 outerColor;
|
||||
if(innerBorder == 0.0 && outerBorder == 0.0) {
|
||||
outerColor = vec4(0);
|
||||
} else if(innerBorder == 0.0) {
|
||||
outerColor = vBorderColor;
|
||||
} else {
|
||||
outerColor = blend(vBorderColor, vColor);
|
||||
}
|
||||
outColor = distInterp(vColor, outerColor, d - innerBorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
outColor = vColor;
|
||||
}
|
||||
|
||||
`).concat(t.picking?`if(outColor.a == 0.0) discard;
|
||||
else outColor = vIndex;`:"",`
|
||||
}
|
||||
`),o=Ky(a,n,s);o.aPosition=a.getAttribLocation(o,"aPosition"),o.aIndex=a.getAttribLocation(o,"aIndex"),o.aVertType=a.getAttribLocation(o,"aVertType"),o.aTransform=a.getAttribLocation(o,"aTransform"),o.aAtlasId=a.getAttribLocation(o,"aAtlasId"),o.aTex=a.getAttribLocation(o,"aTex"),o.aPointAPointB=a.getAttribLocation(o,"aPointAPointB"),o.aPointCPointD=a.getAttribLocation(o,"aPointCPointD"),o.aLineWidth=a.getAttribLocation(o,"aLineWidth"),o.aColor=a.getAttribLocation(o,"aColor"),o.aCornerRadius=a.getAttribLocation(o,"aCornerRadius"),o.aBorderColor=a.getAttribLocation(o,"aBorderColor"),o.uPanZoomMatrix=a.getUniformLocation(o,"uPanZoomMatrix"),o.uAtlasSize=a.getUniformLocation(o,"uAtlasSize"),o.uBGColor=a.getUniformLocation(o,"uBGColor"),o.uZoom=a.getUniformLocation(o,"uZoom"),o.uTextures=[];for(var u=0;u<this.batchManager.getMaxAtlasesPerBatch();u++)o.uTextures.push(a.getUniformLocation(o,"uTexture".concat(u)));return o}},{key:"_createVAO",value:function(){var t=[0,0,1,0,1,1,0,0,1,1,0,1];this.vertexCount=t.length/2;var a=this.maxInstances,n=this.gl,i=this.program,s=n.createVertexArray();return n.bindVertexArray(s),tm(n,"vec2",i.aPosition,t),this.transformBuffer=am(n,a,i.aTransform),this.indexBuffer=Fr(n,a,"vec4",i.aIndex),this.vertTypeBuffer=Fr(n,a,"int",i.aVertType),this.atlasIdBuffer=Fr(n,a,"int",i.aAtlasId),this.texBuffer=Fr(n,a,"vec4",i.aTex),this.pointAPointBBuffer=Fr(n,a,"vec4",i.aPointAPointB),this.pointCPointDBuffer=Fr(n,a,"vec4",i.aPointCPointD),this.lineWidthBuffer=Fr(n,a,"vec2",i.aLineWidth),this.colorBuffer=Fr(n,a,"vec4",i.aColor),this.cornerRadiusBuffer=Fr(n,a,"vec4",i.aCornerRadius),this.borderColorBuffer=Fr(n,a,"vec4",i.aBorderColor),n.bindVertexArray(null),s}},{key:"buffers",get:function(){var t=this;return this._buffers||(this._buffers=Object.keys(this).filter(function(a){return rt(a,"Buffer")}).map(function(a){return t[a]})),this._buffers}},{key:"startFrame",value:function(t){var a=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Ea.SCREEN;this.panZoomMatrix=t,this.renderTarget=a,this.batchDebugInfo=[],this.wrappedCount=0,this.simpleCount=0,this.startBatch()}},{key:"startBatch",value:function(){this.instanceCount=0,this.batchManager.startBatch()}},{key:"endFrame",value:function(){this.endBatch()}},{key:"_isVisible",value:function(t,a){return t.visible()?a&&a.isVisible?a.isVisible(t):!0:!1}},{key:"drawTexture",value:function(t,a,n){var i=this.atlasManager,s=this.batchManager,o=i.getRenderTypeOpts(n);if(this._isVisible(t,o)&&!(t.isEdge()&&!this._isValidEdge(t))){if(this.renderTarget.picking&&o.getTexPickingMode){var u=o.getTexPickingMode(t);if(u===Pn.IGNORE)return;if(u==Pn.USE_BB){this.drawPickingRectangle(t,a,n);return}}var l=i.getAtlasInfo(t,n),v=Cr(l),f;try{for(v.s();!(f=v.n()).done;){var c=f.value,h=c.atlas,d=c.tex1,y=c.tex2;s.canAddToCurrentBatch(h)||this.endBatch();for(var g=s.getAtlasIndexForBatch(h),p=0,m=[[d,!0],[y,!1]];p<m.length;p++){var b=Xe(m[p],2),w=b[0],E=b[1];if(w.w!=0){var C=this.instanceCount;this.vertTypeBuffer.getView(C)[0]=Es;var x=this.indexBuffer.getView(C);Ft(a,x);var T=this.atlasIdBuffer.getView(C);T[0]=g;var k=this.texBuffer.getView(C);k[0]=w.x,k[1]=w.y,k[2]=w.w,k[3]=w.h;var B=this.transformBuffer.getMatrixView(C);this.setTransformMatrix(t,B,o,c,E),this.instanceCount++,E||this.wrappedCount++,this.instanceCount>=this.maxInstances&&this.endBatch()}}}}catch(D){v.e(D)}finally{v.f()}}}},{key:"setTransformMatrix",value:function(t,a,n,i){var s=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,o=0;if(n.shapeProps&&n.shapeProps.padding&&(o=t.pstyle(n.shapeProps.padding).pfValue),i){var u=i.bb,l=i.tex1,v=i.tex2,f=l.w/(l.w+v.w);s||(f=1-f);var c=this._getAdjustedBB(u,o,s,f);this._applyTransformMatrix(a,c,n,t)}else{var h=n.getBoundingBox(t),d=this._getAdjustedBB(h,o,!0,1);this._applyTransformMatrix(a,d,n,t)}}},{key:"_applyTransformMatrix",value:function(t,a,n,i){var s,o;Wl(t);var u=n.getRotation?n.getRotation(i):0;if(u!==0){var l=n.getRotationPoint(i),v=l.x,f=l.y;pn(t,t,[v,f]),$l(t,t,u);var c=n.getRotationOffset(i);s=c.x+(a.xOffset||0),o=c.y+(a.yOffset||0)}else s=a.x1,o=a.y1;pn(t,t,[s,o]),Hs(t,t,[a.w,a.h])}},{key:"_getAdjustedBB",value:function(t,a,n,i){var s=t.x1,o=t.y1,u=t.w,l=t.h,v=t.yOffset;a&&(s-=a,o-=a,u+=2*a,l+=2*a);var f=0,c=u*i;return n&&i<1?u=c:!n&&i<1&&(f=u-c,s+=f,u=c),{x1:s,y1:o,w:u,h:l,xOffset:f,yOffset:v}}},{key:"drawPickingRectangle",value:function(t,a,n){var i=this.atlasManager.getRenderTypeOpts(n),s=this.instanceCount;this.vertTypeBuffer.getView(s)[0]=Vt;var o=this.indexBuffer.getView(s);Ft(a,o);var u=this.colorBuffer.getView(s);bt([0,0,0],1,u);var l=this.transformBuffer.getMatrixView(s);this.setTransformMatrix(t,l,i),this.simpleCount++,this.instanceCount++,this.instanceCount>=this.maxInstances&&this.endBatch()}},{key:"drawNode",value:function(t,a,n){var i=this.simpleShapeOptions.get(n);if(this._isVisible(t,i)){var s=i.shapeProps,o=this._getVertTypeForShape(t,s.shape);if(o===void 0||i.isSimple&&!i.isSimple(t,this.renderTarget)){this.drawTexture(t,
Line truncated
|
||||
@@ -0,0 +1,4 @@
|
||||
import{_ as m,ao as x,c as k,Q as O,l as p,ap as A,aq as Y,ar as b,as as _,at as z,au as H,av as D,aw as J,ax as T,ay as X,ah as R,af as F,az as q,aA as W}from"./mermaid.core.js";import{l as j}from"./layout.js";import"./index.js";var C=m((t,e,r)=>Math.max(e,Math.min(r,t)),"clamp"),P=m((t="TB")=>{switch(t){case"BT":return"bottom";case"LR":return"right";case"RL":return"left";default:return"top"}},"getDefaultSelfLoopSide"),Q=m(t=>t==="flowchart"||t==="flowchart-v2"||t==="stateDiagram"||t==="er"||t==="classDiagram","shouldMergeSelfLoopSegments"),U=["x","y","width","height","labelBBox","intersect","calcIntersect","diff","clusterNode"],K=m((t,e,r,d,a)=>{const f=[],h=new Set;if(r.forEach(({start:i,end:o})=>{i!==d&&h.add(i),o!==d&&h.add(o)}),h.forEach(i=>{const o=t.node(i);typeof o?.x=="number"&&typeof o?.y=="number"&&f.push(o)}),f.length===0&&r.forEach(({edge:i})=>{(i.points??[]).forEach(o=>{typeof o?.x=="number"&&typeof o?.y=="number"&&f.push(o)})}),f.length===0)return P(a);const c=f.reduce((i,o)=>({x:i.x+o.x/f.length,y:i.y+o.y/f.length}),{x:0,y:0}),l=c.x-e.x,s=c.y-e.y;return Math.abs(l)>Math.abs(s)?l>0?"right":"left":Math.abs(s)>0?s>0?"bottom":"top":P(a)},"getSelfLoopSide"),V=m((t,e="top",r=0,d=0)=>{const a=t.x,f=t.y-r,h=t.width/2,c=t.height/2,l=Math.max(36,Math.min(100,t.width*.8)),s=C(Math.max(d,t.width*.35),36,l),i=C(Math.min(t.width,t.height)*.45,24,48);switch(e){case"bottom":{const o=f+c;return[{x:a-s/2,y:o},{x:a-s/2,y:o+i},{x:a+s/2,y:o+i},{x:a+s/2,y:o}]}case"right":{const o=a+h;return[{x:o,y:f-s/2},{x:o+i,y:f-s/2},{x:o+i,y:f+s/2},{x:o,y:f+s/2}]}case"left":{const o=a-h;return[{x:o,y:f-s/2},{x:o-i,y:f-s/2},{x:o-i,y:f+s/2},{x:o,y:f+s/2}]}default:{const o=f-c;return[{x:a-s/2,y:o},{x:a-s/2,y:o-i},{x:a+s/2,y:o-i},{x:a+s/2,y:o}]}}},"getSelfLoopPoints"),Z=m((t,e,r="top",d=0,a={})=>{const h=t.x,c=t.y-d,l=a.width??0,s=a.height??0;switch(r){case"bottom":return{x:h,y:Math.max(...e.map(i=>i.y))+s/2+4};case"right":return{x:Math.max(...e.map(i=>i.x))+l/2+4,y:c};case"left":return{x:Math.min(...e.map(i=>i.x))-l/2-4,y:c};default:return{x:h,y:Math.min(...e.map(i=>i.y))-s/2-4}}},"getSelfLoopLabelPosition"),B=m((t,e=0,{mergeSelfLoops:r=!0}={})=>{const d=new Map,a=[],f=t.graph()?.rankdir;return t.edges().forEach(h=>{const c=t.edge(h);if(r&&c.selfLoop){const l=c.selfLoop.id;d.has(l)||d.set(l,[]),d.get(l).push({edge:c,start:h.v,end:h.w})}else a.push({edge:c,start:h.v,end:h.w})}),d.forEach(h=>{if(h.length!==3){h.forEach(u=>a.push(u));return}h.sort((u,E)=>u.edge.selfLoop.order-E.edge.selfLoop.order);const[c,l,s]=h,i=c.edge.originalEdge??l.edge.originalEdge??s.edge.originalEdge??l.edge,o=t.node(i.start);if(!o){h.forEach(u=>a.push(u));return}const n={width:l.edge.width,height:l.edge.height},L=K(t,o,h,i.start,f),w=V(o,L,e,n.width??0),y=Z(o,w,L,e,n),g={...l.edge,...i,id:i.id,points:w,start:i.start,end:i.end,x:y.x,y:y.y,width:n.width,height:n.height,labelStyle:l.edge.labelStyle,fromCluster:c.edge.fromCluster??l.edge.fromCluster??s.edge.fromCluster,toCluster:c.edge.toCluster??l.edge.toCluster??s.edge.toCluster};delete g.selfLoop,delete g.originalEdge,a.push({edge:g,start:g.start,end:g.end})}),a},"getEdgesToRender"),I=m(async({element:t,graph:e,diagramType:r,id:d,parentCluster:a,siteConfig:f})=>{const h=e.graph().rankdir;p.trace("Dir in recursive render - dir:",h);const{clusters:c,edgePaths:l,edgeLabels:s,nodes:i,rootGroups:o}=_(t,{edgePathsClass:"edgePaths"});e.nodes()?p.info("Recursive render XXX",e.nodes()):p.info("No nodes found for",e),e.edges().length>0&&p.info("Recursive edges",e.edge(e.edges()[0]));const n=Q(r);await Promise.all(e.nodes().map(async function(y){const g=e.node(y);if(a!==void 0){const u=JSON.parse(JSON.stringify(a.clusterData));p.trace(`Setting data for parent cluster XXX
|
||||
Node.id = `,y,`
|
||||
data=`,u.height,`
|
||||
Parent cluster`,a.height),e.setNode(a.id,u),e.parent(y)||(p.trace("Setting parent",y,a.id),e.setParent(y,a.id,u))}if(p.info("(Insert) Node XXX"+y+": "+JSON.stringify(e.node(y))),g?.clusterNode){p.info("Cluster identified XBX",y,g.width,e.node(y));const{ranksep:u,nodesep:E}=e.graph();g.graph.setGraph({...g.graph.graph(),ranksep:u+25,nodesep:E});const S=await oe({element:i,graph:g.graph,diagramType:r,id:d,parentCluster:e.node(y),siteConfig:f}),N=S.elem;z(g,N),g.diff=S.diff||0,p.info("New compound node after recursive render XAX",y,"width",g.width,"height",g.height),H(N,g)}else e.children(y).length>0?(p.trace("Cluster - the non recursive path XBX",y,g.id,g,g.width,"Graph:",e),p.trace(D(g.id,e)),b.set(g.id,{id:D(g.id,e),node:g})):(p.trace("Node - the non recursive path XAX",y,i,e.node(y),h),await J(i,e.node(y),{config:f,dir:h}))})),await m(async()=>{const y=e.edges().map(async function(g){const u=e.edge(g.v,g.w,g.name);if(p.info("Edge "+g.v+" -> "+g.w+": "+JSON.stringify(g)),p.info("Edge "+g.v+" -> "+g.w+": ",g," ",JSON.stringify(e.edge(g))),p.info("Fix",b,"ids:",g.v,g.w,"Translating: ",b.get(g.v),b.get(g.w)),n&&u.selfLoop){if(u.selfLoop.order!==1)return;const E={...u.originalEdge,...u,id:u.selfLoop.id,startLabelLeft:u.originalEdge?.startLabelLeft??u.startLabelLeft,startLabelRight:u.originalEdge?.startLabelRight??u.startLabelRight,endLabelLeft:u.originalEdge?.endLabelLeft??u.endLabelLeft,endLabelRight:u.originalEdge?.endLabelRight??u.endLabelRight};await X(s,E),u.width=E.width,u.height=E.height,u.labelStyle=E.labelStyle;return}await X(s,u)});await Promise.all(y)},"processEdges")();const{subGraphTitleTotalMargin:w}=T(f);return{elem:o,graph:e,groups:{clusters:c,edgePaths:l,edgeLabels:s,nodes:i,rootGroups:o},diagramType:r,id:d,mergeSelfLoops:n,subGraphTitleTotalMargin:w}},"measureDagreGraph"),M=m(t=>{p.info("############################################# XXX"),p.info("### Layout ### XXX"),p.info("############################################# XXX"),j(t)},"runDagreGraphLayout"),$=m((t,e,r)=>{const d=t.node(e);if(!d)return;const a={...d};return d?.clusterNode?a.y=(d.y??0)+r:t.children(e).length>0?a.height=(d.height??0)+r:a.y=(d.y??0)+r/2,a},"normalizeDagreNode"),v=m((t,e)=>{U.forEach(r=>{e[r]!==void 0&&(t[r]=e[r])})},"applyDagreNodeLayout"),ee=m((t,e,r,d)=>({...t,start:t.start??e,end:t.end??r,points:(t.points??[]).map(a=>({...a,y:typeof a.y=="number"?a.y+d:a.y}))}),"normalizeDagreEdge"),te=m((t,e)=>{const{graph:r,mergeSelfLoops:d,subGraphTitleTotalMargin:a=0}=e,f=new Map(t.nodes.map(c=>[c.id,c]));x(r).forEach(c=>{const l=$(r,c,a);if(!l)return;v(r.node(c),l);const s=f.get(c);s&&v(s,l)});const h=a/2;return t.edges=B(r,h,{mergeSelfLoops:d}).map(({edge:c,start:l,end:s})=>ee(c,l,s,h)),t},"applyDagreLayoutResult"),re=m(async({elem:t,graph:e,groups:{clusters:r,edgePaths:d},diagramType:a,id:f,mergeSelfLoops:h,subGraphTitleTotalMargin:c})=>{let l=0;await Promise.all(x(e).map(async function(o){const n=e.node(o);if(p.info("Position XBX => "+o+": ("+n.x,","+n.y,") width: ",n.width," height: ",n.height),n?.clusterNode)n.y+=c,p.info("A tainted cluster node XBX1",o,n.id,n.width,n.height,n.x,n.y,e.parent(o)),b.get(n.id).node=n,R(n);else if(e.children(o).length>0){p.info("A pure cluster node XBX1",o,n.id,n.x,n.y,n.width,n.height,e.parent(o)),n.height+=c,e.node(n.parentId);const L=n?.padding/2||0,w=n?.labelBBox?.height||0,y=w-L||0;p.debug("OffsetY",y,"labelHeight",w,"halfPadding",L),await F(r,n),b.get(n.id).node=n}else{const L=e.node(n.parentId);n.y+=c/2,p.info("A regular node XBX1 - using the padding",n.id,"parent",n.parentId,n.width,n.height,n.x,n.y,"offsetY",n.offsetY,"parent",L,L?.offsetY,n),R(n)}}));const s=c/2;return B(e,s,{mergeSelfLoops:h}).forEach(function({edge:o,start:n,end:L}){p.info("Edge "+n+" -> "+L+": "+JSON.stringify(o),o),o.points.forEach(u=>u.y+=s);const w=e.node(n),y=e.node(L),g=q(d,o,b,a,w,y,f);W(o,g)}),e.nodes().forEach(function(o){const n=e.node(o);p.info(o,n.type,n.diff),n.isGroup&&(l=n.diff)}),p.warn("Returning from recursive render XAX",t,l),{elem:t,diff:l}},"paintDagreLayoutCore"),oe=m(async t=>{const e=await I(t);return M(e.graph),await re(e)},"renderDagreSubgraph"),G=m(t=>{const e=new O({multigraph:!0,compound:!0}).setGraph({rankdir:t.direction,nodesep:t.config?.nodeSpacing||t.nodeSpacing||t.config?.flowchart?.nodeSpacing,ranksep:t.config?.rankSpacing||t.rankSpacing||t.config?.flowchart?.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});return t.nodes.forEach(r=>{e.setNode(r.id,{...r}),r.parentId&&e.setParent(r.id,r.parentId)}),p.debug("Edges:",t.edges),t.edges.forEach(r=>{if(r.start===r.end){const d=r.start,a=d+"---"+d+"---1",f=d+"---"+d+"---2",h=e.node(d);e.setNode(a,{domId:a,id:a,parentId:h.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),e.setParent(a,h.parentId),e.setNode(f,{domId:f,id:f,parentId:h.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),e.setParent(f,h.pa
Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
function J(n){return Math.abs(n=Math.round(n))>=1e21?n.toLocaleString("en").replace(/,/g,""):n.toString(10)}function j(n,t){if(!isFinite(n)||n===0)return null;var e=(n=t?n.toExponential(t-1):n.toExponential()).indexOf("e"),i=n.slice(0,e);return[i.length>1?i[0]+i.slice(2):i,+n.slice(e+1)]}function K(n){return n=j(Math.abs(n)),n?n[1]:NaN}function Q(n,t){return function(e,i){for(var o=e.length,a=[],c=0,h=n[0],M=0;o>0&&h>0&&(M+h+1>i&&(h=Math.max(1,i-M)),a.push(e.substring(o-=h,o+h)),!((M+=h+1)>i));)h=n[c=(c+1)%n.length];return a.reverse().join(t)}}function V(n){return function(t){return t.replace(/[0-9]/g,function(e){return n[+e]})}}var W=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function $(n){if(!(t=W.exec(n)))throw new Error("invalid format: "+n);var t;return new L({fill:t[1],align:t[2],sign:t[3],symbol:t[4],zero:t[5],width:t[6],comma:t[7],precision:t[8]&&t[8].slice(1),trim:t[9],type:t[10]})}$.prototype=L.prototype;function L(n){this.fill=n.fill===void 0?" ":n.fill+"",this.align=n.align===void 0?">":n.align+"",this.sign=n.sign===void 0?"-":n.sign+"",this.symbol=n.symbol===void 0?"":n.symbol+"",this.zero=!!n.zero,this.width=n.width===void 0?void 0:+n.width,this.comma=!!n.comma,this.precision=n.precision===void 0?void 0:+n.precision,this.trim=!!n.trim,this.type=n.type===void 0?"":n.type+""}L.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function _(n){n:for(var t=n.length,e=1,i=-1,o;e<t;++e)switch(n[e]){case".":i=o=e;break;case"0":i===0&&(i=e),o=e;break;default:if(!+n[e])break n;i>0&&(i=0);break}return i>0?n.slice(0,i)+n.slice(o+1):n}var N;function v(n,t){var e=j(n,t);if(!e)return N=void 0,n.toPrecision(t);var i=e[0],o=e[1],a=o-(N=Math.max(-8,Math.min(8,Math.floor(o/3)))*3)+1,c=i.length;return a===c?i:a>c?i+new Array(a-c+1).join("0"):a>0?i.slice(0,a)+"."+i.slice(a):"0."+new Array(1-a).join("0")+j(n,Math.max(0,t+a-1))[0]}function X(n,t){var e=j(n,t);if(!e)return n+"";var i=e[0],o=e[1];return o<0?"0."+new Array(-o).join("0")+i:i.length>o+1?i.slice(0,o+1)+"."+i.slice(o+1):i+new Array(o-i.length+2).join("0")}const O={"%":(n,t)=>(n*100).toFixed(t),b:n=>Math.round(n).toString(2),c:n=>n+"",d:J,e:(n,t)=>n.toExponential(t),f:(n,t)=>n.toFixed(t),g:(n,t)=>n.toPrecision(t),o:n=>Math.round(n).toString(8),p:(n,t)=>X(n*100,t),r:X,s:v,X:n=>Math.round(n).toString(16).toUpperCase(),x:n=>Math.round(n).toString(16)};function R(n){return n}var U=Array.prototype.map,Y=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function nn(n){var t=n.grouping===void 0||n.thousands===void 0?R:Q(U.call(n.grouping,Number),n.thousands+""),e=n.currency===void 0?"":n.currency[0]+"",i=n.currency===void 0?"":n.currency[1]+"",o=n.decimal===void 0?".":n.decimal+"",a=n.numerals===void 0?R:V(U.call(n.numerals,String)),c=n.percent===void 0?"%":n.percent+"",h=n.minus===void 0?"−":n.minus+"",M=n.nan===void 0?"NaN":n.nan+"";function T(f,g){f=$(f);var b=f.fill,p=f.align,m=f.sign,w=f.symbol,S=f.zero,E=f.width,F=f.comma,y=f.precision,C=f.trim,d=f.type;d==="n"?(F=!0,d="g"):O[d]||(y===void 0&&(y=12),C=!0,d="g"),(S||b==="0"&&p==="=")&&(S=!0,b="0",p="=");var q=(g&&g.prefix!==void 0?g.prefix:"")+(w==="$"?e:w==="#"&&/[boxX]/.test(d)?"0"+d.toLowerCase():""),B=(w==="$"?i:/[%p]/.test(d)?c:"")+(g&&g.suffix!==void 0?g.suffix:""),D=O[d],H=/[defgprs%]/.test(d);y=y===void 0?6:/[gprs]/.test(d)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y));function G(r){var l=q,u=B,x,I,k;if(d==="c")u=D(r)+u,r="";else{r=+r;var P=r<0||1/r<0;if(r=isNaN(r)?M:D(Math.abs(r),y),C&&(r=_(r)),P&&+r==0&&m!=="+"&&(P=!1),l=(P?m==="("?m:h:m==="-"||m==="("?"":m)+l,u=(d==="s"&&!isNaN(r)&&N!==void 0?Y[8+N/3]:"")+u+(P&&m==="("?")":""),H){for(x=-1,I=r.length;++x<I;)if(k=r.charCodeAt(x),48>k||k>57){u=(k===46?o+r.slice(x+1):r.slice(x))+u,r=r.slice(0,x);break}}}F&&!S&&(r=t(r,1/0));var z=l.length+r.length+u.length,s=z<E?new Array(E-z+1).join(b):"";switch(F&&S&&(r=t(s+r,s.length?E-u.length:1/0),s=""),p){case"<":r=l+r+u+s;break;case"=":r=l+s+r+u;break;case"^":r=s.slice(0,z=s.length>>1)+l+r+u+s.slice(z);break;default:r=s+l+r+u;break}return a(r)}return G.toString=function(){return f+""},G}function Z(f,g){var b=Math.max(-8,Math.min(8,Math.floor(K(g)/3)))*3,p=Math.pow(10,-b),m=T((f=$(f),f.type="f",f),{suffix:Y[8+b/3]});return function(w){return m(p*w)}}return{format:T,formatPrefix:Z}}var A,tn,rn;en({thousands:",",grouping:[3],currency:["$",""]});function en(n){return A=nn(n),tn=A.format,rn=A.formatPrefix,A}export{rn as a,tn as b,K as e,$ as f};
|
||||
@@ -0,0 +1,30 @@
|
||||
import{I as X}from"./chunk-2Q5K7J3B.js";import{p as z}from"./chunk-JWPE2WC7.js";import{n as O,b as G,s as Y,o as P,g as F,a as Z,_ as g,A as N,l as A,D as q,d as U,y as E,p as j,i as J,ak as K,B as Q,al as ee}from"./mermaid.core.js";import{p as te}from"./cynefin-OW5HDTMX.js";import"./index.js";var D=/[─━│┃└┗├┣]/,L=/[└┗├┣]/,ne=/[─━]/,k=/^[\s│┃]+$/,T=/^\s*(title[\t ]|accTitle[\t ]*:|accDescr[\t ]*[:{])/,V=/^\s*%%/,re=" ";function _(n){return n.some(t=>D.test(t))}g(_,"isBoxDrawingFormat");function M(n){for(const t of n){const e=L.exec(t);if(e?.index&&e.index>0)return e.index}return 4}g(M,"inferSegmentWidth");function S(n,t){return n.replace(/\bline\s+(\d+)\b/gi,(e,r)=>{const s=parseInt(r,10),i=t.get(s);return i?`line ${i}`:e})}g(S,"remapErrorLines");function R(n){const t=n.split(`
|
||||
`),e=new Map;let r=-1;for(const[p,c]of t.entries())if(c.trim()==="treeView-beta"){r=p;break}if(r===-1)return{text:n,lineMap:e};const s=[];for(let p=r+1;p<t.length;p++){const c=t[p];c.trim()===""||V.test(c)||T.test(c)||k.test(c)||s.push(c.replace(/\t/g," "))}if(!_(s))return{text:n,lineMap:e};const i=M(s),o=[];let a=0;for(let p=0;p<=r;p++)o.push(t[p]),a++,e.set(a,p+1);for(let p=r+1;p<t.length;p++){const c=t[p],d=c.trim(),h=p+1;if(d===""){o.push(c),a++,e.set(a,h);continue}if(V.test(c)){o.push(c),a++,e.set(a,h);continue}if(T.test(c)){o.push(c),a++,e.set(a,h);continue}if(k.test(c))continue;const l=c.replace(/\t/g," "),w=L.exec(l);if(w?.index!==void 0){const f=w.index,m=Math.round(f/i)+1;let u=f+1;for(;u<l.length&&ne.test(l[u]);)u++;for(;u<l.length&&l[u]===" ";)u++;const v=l.slice(u).trimEnd();if(!v)throw new Error(`Line ${h}: Empty node — expected a filename or directory name after the box-drawing prefix`);const W=re.repeat(m);o.push(W+v),a++,e.set(a,h)}else{if(/^[\s─━│┃└┗├┣]+$/.test(l))continue;if(D.test(l))o.push(c),a++,e.set(a,h);else{if(/^\s+/.test(l))throw new Error(`Line ${h}: Unexpected indentation without box-drawing characters. In box-drawing format, use ├── or └── prefixes for indented nodes.`);o.push(c),a++,e.set(a,h)}}}return{text:o.join(`
|
||||
`),lineMap:e}}g(R,"preprocessBoxDrawing");var x=new X(()=>({cnt:1,stack:[{id:0,level:-1,name:"/",nodeType:"directory",children:[]}]})),ie=g(()=>{x.reset(),j()},"clear"),oe=g(()=>x.records.stack[0],"getRoot"),se=g(()=>x.records.cnt,"getCount"),ae=Q.treeView,ce=g(()=>N(ae,E().treeView),"getConfig"),le=g((n,t,e,r,s,i)=>{for(;n<=x.records.stack[x.records.stack.length-1].level;)x.records.stack.pop();const o={id:x.records.cnt++,level:n,name:t,nodeType:e,icon:s,cssClass:r,description:i,children:[]};x.records.stack[x.records.stack.length-1].children.push(o),x.records.stack.push(o)},"addNode"),de={clear:ie,addNode:le,getRoot:oe,getCount:se,getConfig:ce,getAccTitle:Z,getAccDescription:F,getDiagramTitle:P,setAccDescription:Y,setAccTitle:G,setDiagramTitle:O},I=de,he=g(n=>{z(n,I);for(const t of n.nodes){const e=typeof t.indent=="number"?t.indent:0;let r=t.name;const s=r.endsWith("/");s&&(r=r.slice(0,-1));const i=s?"directory":"file",o=t.classAnnotation||void 0,a=t.iconAnnotation,p=a!==void 0?a||"none":void 0,c=t.descAnnotation||void 0,d=c?J(c,E()):void 0;I.addNode(e,r,i,o,p,d)}},"populate"),pe={parse:g(async n=>{const{text:t,lineMap:e}=R(n);try{const r=await te("treeView",t);A.debug(r),he(r)}catch(r){throw e.size>0&&r instanceof Error&&(r.message=S(r.message,e)),r}},"parse")},b={prefix:"mermaid-treeview",height:24,width:24,icons:{folder:{body:'<path fill="currentColor" d="M10.59 4.59A2 2 0 0 0 9.17 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.17z"/>'},file:{body:'<path fill="currentColor" fill-rule="evenodd" d="M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8.83a2 2 0 0 0-.59-1.42l-4.82-4.82A2 2 0 0 0 13.17 2H6Zm7.5 1.9l4.6 4.6h-3.6a1 1 0 0 1-1-1V3.9Z" clip-rule="evenodd"/>'}}};function H(n,t){const e=t?.filenameIcons?.[n];if(e)return e;const r=n.lastIndexOf(".");if(r>0){const s=n.substring(r).toLowerCase(),i=t?.extensionIcons;return i?.[s]??i?.[s.slice(1)]}}g(H,"detectIcon");function C(n,t){return n.includes(":")?n:n in b.icons||!t?`${b.prefix}:${n}`:`${t}:${n}`}g(C,"qualifyIcon");function B(n,t){if(n.icon!=="none"){if(n.icon)return C(n.icon,t.defaultIconPack);if(t.showIcons){if(n.nodeType==="file"){const e=H(n.name,t);if(e==="none")return;if(e)return C(e,t.defaultIconPack)}return`${b.prefix}:${n.nodeType==="directory"?"folder":"file"}`}}}g(B,"getNodeIcon");ee([{name:b.prefix,icons:b}]);var y=14,ge=4,fe=16,ue=g(async(n,t)=>{const e=[],r=g(i=>{const o=B(i,t);o&&e.push({icon:o,node:i}),i.children.forEach(r)},"collect");r(n);const s=await Promise.all(e.map(async({icon:i,node:o})=>({id:o.id,svg:await K(i,{height:y,width:y})})));return new Map(s.map(({id:i,svg:o})=>[i,o]))},"resolveNodeIcons"),we=g((n,t,e,r,s,i)=>{const o=r.append("g");let a="treeView-node-label";e.nodeType==="directory"&&(a+=" treeView-node-dir"),e.cssClass&&(a+=` ${e.cssClass}`);const p=y+ge,c=B(e,s),d=c!==void 0;c&&o.append("g").attr("class","treeView-node-icon").attr("transform",`translate(${n+s.paddingX}, ${t+s.paddingY})`).html(i.get(e.id)??"");const h=o.append("text").text(e.name).attr("dominant-baseline","middle").attr("class",a),{height:l,width:w}=h.node().getBBox(),f=l+s.paddingY*2,m=n+s.paddingX+(d?p:0);h.attr("x",m),h.attr("y",t+f/2);const u=m+w,v=w+s.paddingX*2+(d?p:0);return e.BBox={x:n,y:t,width:v,height:f},e.cssClass?.split(/\s+/).includes("highlight")&&o.insert("rect",":first-child").attr("x",n).attr("y",t+1).attr("width",0).attr("height",f-2).attr("rx",3).attr("class","treeView-highlight-bg"),{node:e,nodeGroup:o,labelRightEdge:u,centerY:t+f/2}},"positionLabel"),$=g((n,t,e,r,s,i)=>n.append("line").attr("x1",t).attr("y1",e).attr("x2",r).attr("y2",s).attr("stroke-width",i).attr("class","treeView-node-line"),"positionLine"),me=g((n,t,e,r)=>{let s=0,i=0;const o=[],a=g((d,h,l,w)=>{const f=w*(l.rowIndent+l.paddingX),m=we(f,s,h,d,l,r);o.push(m);const{height:u,width:v}=h.BBox;$(d,f-l.rowIndent,s+u/2,f,s+u/2,l.lineThickness),i=Math.max(i,f+v),s+=u},"drawNode"),p=g((d,h=0)=>{a(n,d,e,h),d.children.forEach(m=>{p(m,h+1)});const{x:l,y:w,height:f}=d.BBox;if(d.children.length){const{y:m,height:u}=d.children[d.children.length-1].BBox;$(n,l+e.paddingX,w+f,l+e.paddingX,m+u/2+e.lineThickness/2,e.lineThickness)}},"processNode");p(t);const c=o.filter(d=>d.node.description);if(c.length>0){const h=Math.max(...o.map(l=>l.labelRightEdge))+fe;for(const l of c){const f=l.nodeGroup.append("text").text(l.node.description).attr("dominant-baseline","middle").attr("class","treeView-node-description").attr("x",h).attr("y",l.centerY).node().getBBox();i=Math.max(i,h+f.width+e.paddingX)}}for(const d of o)if(d.node.cssClass?.split(/\s+/).includes("highlight")){const h=d.nodeGroup.select(".treeView-highlight-bg");if(!h.empty()){const l=i-d.node.BBox.x+8;h.attr("width",l),i=Math.max(i,d.node.BBox.x+l+2)}}return{totalHeight:s,totalWidth:i}},"drawTree"),xe=g(async(n,t,e,r)=>{A.debug(`Rendering treeView diagram
|
||||
`+n);const s=r.db,i=s.getRoot(),o=s.getConfig(),a=q(t),p=a.append("g");p.attr("class","tree-view");const c=await ue(i,o),{totalHeight:d,totalWidth:h}=me(p,i,o,c);a.attr("viewBox",`-${o.lineThickness/2} 0 ${h} ${d}`),U(a,d,h,o.useMaxWidth)},"draw"),ve={draw:xe},be=ve,Ie={labelFontSize:"16px",labelColor:"black",lineColor:"black",iconColor:"#546e7a",descriptionColor:"#6a9955",highlightBg:"rgba(255, 193, 7, 0.15)",highlightStroke:"#ffc107"},Ce=g(({treeView:n})=>{const{labelFontSize:t,labelColor:e,lineColor:r,iconColor:s,descriptionColor:i,highlightBg:o,highlightStroke:a}=N(Ie,n);return`
|
||||
.treeView-node-label {
|
||||
font-size: ${t};
|
||||
fill: ${e};
|
||||
white-space: pre;
|
||||
}
|
||||
.treeView-node-dir {
|
||||
font-weight: bold;
|
||||
}
|
||||
.treeView-node-line {
|
||||
stroke: ${r};
|
||||
}
|
||||
.treeView-node-icon {
|
||||
color: ${s};
|
||||
}
|
||||
.treeView-node-description {
|
||||
font-size: ${t};
|
||||
fill: ${i};
|
||||
font-style: italic;
|
||||
white-space: pre;
|
||||
}
|
||||
.treeView-highlight-bg {
|
||||
fill: ${o};
|
||||
stroke: ${a};
|
||||
stroke-width: 1;
|
||||
}
|
||||
`},"styles"),ye=Ce,Ne={db:I,renderer:be,parser:pe,styles:ye};export{Ne as diagram};
|
||||
@@ -0,0 +1,41 @@
|
||||
import{p as I}from"./chunk-JWPE2WC7.js";import{s as _,g as E,o as D,n as F,a as P,b as z,_ as c,D as G,p as B,A as w,y as C,B as W,l as b,E as V,d as H}from"./mermaid.core.js";import{p as j}from"./cynefin-OW5HDTMX.js";import"./index.js";var x={showLegend:!0,ticks:5,max:null,min:0,graticule:"circle"},y=32,A={axes:[],curves:[],options:x},m=structuredClone(A),U=W.radar,X=c(()=>w({...U,...C().radar}),"getConfig"),M=c(()=>m.axes,"getAxes"),K=c(()=>m.curves,"getCurves"),N=c(()=>m.options,"getOptions"),Y=c(a=>{m.axes=a.map(t=>({name:t.name,label:t.label??t.name}))},"setAxes"),Z=c(a=>{m.curves=a.map(t=>({name:t.name,label:t.label??t.name,entries:q(t.entries)}))},"setCurves"),q=c(a=>{if(a[0].axis==null)return a.map(e=>e.value);const t=M();if(t.length===0)throw new Error("Axes must be populated before curves for reference entries");return t.map(e=>{const r=a.find(s=>s.axis?.$refText===e.name);if(r===void 0)throw new Error("Missing entry for axis "+e.label);return r.value})},"computeCurveEntries"),J=c(a=>{const t=a.reduce((e,r)=>(e[r.name]=r,e),{});m.options={showLegend:t.showLegend?.value??x.showLegend,ticks:t.ticks?.value??x.ticks,max:t.max?.value??x.max,min:t.min?.value??x.min,graticule:t.graticule?.value??x.graticule},m.options.ticks>y&&(b.warn(`Radar diagram ticks (${m.options.ticks}) exceeds maximum allowed (${y}). Using ${y} instead.`),m.options.ticks=y)},"setOptions"),Q=c(()=>{B(),m=structuredClone(A)},"clear"),$={getAxes:M,getCurves:K,getOptions:N,setAxes:Y,setCurves:Z,setOptions:J,getConfig:X,clear:Q,setAccTitle:z,getAccTitle:P,setDiagramTitle:F,getDiagramTitle:D,getAccDescription:E,setAccDescription:_},tt=c(a=>{I(a,$);const{axes:t,curves:e,options:r}=a;$.setAxes(t),$.setCurves(e),$.setOptions(r)},"populate"),et={parse:c(async a=>{const t=await j("radar",a);b.debug(t),tt(t)},"parse")},at=c((a,t,e,r)=>{const s=r.db,i=s.getAxes(),l=s.getCurves(),n=s.getOptions(),o=s.getConfig(),d=s.getDiagramTitle(),p=G(t),u=rt(p,o),g=n.max??Math.max(...l.map(f=>Math.max(...f.entries))),h=n.min,v=Math.min(o.width,o.height)/2;st(u,i,v,n.ticks,n.graticule),nt(u,i,v,o),L(u,i,l,h,g,n.graticule,o),k(u,l,n.showLegend,o),u.append("text").attr("class","radarTitle").text(d).attr("x",0).attr("y",-o.height/2-o.marginTop)},"draw"),rt=c((a,t)=>{const e=t.width+t.marginLeft+t.marginRight,r=t.height+t.marginTop+t.marginBottom,s={x:t.marginLeft+t.width/2,y:t.marginTop+t.height/2};return H(a,r,e,t.useMaxWidth??!0),a.attr("viewBox",`0 0 ${e} ${r}`).attr("overflow","visible"),a.append("g").attr("transform",`translate(${s.x}, ${s.y})`)},"drawFrame"),st=c((a,t,e,r,s)=>{if(s==="circle")for(let i=0;i<r;i++){const l=e*(i+1)/r;a.append("circle").attr("r",l).attr("class","radarGraticule")}else if(s==="polygon"){const i=t.length;for(let l=0;l<r;l++){const n=e*(l+1)/r,o=t.map((d,p)=>{const u=2*p*Math.PI/i-Math.PI/2,g=n*Math.cos(u),h=n*Math.sin(u);return`${g},${h}`}).join(" ");a.append("polygon").attr("points",o).attr("class","radarGraticule")}}},"drawGraticule"),nt=c((a,t,e,r)=>{const s=t.length;for(let i=0;i<s;i++){const l=t[i].label,n=2*i*Math.PI/s-Math.PI/2,o=Math.cos(n),d=Math.sin(n);a.append("line").attr("x1",0).attr("y1",0).attr("x2",e*r.axisScaleFactor*o).attr("y2",e*r.axisScaleFactor*d).attr("class","radarAxisLine");const p=o>.01?"start":o<-.01?"end":"middle",u=d>.01?"hanging":d<-.01?"auto":"central",g=4;a.append("text").text(l).attr("x",e*r.axisLabelFactor*o+g*o).attr("y",e*r.axisLabelFactor*d+g*d).attr("text-anchor",p).attr("dominant-baseline",u).attr("class","radarAxisLabel")}},"drawAxes");function L(a,t,e,r,s,i,l){const n=t.length,o=Math.min(l.width,l.height)/2;e.forEach((d,p)=>{if(d.entries.length!==n)return;const u=d.entries.map((g,h)=>{const v=2*Math.PI*h/n-Math.PI/2,f=T(g,r,s,o),O=f*Math.cos(v),R=f*Math.sin(v);return{x:O,y:R}});i==="circle"?a.append("path").attr("d",S(u,l.curveTension)).attr("class",`radarCurve-${p}`):i==="polygon"&&a.append("polygon").attr("points",u.map(g=>`${g.x},${g.y}`).join(" ")).attr("class",`radarCurve-${p}`)})}c(L,"drawCurves");function T(a,t,e,r){const s=Math.min(Math.max(a,t),e);return r*(s-t)/(e-t)}c(T,"relativeRadius");function S(a,t){const e=a.length;let r=`M${a[0].x},${a[0].y}`;for(let s=0;s<e;s++){const i=a[(s-1+e)%e],l=a[s],n=a[(s+1)%e],o=a[(s+2)%e],d={x:l.x+(n.x-i.x)*t,y:l.y+(n.y-i.y)*t},p={x:n.x-(o.x-l.x)*t,y:n.y-(o.y-l.y)*t};r+=` C${d.x},${d.y} ${p.x},${p.y} ${n.x},${n.y}`}return`${r} Z`}c(S,"closedRoundCurve");function k(a,t,e,r){if(!e)return;const s=(r.width/2+r.marginRight)*3/4,i=-(r.height/2+r.marginTop)*3/4,l=20;t.forEach((n,o)=>{const d=a.append("g").attr("transform",`translate(${s}, ${i+o*l})`);d.append("rect").attr("width",12).attr("height",12).attr("class",`radarLegendBox-${o}`),d.append("text").attr("x",16).attr("y",0).attr("class","radarLegendText").text(n.label)})}c(k,"drawLegend");var ot={draw:at},it=c((a,t)=>{let e="";for(let r=0;r<a.THEME_COLOR_LIMIT;r++){const s=a[`cScale${r}`];e+=`
|
||||
.radarCurve-${r} {
|
||||
color: ${s};
|
||||
fill: ${s};
|
||||
fill-opacity: ${t.curveOpacity};
|
||||
stroke: ${s};
|
||||
stroke-width: ${t.curveStrokeWidth};
|
||||
}
|
||||
.radarLegendBox-${r} {
|
||||
fill: ${s};
|
||||
fill-opacity: ${t.curveOpacity};
|
||||
stroke: ${s};
|
||||
}
|
||||
`}return e},"genIndexStyles"),lt=c(a=>{const t=V(),e=C(),r=w(t,e.themeVariables),s=w(r.radar,a);return{themeVariables:r,radarOptions:s}},"buildRadarStyleOptions"),ct=c(({radar:a}={})=>{const{themeVariables:t,radarOptions:e}=lt(a);return`
|
||||
.radarTitle {
|
||||
font-size: ${t.fontSize};
|
||||
color: ${t.titleColor};
|
||||
dominant-baseline: hanging;
|
||||
text-anchor: middle;
|
||||
}
|
||||
.radarAxisLine {
|
||||
stroke: ${e.axisColor};
|
||||
stroke-width: ${e.axisStrokeWidth};
|
||||
}
|
||||
.radarAxisLabel {
|
||||
font-size: ${e.axisLabelFontSize}px;
|
||||
color: ${e.axisColor};
|
||||
}
|
||||
.radarGraticule {
|
||||
fill: ${e.graticuleColor};
|
||||
fill-opacity: ${e.graticuleOpacity};
|
||||
stroke: ${e.graticuleColor};
|
||||
stroke-width: ${e.graticuleStrokeWidth};
|
||||
}
|
||||
.radarLegendText {
|
||||
text-anchor: start;
|
||||
font-size: ${e.legendFontSize}px;
|
||||
dominant-baseline: hanging;
|
||||
}
|
||||
${it(t,e)}
|
||||
`},"styles"),mt={parser:et,db:$,renderer:ot,styles:ct};export{mt as diagram};
|
||||
@@ -0,0 +1,3 @@
|
||||
import{p as re}from"./chunk-JWPE2WC7.js";import{o as oe,n as se,s as de,g as le,a as ce,b as me,_ as o,l as g,c as T,j as ue,z as xe,p as fe,A as ge,y as M,B as he,i as y,w as P,am as pe}from"./mermaid.core.js";import{p as be,i as ve}from"./cynefin-OW5HDTMX.js";import"./index.js";var $="position frame",D="frame positioned",S="position relation",N="relation positioned",we=o(function(e){g.debug("options str",e)},"setOptions"),ye=o(function(){return{}},"getOptions"),Pe=o(function(){C(),fe()},"clear");function C(){B={}}o(C,"reset");var Se=he.eventmodeling,ke=o(()=>ge({...Se,...M().eventmodeling}),"getConfig"),B={};function O(){let e=Fe;const{ast:n}=B,t=E();if(!n)throw new Error("No data for EventModel");return n.frames.forEach((i,a)=>{const r=G(i,n.dataEntities,t);e=v(e,{$kind:$,index:a,frame:i,textProps:r});let d;K(i)?(g.debug("source frame",i.sourceFrames),d=n.frames.filter(l=>i.sourceFrames.some(c=>c.$refText===l.name)),d.forEach(l=>{e=v(e,{$kind:S,index:a,frame:i,sourceFrame:l})})):e=v(e,{$kind:S,index:a,frame:i})}),e={...e,sortedSwimlanesArray:A(e.swimlanes)},e}o(O,"getState");function I(e){B.ast=e}o(I,"setAst");var s={swimlaneMinHeight:70,swimlanePadding:15,swimlaneGap:10,boxPadding:10,boxOverlap:90,boxDefaultY:0,boxMinWidth:80,boxMaxWidth:450,boxMinHeight:80,boxMaxHeight:750,contentStartX:250,textMaxWidth:430,boxTextFontWeight:"bold",boxTextPadding:10,swimlaneTextFontWeight:"bold",labelUiAutomation:"UI/Automation",labelUiAutomationPrefix:"UI/A: ",labelCommandReadModel:"Command/Read Model",labelCommandReadModelPrefix:"C/RM: ",labelEvents:"Events",labelEventsPrefix:"Stream: "};function E(){return s}o(E,"getDiagramProps");var Fe={boxes:[],swimlanes:{},relations:[],maxR:0,sortedSwimlanesArray:[]};function W(e){const n=e.split(".");if(n.length===2)return n[0]}o(W,"extractNamespace");function H(e){const n=e.split(".");return n.length===2?n[1]:e}o(H,"extractName");function U(e,n){if(!(!n||n.length===0))return Object.values(e).find(t=>t.namespace===n)}o(U,"findSwimlaneByNamespace");function b(e,n,t){return Math.max(n,...Object.keys(e).filter(i=>{const a=Number.parseInt(i);return a>n&&a<t}).map(i=>Number.parseInt(i)))+1}o(b,"findNextAvailableIndex");function _(e,n){const t=W(e.entityIdentifier),i=U(n,t);switch(e.modelEntityType){case"ui":case"pcr":case"processor":return i?{index:i.index,label:i.namespace||s.labelUiAutomation}:t?{index:b(n,0,100),label:s.labelUiAutomationPrefix+t}:{index:0,label:s.labelUiAutomation};case"rmo":case"readmodel":case"cmd":case"command":return i?{index:i.index,label:i.namespace||s.labelCommandReadModel}:t?{index:b(n,100,200),label:s.labelCommandReadModelPrefix+t}:{index:100,label:s.labelCommandReadModel};default:return i?{index:i.index,label:i.namespace||s.labelEvents}:t?{index:b(n,200,300),label:s.labelEventsPrefix+t}:{index:200,label:s.labelEvents}}}o(_,"calculateSwimlaneProps");function L(e){const{themeVariables:n}=M();switch(e.modelEntityType){case"ui":return{fill:n.emUiFill??"white",stroke:n.emUiStroke??"#dbdada"};case"pcr":case"processor":return{fill:n.emProcessorFill??"#edb3f6",stroke:n.emProcessorStroke??"#b88cbf"};case"rmo":case"readmodel":return{fill:n.emReadModelFill??"#d3f1a2",stroke:n.emReadModelStroke??"#a3b732"};case"cmd":case"command":return{fill:n.emCommandFill??"#bcd6fe",stroke:n.emCommandStroke??"#679ac3"};case"evt":case"event":return{fill:n.emEventFill??"#ffb778",stroke:n.emEventStroke??"#c19a0f"};default:return{fill:"red",stroke:"black"}}}o(L,"calculateEntityVisualProps");function G(e,n,t){const i=M(),a=y(H(e.entityIdentifier)??"",i);let r;const d={fontSize:16,fontWeight:700,fontFamily:'"trebuchet ms", verdana, arial, sans-serif',joinWith:"<br/>"};let c=`<b>${P(a,t.textMaxWidth,d)}</b>`;if(e.dataInlineValue&&(r=e.dataInlineValue,r=r.substring(r.indexOf("{")+1),r=r.substring(0,r.lastIndexOf("}")-1),r=y(r,i),r=P(r,t.textMaxWidth,d),r=r.replaceAll(" "," ")),e.dataReference){const p=n.find(w=>w.name===e.dataReference?.$refText);p&&(r=p.dataBlockValue,r=r.substring(r.indexOf(`{
|
||||
`)+2),r=r.substring(0,r.lastIndexOf("}")-1),r=y(r,i),r=P(r,t.textMaxWidth,d),r=r.replaceAll(" "," "),r+="<br/>")}const m=r!==void 0;m&&(c+=`<br/><br/><code style="text-align: left; display: block;max-width:${t.textMaxWidth}px">${r}</code>`);const x={fontSize:d.fontSize,fontWeight:d.fontWeight,fontFamily:d.fontFamily},u=pe(c,x),h=m?u.width/3:u.width,f={content:c,width:h,height:u.height};return g.debug(`[${e.name}] ${e.entityIdentifier} text`,f),f}o(G,"calculateTextProps");function V(e,n){const t=n,i=L(t.frame),a={width:t.textProps.width+2*s.boxTextPadding,height:t.textProps.height+2*s.boxTextPadding};return[{$kind:D,frame:t.frame,index:t.index,visual:i,dimension:a,textProps:t.textProps}]}o(V,"decidePositionFrame");function X(e,n,t){return n===void 0?s.contentStartX:n.index===e.index&&e.r?e.r+s.boxPadding:t===void 0?s.contentStartX:t.r-s.boxOverlap+s.boxPadding}o(X,"calculateX");function j(e,n){const t=[...e.map(i=>i.r),n];return Math.max(...t)}o(j,"calculateMaxRight");function A(e){return Object.values(e).sort((n,t)=>n.index-t.index)}o(A,"sortedSwimlanesArray");function Y(e,n){const t=n,i=_(t.frame,e.swimlanes);let a;i.index in e.swimlanes?a=e.swimlanes[i.index]:a={index:i.index,label:i.label,r:0,y:i.index*s.swimlaneMinHeight+s.swimlaneGap,height:s.swimlaneMinHeight,maxHeight:s.swimlaneMinHeight};const r=e.boxes.length>0?e.boxes[e.boxes.length-1]:void 0,d=e.previousSwimlaneNumber!==void 0?e.swimlanes[e.previousSwimlaneNumber]:void 0,l={width:Math.max(s.boxMinWidth,Math.min(s.boxMaxWidth,t.dimension.width))+2*s.boxPadding,height:Math.max(s.boxMinHeight,Math.min(s.boxMaxHeight,t.dimension.height))+2*s.boxPadding},c=X(a,d,r),m=c+l.width+s.boxPadding,x=j(Object.values(e.swimlanes),m);a.r=c+l.width,a.maxHeight=Math.max(a.maxHeight,l.height),a.height=Math.max(s.swimlaneMinHeight,a.maxHeight)+2*s.swimlanePadding;const u={x:c,y:s.swimlanePadding+a.y,r:m,dimension:l,leftSibling:!1,swimlane:a,visual:t.visual,text:t.textProps.content,frame:t.frame,index:t.index},h={...e,boxes:[...e.boxes,u],swimlanes:{...e.swimlanes,[`${a.index}`]:a},previousSwimlaneNumber:i.index,previousFrame:t.frame,maxR:x},f=A(h.swimlanes);f.length>0&&(f[0].y=0);for(let p=1;p<f.length;p++){const w=f[p],R=f[p-1];w.y=R.y+R.height+s.swimlaneGap}return h}o(Y,"evolveFramePositioned");function z(e,n){return e===0&&n.sourceFrames.length===0}o(z,"isFirstFrame");function K(e){return e.sourceFrames!==void 0&&e.sourceFrames!==null&&e.sourceFrames.length>0}o(K,"hasSourceFrame");function k(e,n){if(n!=null)return e.find(t=>t.frame.name===n.name)}o(k,"findBoxByFrame");function q(e,n,t){if(!(t<0))for(let i=t;i>=0;i--){const a=e[i];if(a.swimlane.index!==n)return a}}o(q,"findBoxByLineIndex");function J(e,n){const t=n;if(ve(t.frame)||z(t.index,t.frame))return[];const i=k(e.boxes,t.frame);if(i===void 0)throw new Error(`Target box not found for frame ${t.frame.name}`);let a;return t.sourceFrame?a=k(e.boxes,t.sourceFrame):a=q(e.boxes,i.swimlane.index,t.index-1),a===void 0?[]:[{$kind:N,frame:t.frame,index:t.index,sourceBox:a,targetBox:i}]}o(J,"decidePositionRelation");function Q(e,n){const t=n,i={visual:{fill:"none",stroke:"#000"},source:{x:t.sourceBox.x,y:t.sourceBox.y},target:{x:t.targetBox.x,y:t.targetBox.y},sourceBox:t.sourceBox,targetBox:t.targetBox};return{...e,relations:[...e.relations,i]}}o(Q,"evolveRelationPositioned");var Me={[$]:V,[S]:J},Be={[D]:Y,[N]:Q};function Z(e,n){const t=Me[n.$kind];if(t==null)return[];const i=t(e,n);return g.debug("decided events",i),i}o(Z,"decide");function ee(e,n){const t=n.reduce((i,a)=>{const r=Be[a.$kind];return r==null?i:r(i,a)},e);return g.debug("evolve events",{state:e,newState:t,events:n}),t}o(ee,"evolve");function v(e,n){const t=Z(e,n);return ee(e,t)}o(v,"dispatch");var F={getConfig:ke,setOptions:we,getOptions:ye,clear:Pe,setAccTitle:me,getAccTitle:ce,getAccDescription:le,setAccDescription:de,setDiagramTitle:se,getDiagramTitle:oe,setAst:I,getDiagramProps:E,getState:O},Ee={parse:o(async e=>{const n=await be("eventmodeling",e);g.debug(n),F.setAst(n),re(n,F)},"parse")},Ae=T(),Re=Ae?.eventmodeling;function te(e,n){return t=>{const i=t.swimlane.y+n.swimlanePadding,a=e.append("g").attr("class","em-box");a.append("rect").attr("x",t.x).attr("y",i).attr("rx","3").attr("width",t.dimension.width).attr("height",t.dimension.height).attr("stroke",t.visual.stroke).attr("fill",t.visual.fill),a.append("foreignObject").attr("x",t.x+n.boxPadding).attr("y",i+10).attr("width",t.dimension.width-2*n.boxPadding).attr("height",t.dimension.height-2*n.boxPadding).append("xhtml:div").style("display","table").style("height","100%").style("width","100%").append("span").style("display","table-cell").style("text-align","center").style("vertical-align","middle").html(t.text)}}o(te,"renderD3Box");function ne(e,n){return e>n}o(ne,"dirUpwards");function ie(e,n,t,i){return a=>{const r=a.sourceBox.swimlane.y+n.swimlanePadding,d=a.targetBox.swimlane.y+n.swimlanePadding,l=ne(r,d),c=a.sourceBox.x+a.sourceBox.dimension.width*2/3,m=a.targetBox.x+a.targetBox.dimeLine truncated
|
||||
`,"id:",n,t),!Re)throw new Error("EventModeling config not found");const a=i.db,{themeVariables:r,eventmodeling:d}=T(),l=ue(`[id="${n}"]`),c=a.getDiagramProps(),m=a.getState(),x=`em-arrowhead-${n}`,u=r.emArrowhead??"#000000";m.sortedSwimlanesArray.forEach(ae(l,m.maxR,c,r)),m.boxes.forEach(te(l,c)),m.relations.forEach(ie(l,c,x,r)),l.append("defs").append("marker").attr("id",x).attr("markerWidth","10").attr("markerHeight","7").attr("refX","10").attr("refY","3.5").attr("orient","auto").append("polygon").attr("points","0 0, 10 3.5, 0 7").attr("fill",u),xe(void 0,l,d?.padding??30,d?.useMaxWidth)},"draw"),$e={draw:Te},De=o(e=>"","getStyles"),Ne=De,He={parser:Ee,db:F,renderer:$e,styles:Ne};export{He as diagram};
|
||||
@@ -0,0 +1,24 @@
|
||||
import{p as ge}from"./chunk-JWPE2WC7.js";import{_ as w,E as ye,y as ae,A as ee,D as Se,d as ve,l as te,bd as B,j as U,b as xe,a as be,n as we,o as Ce,g as Te,s as Le,B as $e,be as Ae,p as Fe}from"./mermaid.core.js";import{s as Ne}from"./chunk-POPQ4Y6H.js";import{p as Me}from"./cynefin-OW5HDTMX.js";import{b as O}from"./defaultLocale.js";import{o as Q}from"./ordinal.js";import"./index.js";import"./init.js";function _e(t){var a=0,l=t.children,n=l&&l.length;if(!n)a=1;else for(;--n>=0;)a+=l[n].value;t.value=a}function ke(){return this.eachAfter(_e)}function ze(t,a){let l=-1;for(const n of this)t.call(a,n,++l,this);return this}function Ve(t,a){for(var l=this,n=[l],r,i,h=-1;l=n.pop();)if(t.call(a,l,++h,this),r=l.children)for(i=r.length-1;i>=0;--i)n.push(r[i]);return this}function De(t,a){for(var l=this,n=[l],r=[],i,h,d,g=-1;l=n.pop();)if(r.push(l),i=l.children)for(h=0,d=i.length;h<d;++h)n.push(i[h]);for(;l=r.pop();)t.call(a,l,++g,this);return this}function Pe(t,a){let l=-1;for(const n of this)if(t.call(a,n,++l,this))return n}function Be(t){return this.eachAfter(function(a){for(var l=+t(a.data)||0,n=a.children,r=n&&n.length;--r>=0;)l+=n[r].value;a.value=l})}function Ee(t){return this.eachBefore(function(a){a.children&&a.children.sort(t)})}function Re(t){for(var a=this,l=We(a,t),n=[a];a!==l;)a=a.parent,n.push(a);for(var r=n.length;t!==l;)n.splice(r,0,t),t=t.parent;return n}function We(t,a){if(t===a)return t;var l=t.ancestors(),n=a.ancestors(),r=null;for(t=l.pop(),a=n.pop();t===a;)r=t,t=l.pop(),a=n.pop();return r}function He(){for(var t=this,a=[t];t=t.parent;)a.push(t);return a}function Ie(){return Array.from(this)}function Oe(){var t=[];return this.eachBefore(function(a){a.children||t.push(a)}),t}function Ge(){var t=this,a=[];return t.each(function(l){l!==t&&a.push({source:l.parent,target:l})}),a}function*qe(){var t=this,a,l=[t],n,r,i;do for(a=l.reverse(),l=[];t=a.pop();)if(yield t,n=t.children)for(r=0,i=n.length;r<i;++r)l.push(n[r]);while(l.length)}function ne(t,a){t instanceof Map?(t=[void 0,t],a===void 0&&(a=Ye)):a===void 0&&(a=je);for(var l=new Z(t),n,r=[l],i,h,d,g;n=r.pop();)if((h=a(n.data))&&(g=(h=Array.from(h)).length))for(n.children=h,d=g-1;d>=0;--d)r.push(i=h[d]=new Z(h[d])),i.parent=n,i.depth=n.depth+1;return l.eachBefore(Ze)}function Xe(){return ne(this).eachBefore(Ue)}function je(t){return t.children}function Ye(t){return Array.isArray(t)?t[1]:null}function Ue(t){t.data.value!==void 0&&(t.value=t.data.value),t.data=t.data.data}function Ze(t){var a=0;do t.height=a;while((t=t.parent)&&t.height<++a)}function Z(t){this.data=t,this.depth=this.height=0,this.parent=null}Z.prototype=ne.prototype={constructor:Z,count:ke,each:ze,eachAfter:De,eachBefore:Ve,find:Pe,sum:Be,sort:Ee,path:Re,ancestors:He,descendants:Ie,leaves:Oe,links:Ge,copy:Xe,[Symbol.iterator]:qe};function Je(t){if(typeof t!="function")throw new Error;return t}function G(){return 0}function q(t){return function(){return t}}function Ke(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function Qe(t,a,l,n,r){for(var i=t.children,h,d=-1,g=i.length,c=t.value&&(n-a)/t.value;++d<g;)h=i[d],h.y0=l,h.y1=r,h.x0=a,h.x1=a+=h.value*c}function et(t,a,l,n,r){for(var i=t.children,h,d=-1,g=i.length,c=t.value&&(r-l)/t.value;++d<g;)h=i[d],h.x0=a,h.x1=n,h.y0=l,h.y1=l+=h.value*c}var tt=(1+Math.sqrt(5))/2;function at(t,a,l,n,r,i){for(var h=[],d=a.children,g,c,p=0,b=0,s=d.length,x,S,v=a.value,u,y,N,$,V,W,M;p<s;){x=r-l,S=i-n;do u=d[b++].value;while(!u&&b<s);for(y=N=u,W=Math.max(S/x,x/S)/(v*t),M=u*u*W,V=Math.max(N/M,M/y);b<s;++b){if(u+=c=d[b].value,c<y&&(y=c),c>N&&(N=c),M=u*u*W,$=Math.max(N/M,M/y),$>V){u-=c;break}V=$}h.push(g={value:u,dice:x<S,children:d.slice(p,b)}),g.dice?Qe(g,l,n,r,v?n+=S*u/v:i):et(g,l,n,v?l+=x*u/v:r,i),v-=u,p=b}return h}const nt=(function t(a){function l(n,r,i,h,d){at(a,n,r,i,h,d)}return l.ratio=function(n){return t((n=+n)>1?n:1)},l})(tt);function lt(){var t=nt,a=!1,l=1,n=1,r=[0],i=G,h=G,d=G,g=G,c=G;function p(s){return s.x0=s.y0=0,s.x1=l,s.y1=n,s.eachBefore(b),r=[0],a&&s.eachBefore(Ke),s}function b(s){var x=r[s.depth],S=s.x0+x,v=s.y0+x,u=s.x1-x,y=s.y1-x;u<S&&(S=u=(S+u)/2),y<v&&(v=y=(v+y)/2),s.x0=S,s.y0=v,s.x1=u,s.y1=y,s.children&&(x=r[s.depth+1]=i(s)/2,S+=c(s)-x,v+=h(s)-x,u-=d(s)-x,y-=g(s)-x,u<S&&(S=u=(S+u)/2),y<v&&(v=y=(v+y)/2),t(s,S,v,u,y))}return p.round=function(s){return arguments.length?(a=!!s,p):a},p.size=function(s){return arguments.length?(l=+s[0],n=+s[1],p):[l,n]},p.tile=function(s){return arguments.length?(t=Je(s),p):t},p.padding=function(s){return arguments.length?p.paddingInner(s).paddingOuter(s):p.paddingInner()},p.paddingInner=function(s){return arguments.length?(i=typeof s=="function"?s:q(+s),p):i},p.paddingOuter=function(s){return arguments.length?p.paddingTop(s).paddingRight(s).paddingBottom(s).paddingLeft(s):p.paddingTop()},p.paddingTop=function(s){return arguments.length?(h=typeof s=="function"?s:q(+s),p):h},p.paddingRight=function(s){return arguments.length?(d=typeof s=="function"?s:q(+s),p)Line truncated
|
||||
.treemapNode.section {
|
||||
stroke: ${r.sectionStrokeColor};
|
||||
stroke-width: ${r.sectionStrokeWidth};
|
||||
fill: ${r.sectionFillColor};
|
||||
}
|
||||
.treemapNode.leaf {
|
||||
stroke: ${r.leafStrokeColor};
|
||||
stroke-width: ${r.leafStrokeWidth};
|
||||
fill: ${r.leafFillColor};
|
||||
}
|
||||
.treemapLabel {
|
||||
fill: ${h};
|
||||
font-size: ${r.labelFontSize};
|
||||
}
|
||||
.treemapValue {
|
||||
fill: ${d};
|
||||
font-size: ${r.valueFontSize};
|
||||
}
|
||||
.treemapTitle {
|
||||
fill: ${i};
|
||||
font-size: ${r.titleFontSize};
|
||||
}
|
||||
`},"getStyles"),ut=pt,Ct={parser:he,get db(){return new oe},renderer:ht,styles:ut};export{Ct as diagram};
|
||||
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user