prreviewbuddy 0.91.1 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/main.js +9 -4
- package/dist/{mcp_tools-BR7_XprF.js → mcp_tools-DbFR9Z90.js} +311 -55
- package/dist/server.js +252 -71
- package/package.json +1 -1
- package/static/{chunk-DMxtqzNj.js → chunk-Cg4cN8Vk.js} +1 -1
- package/static/reviews.js +1 -1
- package/static/runs.js +1 -1
- package/static/shell.css +31 -10
- package/static/sidebar.css +25 -6
- package/static/workspace.js +76 -63
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $a as
|
|
1
|
+
import { $a as versionFrom, $i as isExplanationAction, $n as hasAuthorContent, $r as latestKissRun, $t as waitingHistory, A as settleQueue, Aa as isCommitReview, Ai as deleteCollection, An as markRead, Ar as endedAt, At as askCheckout, B as humanizeModelId, Ba as clearAgentPreference, Bi as readQueue, Bn as unreadCount$1, Br as progressSteps, Bt as runJob, C as parsePullRequestLink, Ca as describeAuthorship, Ci as lifecyclePillLabel, Cn as categoryOf, Cr as reviewedShaOf, Ct as RefreshUnavailableError, D as activateQueued, Da as originRemote, Di as collectionsStamp, Dn as eventName, Dr as RUN_RECORD_RETENTION_MS, E as relativeTime$1, Ea as resolveTarget, Ei as sectionOf$1, En as createPrUpdate, Er as fillFileUrlTemplate, Et as mergeUpdateInto, Fa as git, Fi as updateCollections, Fn as readStateOf, Fr as saveJob, Ft as failureOf$1, G as reviewTag, Ga as AGENT_IDS, Gi as questionsOutstanding, Gn as saveRefresh, H as findingRef, Ha as writeAgentPreference, Hi as isIssueOutstanding, Hn as existingPaths, Ia as refExists, Ii as queueAdd, In as removeNotification, It as observeReview, Ja as agentIdentityOf, Ji as verdictClosure, Jn as agentEnvOf, Jr as groupByLineage, Jt as telemetryUploadConsent, K as reviewTagsIn, Ka as agentById, Ki as resolvedByConversation, Kn as storedConversation, Kr as decideKissRecommendation, L as modelProvenanceOf, La as resolveAgent, Li as queueAddMany, Ln as retireNotification, Lr as isLiveJob, Lt as prepareReview, Ma as AmbiguousRefError, Mi as readCollections, Mn as moveIds, Mr as isTerminal, Mt as UpdateAlreadyRunningError, N as cliEntry, Na as shortNameOf, Ni as renameCollection, Nn as pruneNotifications, Nr as loadJob, Nt as liveUpdateFor, O as compactQueue, Oa as DetachedHeadError, Oi as createCollection, On as listNotifications, Or as allJobs, Ot as recheckConversation, Pa as shorthandOf, Pi as setMembership, Pn as readNotification, Pr as pruneJobResidue, Pt as runningJobs, Q as markAskRunning, Qa as runnableAgents, Qi as explanationOf, Qn as authorPreview, Qr as kissDecisionsOf, Qt as feedbackUrl, R as provenanceDetailLines, Ra as resolveModel, Ri as queueRemove, Rn as toneOf, Rr as isTerminalJobPhase, Rt as settleReview, S as extractPullRequestLinks, Sa as objectWithAnyOf, Si as lifecyclePillAction, Sn as subjectOf, Sr as followedBranch, St as FETCH_TIMEOUT_MS, T as runBatch, Ta as kissOutcome, Ti as parseLifecycle, Tn as createPrState, Tr as forgeResolver, Tt as cancellableHere, U as findingRefs, Ui as isQuestionOutstanding, Un as ASK_REREAD_CEILING_MS, Ut as readEvents, V as findingAtRef, Va as readAgentPreference, Vi as doneVerb, Vn as withUsageRecorded, Vr as purposeOf, W as refOf, Wa as STORE_ROOT, Wi as issuesOutstanding, Wn as prepareConversationWithin, Wr as isClaimed, Wt as record, X as isRequestId, Xa as isAgentId, Xi as recommendationsOf, Xn as chatConversationSection, Xr as indexReviewGroups, Xt as BUILD_VERSION, Ya as detectAgents, Yi as processDiscussion, Yn as EXPLAIN_SIMPLY_PROMPT, Yr as indexKeptIds, Za as parkedReason, Zi as explanationActionOf, Zn as UNESTABLISHED_MOTIVATION, Zr as kissDecisionRefusal, _ as endRun, _a as wasReassessed, _i as activityLabel, _n as ancestorsOf, _t as parseWorkspacePort, a as lineageIds, aa as closesFinding, an as ARCHITECTURE_DIR, ar as renderInlineText, b as stopRun, ba as asString, bi as lifecycleItemLabel, bn as announceJob, br as checkCodeFreshness, bt as writePortPreference, c as discardJob, ca as findingIn, ci as recentWorkspaces, cn as partFiles, cr as isBusy, ct as serverEntryExists, d as startKissJob, da as isGradeFor, di as summarise, dn as storeArchitecture, dr as createWorktree, ea as withExplanation, ei as lineageKey, en as mapRuntimes, er as GITHUB_MARKDOWN, f as isDriving, fa as isOffered, fi as touchWorkspace, fn as storedScopes, fr as holdWorktree, g as completeRun, ga as statusLookup, gi as activityAttributes, gn as REPOSITORY_SCOPE, gr as requestJobStop, gt as MIN_WORKSPACE_PORT, h as beginRun, ha as sourcePhrase, hi as LIFECYCLES, hn as QUOTE_LIMITS, hr as removeWorktree, ht as MAX_WORKSPACE_PORT, i as deleteReview, ia as assessmentLookup, ii as mutateWorkspace, in as formatDuration, ir as escapeHtml$1, ja as resolveBase, ji as nameProblemText, jn as markReadForReview, jr as fail$1, jt as findingsChanged, ki as createWithMembers, kn as markAllRead, kr as attemptIdOf, kt as updateReview, l as kissAgentId, la as findingStatus, li as reviewedCommit, ln as readArchitecture, lr as withReviewLock, lt as spawnServer, m as RUN_KINDS, ma as reassessIn, mi as workspaceStamps, mn as ownersOf, mr as readMarker, mt as DEFAULT_WORKSPACE_PORT, n as MCP_TOOL_NAMES, na as NO_STATUS, ni as loadWorkspace, nn as RUNTIME_WORDS, no as AgentUnavailableError, nr as CODE_BLOCK_CLASS, o as liveJobsFor, oa as currentGradeIn, oi as presentedKissRunOf, on as checkArchitecture, ot as readServerRecord, p as track, pa as promptGrade, pi as workspaceRevision, pn as trackedFiles, pr as isWorktreeHeld, pt as writeServerRecord, q as endAskRequest, qa as agentFor$1, qi as reviewerDispositions, qn as isUnchanged, qr as followedRefName, qt as recordTelemetryUploadConsent, r as deleteLineage, ra as analysisRefOf, rn as describeRuntimes, rr as CODE_COPY_CLASS, rt as clearServerRecordOwnedBy, s as wasBlocked, sa as findingAssessment, si as previousKissResult, sn as loadArchitecture, sr as ReviewBeingDeletedError, ta as ANALYSIS_ONLY, ti as lineagePosition, tn as recordRuntimeBestEffort, to as AgentCancelledError, tr as renderMarkdown, tt as readIndexToken, ua as gradeScale, un as repositoryKey, ur as MANAGED_ROOT, ut as stopChild, v as heldRuns, va as followUpFindingId, vi as isLifecycle, vn as isArchitectureId, vt as resolveWorkspacePort, w as unsupportedReason, wa as WITHHELD_REASON, wi as offersLifecycle, wn as createArchitectureNotification, wr as fetchHead, wt as cancelUpdate, x as batchIdentity, xa as extractJsonText, xi as lifecycleName$1, xn as branchOf, xr as checkReview, y as reportRun, ya as SEVERITIES, yi as lifecycleChip, yn as writeJsonAtomically, yr as findExistingReview, yt as statedWorkspacePort, z as agentDisplayName, za as agentIdOf, zi as queueStamp, zn as unreadAround, zr as phasesFor, zt as reanalyseReview } from "./mcp_tools-DbFR9Z90.js";
|
|
2
2
|
import { basename, delimiter, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { execFile, spawn } from "node:child_process";
|
|
4
4
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -1573,6 +1573,7 @@ function tallyEntries(entries) {
|
|
|
1573
1573
|
bySection: {},
|
|
1574
1574
|
verify: 0,
|
|
1575
1575
|
decide: 0,
|
|
1576
|
+
fix: 0,
|
|
1576
1577
|
step: 0
|
|
1577
1578
|
};
|
|
1578
1579
|
for (const entry of entries) {
|
|
@@ -1602,6 +1603,7 @@ function issuesAttentionLine(tally) {
|
|
|
1602
1603
|
const steps = [
|
|
1603
1604
|
tally.verify ? count(tally.verify, "verification step", "verification steps") : "",
|
|
1604
1605
|
tally.decide ? count(tally.decide, "decision", "decisions") : "",
|
|
1606
|
+
tally.fix ? count(tally.fix, "fix", "fixes") : "",
|
|
1605
1607
|
tally.step ? count(tally.step, "next step", "next steps") : ""
|
|
1606
1608
|
].filter(Boolean);
|
|
1607
1609
|
return [count(total, "outstanding issue", "outstanding issues"), ...steps].join(" · ");
|
|
@@ -3443,8 +3445,13 @@ function renderIssuesTab(session, investigatingId, options) {
|
|
|
3443
3445
|
${renderFileLocation(issue.file, "pr-helper-issue-loc", issue.line)}
|
|
3444
3446
|
${renderRelatedFiles(issue.relatedFiles)}
|
|
3445
3447
|
${renderInvestigationProvenance("issue", issue.id, records, now)}`;
|
|
3448
|
+
const actions = `<div class="pr-helper-issue-actions">
|
|
3449
|
+
${renderInvestigationActions("issue", issue.id, records, investigatingId)}
|
|
3450
|
+
${renderDismissToggle(issue, isDismissed, stale, dismissals.get(issue.id), dispositions.isClosedByVerdict(issue))}
|
|
3451
|
+
</div>`;
|
|
3446
3452
|
const body = framed ? `${fixed ? "" : renderConsequence(impact)}${explanation}
|
|
3447
3453
|
${renderReconciliationNote(issue.reconciliationNote)}
|
|
3454
|
+
<div class="pr-helper-card-footer">
|
|
3448
3455
|
<details class="pr-helper-issue-technical">
|
|
3449
3456
|
<summary class="pr-helper-issue-technical-summary">Technical details</summary>
|
|
3450
3457
|
<div class="pr-helper-issue-technical-body">
|
|
@@ -3452,7 +3459,10 @@ function renderIssuesTab(session, investigatingId, options) {
|
|
|
3452
3459
|
<p class="pr-helper-issue-technical-title">${renderInlineText(issue.title)}</p>
|
|
3453
3460
|
${technical}
|
|
3454
3461
|
</div>
|
|
3455
|
-
</details
|
|
3462
|
+
</details>
|
|
3463
|
+
${actions}
|
|
3464
|
+
</div>` : `${technical}
|
|
3465
|
+
${actions}`;
|
|
3456
3466
|
const next = impact?.next ? ` data-issue-next="${impact.next.kind ?? "step"}"` : "";
|
|
3457
3467
|
return `
|
|
3458
3468
|
<div class="pr-helper-issue-card ${cardGradeClass("issue", assessment.current)}${issue.reconciliationStatus ? ` pr-helper-issue-${issue.reconciliationStatus}` : ""}${issue.investigationImpact ? ` pr-helper-issue-investigation-${issue.investigationImpact}` : ""}${isDismissed && !stale ? " pr-helper-issue-card-dismissed" : ""}" data-finding-card="${escapeHtml$1(issue.id)}" data-issue-section="${sectionOf(issue)}"${next}>
|
|
@@ -3476,10 +3486,6 @@ function renderIssuesTab(session, investigatingId, options) {
|
|
|
3476
3486
|
${renderStaleDismissal(stale)}
|
|
3477
3487
|
${renderAssessmentRegion("issue", assessment, grading, statusOf("issue", issue.id))}
|
|
3478
3488
|
${body}
|
|
3479
|
-
<div class="pr-helper-issue-actions">
|
|
3480
|
-
${renderInvestigationActions("issue", issue.id, records, investigatingId)}
|
|
3481
|
-
${renderDismissToggle(issue, isDismissed, stale, dismissals.get(issue.id), dispositions.isClosedByVerdict(issue))}
|
|
3482
|
-
</div>
|
|
3483
3489
|
</div>
|
|
3484
3490
|
`;
|
|
3485
3491
|
};
|
|
@@ -3521,7 +3527,8 @@ function renderConsequence(impact) {
|
|
|
3521
3527
|
}
|
|
3522
3528
|
var STEP_LABELS = {
|
|
3523
3529
|
verify: "To verify:",
|
|
3524
|
-
decide: "To decide:"
|
|
3530
|
+
decide: "To decide:",
|
|
3531
|
+
fix: "To fix:"
|
|
3525
3532
|
};
|
|
3526
3533
|
function renderConsequenceStep(step) {
|
|
3527
3534
|
if (!step) return "";
|
|
@@ -3616,13 +3623,47 @@ function renderQuestionCard(q, sections, answered, records, now = Date.now(), in
|
|
|
3616
3623
|
const answerToggle = `<button type="button" class="pr-helper-question-answer-toggle" data-question-answered="${escapeHtml$1(q.id)}" data-answered="${answered}"${settled ? " data-settled=\"true\"" : ""}${verdictClosed ? " data-verdict-closed=\"true\"" : ""}>${answered ? "Mark as unanswered" : "Mark answered"}</button>`;
|
|
3617
3624
|
const investigationAnswer = q.investigationAnswer?.trim() ? `<h5>Answer from investigation</h5><p class="pr-helper-issue-desc pr-helper-investigation-answer">${renderInlineText(q.investigationAnswer)}</p>` : "";
|
|
3618
3625
|
const reconciliationAnswer = q.reconciliationAnswer?.trim() ? `<h5>${escapeHtml$1(reconciledAnswerHeading(q.reconciledAgainst))}</h5><p class="pr-helper-issue-desc pr-helper-investigation-answer">${renderInlineText(q.reconciliationAnswer)}</p>` : "";
|
|
3626
|
+
const why = q.whyThisMatters ? `<h5>Why this matters</h5><p class="pr-helper-issue-desc">${renderInlineText(q.whyThisMatters)}</p>` : "";
|
|
3627
|
+
const files = q.file || q.relatedFiles?.length ? `<h5>Files</h5>${renderFileLocation(q.file, "pr-helper-issue-loc", void 0)}${renderRelatedFiles(q.relatedFiles)}` : "";
|
|
3628
|
+
const provenance = renderInvestigationProvenance("question", q.id, records, now);
|
|
3629
|
+
const framing = q.framing;
|
|
3630
|
+
const actions = `<div class="pr-helper-question-actions">
|
|
3631
|
+
${jumpLink}
|
|
3632
|
+
<div class="pr-helper-question-actions-group">
|
|
3633
|
+
${renderInvestigationActions("question", q.id, records, investigatingId)}
|
|
3634
|
+
${answerToggle}
|
|
3635
|
+
</div>
|
|
3636
|
+
</div>`;
|
|
3637
|
+
const answers = `${reconciliationAnswer}
|
|
3638
|
+
${renderReconciliationNote(q.reconciliationNote)}
|
|
3639
|
+
${investigationAnswer}`;
|
|
3640
|
+
const body = framing ? `${renderQuestionFraming(framing)}${renderCardExplanation(explanation, options)}
|
|
3641
|
+
${answers}
|
|
3642
|
+
<div class="pr-helper-card-footer">
|
|
3643
|
+
<details class="pr-helper-issue-technical">
|
|
3644
|
+
<summary class="pr-helper-issue-technical-summary">Technical context</summary>
|
|
3645
|
+
<div class="pr-helper-issue-technical-body">
|
|
3646
|
+
<p class="pr-helper-issue-technical-title">${renderInlineText(q.question)}</p>
|
|
3647
|
+
${why}
|
|
3648
|
+
${renderInvestigationNote(q.investigationNote)}
|
|
3649
|
+
${files}
|
|
3650
|
+
${provenance}
|
|
3651
|
+
</div>
|
|
3652
|
+
</details>
|
|
3653
|
+
${actions}
|
|
3654
|
+
</div>` : `${why}${renderCardExplanation(explanation, options)}
|
|
3655
|
+
${answers}
|
|
3656
|
+
${renderInvestigationNote(q.investigationNote)}
|
|
3657
|
+
${files}
|
|
3658
|
+
${provenance}
|
|
3659
|
+
${actions}`;
|
|
3619
3660
|
return `
|
|
3620
3661
|
<div class="pr-helper-question-card${answered ? " pr-helper-question-card-answered" : ""}${q.investigationImpact ? ` pr-helper-issue-investigation-${q.investigationImpact}` : ""}" data-finding-card="${escapeHtml$1(q.id)}"${questionHome(q) ? ` data-section-id="${escapeHtml$1(questionHome(q))}"` : ""}>
|
|
3621
3662
|
<div class="pr-helper-issue-header">
|
|
3622
3663
|
<div class="pr-helper-issue-title-row">
|
|
3623
3664
|
${options?.feedbackSelect ? renderFeedbackPick("question", q.question, questionFeedbackMarkdown(q), ref) : ""}
|
|
3624
3665
|
${renderFindingRef(ref)}
|
|
3625
|
-
<p class="pr-helper-issue-title">${renderInlineText(q.question)}</p>
|
|
3666
|
+
<p class="pr-helper-issue-title">${renderInlineText(framing?.question ?? q.question)}</p>
|
|
3626
3667
|
${renderFindingCopySource(questionMarkdown(q, assessment), "question")}
|
|
3627
3668
|
${options?.askBuddy ? renderAskBuddyMenu("question") : ""}
|
|
3628
3669
|
</div>
|
|
@@ -3639,23 +3680,17 @@ function renderQuestionCard(q, sections, answered, records, now = Date.now(), in
|
|
|
3639
3680
|
</div>
|
|
3640
3681
|
</div>
|
|
3641
3682
|
${renderAssessmentRegion("question", assessment, grading, status)}
|
|
3642
|
-
${
|
|
3643
|
-
${reconciliationAnswer}
|
|
3644
|
-
${renderReconciliationNote(q.reconciliationNote)}
|
|
3645
|
-
${investigationAnswer}
|
|
3646
|
-
${renderInvestigationNote(q.investigationNote)}
|
|
3647
|
-
${q.file || q.relatedFiles?.length ? `<h5>Files</h5>${renderFileLocation(q.file, "pr-helper-issue-loc", void 0)}${renderRelatedFiles(q.relatedFiles)}` : ""}
|
|
3648
|
-
${renderInvestigationProvenance("question", q.id, records, now)}
|
|
3649
|
-
<div class="pr-helper-question-actions">
|
|
3650
|
-
${jumpLink}
|
|
3651
|
-
<div class="pr-helper-question-actions-group">
|
|
3652
|
-
${renderInvestigationActions("question", q.id, records, investigatingId)}
|
|
3653
|
-
${answerToggle}
|
|
3654
|
-
</div>
|
|
3655
|
-
</div>
|
|
3683
|
+
${body}
|
|
3656
3684
|
</div>
|
|
3657
3685
|
`;
|
|
3658
3686
|
}
|
|
3687
|
+
/** What the question means, read first: why the answer matters and what is needed. */
|
|
3688
|
+
function renderQuestionFraming(framing) {
|
|
3689
|
+
return `<div class="pr-helper-issue-impact">
|
|
3690
|
+
<p class="pr-helper-issue-scenario">${renderInlineText(framing.why)}</p>
|
|
3691
|
+
${framing.needed ? `<p class="pr-helper-issue-next pr-helper-question-needed"><span class="pr-helper-issue-next-label">Needed:</span> ${renderInlineText(framing.needed)}</p>` : ""}
|
|
3692
|
+
</div>`;
|
|
3693
|
+
}
|
|
3659
3694
|
/**
|
|
3660
3695
|
* The counter's contents, split out so a caller that toggles one question can refresh it without
|
|
3661
3696
|
* re-rendering the tab.
|
|
@@ -4980,13 +5015,12 @@ ${renderDecisions(processed)}
|
|
|
4980
5015
|
${processed.all.length === 0 ? "<p class=\"prb-pr-empty\">This pull request has no comments yet.</p>" : buckets}`;
|
|
4981
5016
|
}
|
|
4982
5017
|
/**
|
|
4983
|
-
* Re-
|
|
5018
|
+
* Re-checking the discussion, and what the last re-read found.
|
|
4984
5019
|
*
|
|
4985
|
-
*
|
|
4986
|
-
*
|
|
4987
|
-
*
|
|
4988
|
-
*
|
|
4989
|
-
* re-review would be worse off than one who never saw the button.
|
|
5020
|
+
* Two things, said in the order they happen: the threads are fetched and every "already raised"
|
|
5021
|
+
* note corrected, then the findings are reassessed against whatever in the discussion is new to
|
|
5022
|
+
* them. The copy keeps it narrow in the one way that still matters: the code is the commit the
|
|
5023
|
+
* review read, so this is not a re-review and a comment claiming a fix cannot settle it here.
|
|
4990
5024
|
*/
|
|
4991
5025
|
function renderRefresh(refresh) {
|
|
4992
5026
|
if (!refresh?.enabled) return "";
|
|
@@ -4994,10 +5028,12 @@ function renderRefresh(refresh) {
|
|
|
4994
5028
|
return `<div class="prb-pr-refresh">
|
|
4995
5029
|
<button type="button" data-pr-recheck>Re-check the conversation</button>
|
|
4996
5030
|
<span class="prb-pr-refresh-note">
|
|
4997
|
-
Fetches
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5031
|
+
Fetches the description and every thread again, corrects the "Already raised" notes, then
|
|
5032
|
+
reassesses the issues and questions against anything new. A reply can answer a question,
|
|
5033
|
+
resolve or reopen an issue, or suggest a different severity, and a concern it raises becomes a
|
|
5034
|
+
new issue only once it is confirmed in the reviewed code. A comment saying something is fixed
|
|
5035
|
+
is not taken on trust: the issue stays open until an update sees the fix in the code. Newer
|
|
5036
|
+
commits are left for Update review.
|
|
5001
5037
|
</span>
|
|
5002
5038
|
${last ? `<p class="prb-pr-refresh-last" data-ok="${last.ok}">
|
|
5003
5039
|
Last re-checked ${escapeHtml$1(relativeTime$1(last.at))}. ${escapeHtml$1(last.message)}
|
|
@@ -6015,6 +6051,7 @@ function renderFreshAlternative(command) {
|
|
|
6015
6051
|
*/
|
|
6016
6052
|
function renderUpdateOutcome(outcome) {
|
|
6017
6053
|
if (!outcome) return "";
|
|
6054
|
+
if (outcome.scope === "conversation") return renderRecheckOutcome(outcome);
|
|
6018
6055
|
const state = outcome.ok ? "done" : outcome.stop === "refused" ? "refused" : outcome.stop === "cancelled" ? "cancelled" : "stopped";
|
|
6019
6056
|
const title = outcome.ok ? "Review updated" : outcome.stop === "refused" ? "There was nothing to update against" : outcome.stop === "cancelled" ? "Update cancelled" : "The review was not updated";
|
|
6020
6057
|
const facts = outcome.ok ? updateFacts(outcome) : null;
|
|
@@ -6027,6 +6064,72 @@ function renderUpdateOutcome(outcome) {
|
|
|
6027
6064
|
</div>`;
|
|
6028
6065
|
}
|
|
6029
6066
|
/**
|
|
6067
|
+
* What a Re-check of the PR discussion did, in the same notice an update reports through.
|
|
6068
|
+
*
|
|
6069
|
+
* Five outcomes, each with its own headline, because the one the reviewer most needs to tell apart
|
|
6070
|
+
* is the partial: the discussion came in, so the "already raised" lines are current, but the
|
|
6071
|
+
* findings were not judged against it. That one says why and, when trying again could help, offers it.
|
|
6072
|
+
*/
|
|
6073
|
+
function renderRecheckOutcome(outcome) {
|
|
6074
|
+
const retry = outcome.stop === "analysis-failed" && outcome.conversationOk === true;
|
|
6075
|
+
const { state, title, body } = recheckWords(outcome);
|
|
6076
|
+
const facts = outcome.ok && outcome.stop === null ? recheckFacts(outcome) : null;
|
|
6077
|
+
return `<div class="prb-update-result" data-state="${state}" data-update-result data-recheck-result role="status">
|
|
6078
|
+
<div class="prb-update-result-body">
|
|
6079
|
+
${facts ? `<p class="prb-update-facts"><strong>${escapeHtml$1(title)}</strong>${facts.map((fact) => `<span class="prb-update-fact">${escapeHtml$1(fact)}</span>`).join("")}</p>` : `<p><strong>${escapeHtml$1(title)}</strong> ${escapeHtml$1(body)}</p>`}
|
|
6080
|
+
${retry ? "<p class=\"prb-update-footnote\"><button type=\"button\" class=\"prb-fresh-quiet\" data-pr-recheck>Try again</button></p>" : ""}
|
|
6081
|
+
</div>
|
|
6082
|
+
<button type="button" class="prb-fresh-dismiss" data-update-close>Close</button>
|
|
6083
|
+
</div>`;
|
|
6084
|
+
}
|
|
6085
|
+
function recheckWords(outcome) {
|
|
6086
|
+
if (outcome.stop === "cancelled") return {
|
|
6087
|
+
state: "cancelled",
|
|
6088
|
+
title: "Re-check cancelled",
|
|
6089
|
+
body: "The findings and the discussion shown are exactly as they were."
|
|
6090
|
+
};
|
|
6091
|
+
if (outcome.conversationOk !== true) return {
|
|
6092
|
+
state: "refused",
|
|
6093
|
+
title: "The PR discussion was not re-checked",
|
|
6094
|
+
body: outcome.message
|
|
6095
|
+
};
|
|
6096
|
+
if (outcome.stop === "already-current") return {
|
|
6097
|
+
state: "done",
|
|
6098
|
+
title: "No findings changed",
|
|
6099
|
+
body: outcome.message
|
|
6100
|
+
};
|
|
6101
|
+
if (outcome.stop !== null) return {
|
|
6102
|
+
state: "stopped",
|
|
6103
|
+
title: "Discussion refreshed, findings not reassessed",
|
|
6104
|
+
body: `The latest comments are in, and the notes about who raised what are current. ${outcome.message}`
|
|
6105
|
+
};
|
|
6106
|
+
if (outcome.counts && !findingsChanged(outcome.counts)) return {
|
|
6107
|
+
state: "done",
|
|
6108
|
+
title: "No findings changed",
|
|
6109
|
+
body: "The new comments were read against every open issue and question, and each one stands as it was."
|
|
6110
|
+
};
|
|
6111
|
+
return {
|
|
6112
|
+
state: "done",
|
|
6113
|
+
title: "Findings reassessed against the PR discussion",
|
|
6114
|
+
body: outcome.message
|
|
6115
|
+
};
|
|
6116
|
+
}
|
|
6117
|
+
/** What a Re-check changed, largest news first, in the words an update uses. No commit: none moved. */
|
|
6118
|
+
function recheckFacts(outcome) {
|
|
6119
|
+
const counts = outcome.counts;
|
|
6120
|
+
if (!counts || !findingsChanged(counts)) return null;
|
|
6121
|
+
const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
|
|
6122
|
+
return [
|
|
6123
|
+
counts.questionsAnswered ? `${plural(counts.questionsAnswered, "question", "questions")} answered` : "",
|
|
6124
|
+
counts.resolved ? `${plural(counts.resolved, "issue", "issues")} resolved by the discussion` : "",
|
|
6125
|
+
counts.reopened ? `${plural(counts.reopened, "issue", "issues")} reopened` : "",
|
|
6126
|
+
counts.affected ? `${counts.affected} to look at again` : "",
|
|
6127
|
+
counts.newIssues ? plural(counts.newIssues, "new issue", "new issues") : "",
|
|
6128
|
+
counts.gradeChanges ? `${plural(counts.gradeChanges, "grade change", "grade changes")} suggested` : "",
|
|
6129
|
+
counts.decisionsChallenged ? `${plural(counts.decisionsChallenged, "accepted answer", "accepted answers")} put back to you` : ""
|
|
6130
|
+
].filter(Boolean);
|
|
6131
|
+
}
|
|
6132
|
+
/**
|
|
6030
6133
|
* A successful update as a row of facts: the commit it reached, then what changed, largest news first.
|
|
6031
6134
|
*
|
|
6032
6135
|
* Read at a glance rather than parsed. The sentence it replaces ("Updated to 7bc233f: 2 fixed,
|
|
@@ -6411,14 +6514,14 @@ function comparisonNote(code, hasPullRequest) {
|
|
|
6411
6514
|
* button is the subject of the sentence now, so there is one name for one thing and the reviewer
|
|
6412
6515
|
* presses the words they just read.
|
|
6413
6516
|
*
|
|
6414
|
-
*
|
|
6415
|
-
*
|
|
6517
|
+
* It reads no new code: the findings are reassessed against the discussion at the reviewed commit,
|
|
6518
|
+
* and newer commits stay the Update button's.
|
|
6416
6519
|
*/
|
|
6417
6520
|
function conversationNote(report, actions) {
|
|
6418
6521
|
if (!report.conversation.attached) return "";
|
|
6419
6522
|
const when = escapeHtml$1(relativeTime$1(report.conversation.importedAt));
|
|
6420
6523
|
if (!actions) return `The PR conversation was last checked ${when}.`;
|
|
6421
|
-
return `<button type="button" class="prb-fresh-quiet" data-pr-recheck>Re-check the conversation</button> to
|
|
6524
|
+
return `<button type="button" class="prb-fresh-quiet" data-pr-recheck>Re-check the conversation</button> to fetch new comments and reassess issues and questions against them, at the reviewed commit. Last checked ${when}.`;
|
|
6422
6525
|
}
|
|
6423
6526
|
//#endregion
|
|
6424
6527
|
//#region ../../packages/review-harness/src/workspace/progress_view.ts
|
|
@@ -6516,7 +6619,7 @@ function renderProgressView(job, fileCount, cancellable = false, history) {
|
|
|
6516
6619
|
* and can be carried on; an update has the previous review, which is why the two are worded apart.
|
|
6517
6620
|
*/
|
|
6518
6621
|
function renderElapsed(job, cancel, history) {
|
|
6519
|
-
const button = !cancel ? "" : cancel === "Cancel review" ? `<button type="button" class="prb-progress-cancel" data-review-cancel="${escapeHtml$1(job.id)}">Cancel review</button>` : `<button type="button" class="prb-progress-cancel" data-update-cancel="${escapeHtml$1(job.id)}"
|
|
6622
|
+
const button = !cancel ? "" : cancel === "Cancel review" ? `<button type="button" class="prb-progress-cancel" data-review-cancel="${escapeHtml$1(job.id)}">Cancel review</button>` : `<button type="button" class="prb-progress-cancel" data-update-cancel="${escapeHtml$1(job.id)}">${cancel}</button>`;
|
|
6520
6623
|
return `<div class="prb-progress-foot">
|
|
6521
6624
|
<div class="prb-progress-runtime">
|
|
6522
6625
|
<p class="prb-progress-elapsed" data-started-at="${job.startedAt}">${escapeHtml$1(runningFor(job.startedAt))}</p>${renderHistory$1(history)}
|
|
@@ -6564,11 +6667,14 @@ function renderHistory$1(history) {
|
|
|
6564
6667
|
*/
|
|
6565
6668
|
function renderUpdateProgress(job, cancellable = false, history) {
|
|
6566
6669
|
if (!isLiveJob(job)) return "";
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6670
|
+
const recheck = job.scope === "conversation";
|
|
6671
|
+
const title = recheck ? "Re-checking the PR discussion" : "Updating this review";
|
|
6672
|
+
const lead = recheck ? "Issues and questions will be updated from the discussion when reassessment finishes." : "The previous review will be replaced when reassessment finishes.";
|
|
6673
|
+
return `<div class="prb-update-progress" data-update-phase="${escapeHtml$1(job.phase)}"${recheck ? " data-update-scope=\"conversation\"" : ""} role="status" aria-live="polite">
|
|
6674
|
+
<h3 class="prb-doc-title"><span class="prb-spinner" aria-hidden="true"></span>${title}</h3>
|
|
6675
|
+
<p class="prb-progress-lead">${lead}</p>
|
|
6570
6676
|
${renderSteps(job)}
|
|
6571
|
-
${renderElapsed(job, cancellable ? "Cancel update" : null, history)}
|
|
6677
|
+
${renderElapsed(job, cancellable ? recheck ? "Cancel re-check" : "Cancel update" : null, history)}
|
|
6572
6678
|
</div>`;
|
|
6573
6679
|
}
|
|
6574
6680
|
//#endregion
|
|
@@ -6957,20 +7063,28 @@ function renderStandardViews(view, sections) {
|
|
|
6957
7063
|
* When the "already raised" claims below were last true.
|
|
6958
7064
|
*
|
|
6959
7065
|
* Every attribution on this view carries a thread's state — "still unresolved", "since marked
|
|
6960
|
-
* resolved" — and that state was frozen the moment the conversation was imported. Nothing
|
|
6961
|
-
* re-checks it
|
|
6962
|
-
*
|
|
7066
|
+
* resolved" — and that state was frozen the moment the conversation was imported. Nothing on this
|
|
7067
|
+
* view re-checks it; the PR context view does, on request. Until then a card can go on telling the
|
|
7068
|
+
* reviewer that Sergei's thread is open an hour after Sergei closed it.
|
|
6963
7069
|
*
|
|
6964
7070
|
* Every other kind of staleness in this product degrades into vagueness. This one degrades into a
|
|
6965
7071
|
* confident, specific, wrong claim about what a named colleague currently thinks, which is worth
|
|
6966
|
-
* one line above the list
|
|
7072
|
+
* one line above the list: its age always in view, the explanation one disclosure away, and the way
|
|
7073
|
+
* to the re-check beside it.
|
|
6967
7074
|
*
|
|
6968
7075
|
* Only where a conversation was attached: there are no such claims to qualify otherwise.
|
|
6969
7076
|
*/
|
|
6970
7077
|
function renderAttributionAge(view) {
|
|
6971
7078
|
if (!view.prContext) return "";
|
|
6972
7079
|
const imported = relativeTime$1(view.prContext.prContext.discussion.fetchedAt);
|
|
6973
|
-
return `<
|
|
7080
|
+
return `<div class="prb-discussion-age">
|
|
7081
|
+
<details class="prb-discussion-age-detail">
|
|
7082
|
+
<summary class="prb-discussion-age-summary">PR discussion checked ${escapeHtml$1(imported)}</summary>
|
|
7083
|
+
<p class="prb-discussion-age-body">The issues and questions below, their “already raised” notes and whether each thread was still open describe the pull request as it stood when its discussion was fetched. A reply or a resolved thread since then is not reflected here until the conversation is re-checked, which also reassesses the issues and questions against it.</p>
|
|
7084
|
+
</details>
|
|
7085
|
+
<span class="prb-discussion-age-sep" aria-hidden="true">·</span>
|
|
7086
|
+
<button type="button" class="prb-doc-note-link" data-view-nav="prcontext">Re-check in PR context</button>
|
|
7087
|
+
</div>`;
|
|
6974
7088
|
}
|
|
6975
7089
|
/**
|
|
6976
7090
|
* The link out to this file on the pull request.
|
|
@@ -12690,11 +12804,28 @@ feature: E</pre>
|
|
|
12690
12804
|
confirm</b> where the behaviour is right only if it was meant, and <b>Maintenance</b> for what it
|
|
12691
12805
|
costs later. Within each group the most severe come first. Severity and confidence are shown on
|
|
12692
12806
|
each card, separately from its group.</p>
|
|
12693
|
-
<p>A card leads with what the issue means and when it happens, then the step
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12807
|
+
<p>A card leads with what the issue means and when it happens, then the step it calls for, when
|
|
12808
|
+
there is one: <b>To verify</b> settles whether it happens, <b>To decide</b> asks whether it was
|
|
12809
|
+
meant, and <b>To fix</b> names the change that would correct it. The technical account, where it
|
|
12810
|
+
is in the code and its history are under <b>Technical details</b> on the card. Folding a group is
|
|
12811
|
+
remembered for the next review.</p>
|
|
12812
|
+
<p>Issues from older reviews have no summary yet and are under <b>Other issues</b>. The next
|
|
12813
|
+
Update that finds new commits or new discussion writes one for each issue still outstanding,
|
|
12814
|
+
from the same evidence; it does not re-check the issue. An Update that finds nothing new changes
|
|
12815
|
+
nothing.</p>
|
|
12816
|
+
<p>Questions read the same way: each leads with the question in plain terms, why the answer
|
|
12817
|
+
matters and what is needed, with the original question and its files under <b>Technical
|
|
12818
|
+
context</b>. Any answer so far stays on the card.</p>
|
|
12819
|
+
<p>When the review came with a pull request discussion, the line above the issues says when it
|
|
12820
|
+
was last checked. The issues, the questions and their “already raised” notes describe the
|
|
12821
|
+
discussion as it was then; <b>Re-check in PR context</b> takes you to bring it up to date.</p>
|
|
12822
|
+
<p>Re-checking fetches the latest comments and reassesses every issue and question against
|
|
12823
|
+
anything new in them. A reply can answer a question, resolve or reopen an issue, or suggest a
|
|
12824
|
+
different severity or importance, and a concern raised in a comment becomes a new issue once it
|
|
12825
|
+
is confirmed in the reviewed code. A thread marked resolved, or a comment saying something is
|
|
12826
|
+
fixed, does not resolve an issue on its own: it stays open until an Update sees the fix in the
|
|
12827
|
+
code. Re-checking reads no new commits; Update review does. When the comments only change how
|
|
12828
|
+
much one finding matters, <b>Reassess</b> on its card asks about that one alone.</p>
|
|
12698
12829
|
<p>Resolved and dismissed issues are in the closed list at the bottom. One the new commits fixed
|
|
12699
12830
|
leads with what the code now does, when the update could see it.</p>`
|
|
12700
12831
|
},
|
|
@@ -12707,7 +12838,7 @@ feature: E</pre>
|
|
|
12707
12838
|
<li><b>Update</b> brings the review up to date with commits added since it was made. Its
|
|
12708
12839
|
findings are checked against the new commits rather than thrown away, and each says whether
|
|
12709
12840
|
the new commits affected it. In the workspace, Update review appears on a review whose
|
|
12710
|
-
branch has moved on.</li>
|
|
12841
|
+
branch has moved on. It also reads the pull request discussion, as Re-check does.</li>
|
|
12711
12842
|
<li><b>Reanalyse</b> analyses the same commit again, for a fresh judgement of the same change,
|
|
12712
12843
|
for example with a different model.</li>
|
|
12713
12844
|
<li><b>Fresh review</b> reviews the target again as a new review, and keeps the existing one
|
|
@@ -18283,10 +18414,15 @@ async function sendWorkspacePage(res, workspace) {
|
|
|
18283
18414
|
const updateJob = liveUpdateFor(workspace.id);
|
|
18284
18415
|
const reviewJob = workspace.jobId ? loadJob(workspace.jobId) : void 0;
|
|
18285
18416
|
const kissJob = kissRun ? loadJob(kissRun.jobId) : void 0;
|
|
18286
|
-
const
|
|
18287
|
-
|
|
18288
|
-
|
|
18289
|
-
|
|
18417
|
+
const unseen = [workspace.lastUpdate, workspace.lastRecheck].filter((outcome) => outcome !== void 0 && !outcome.acknowledged);
|
|
18418
|
+
const notice = unseen.sort((a, b) => b.at - a.at)[0];
|
|
18419
|
+
if (notice) {
|
|
18420
|
+
const read = new Set(unseen.map((outcome) => outcome.at));
|
|
18421
|
+
mutateWorkspace(workspace.id, (current) => {
|
|
18422
|
+
if (current.lastUpdate && read.has(current.lastUpdate.at)) current.lastUpdate.acknowledged = true;
|
|
18423
|
+
if (current.lastRecheck && read.has(current.lastRecheck.at)) current.lastRecheck.acknowledged = true;
|
|
18424
|
+
});
|
|
18425
|
+
}
|
|
18290
18426
|
const everyReview = recentWorkspaces(Number.MAX_SAFE_INTEGER);
|
|
18291
18427
|
const html = renderWorkspace({
|
|
18292
18428
|
session: workspace.session,
|
|
@@ -18515,33 +18651,78 @@ async function kissFreshnessOrNull(workspace) {
|
|
|
18515
18651
|
}
|
|
18516
18652
|
}
|
|
18517
18653
|
/**
|
|
18518
|
-
* Re-
|
|
18654
|
+
* Re-check the pull request discussion, at the reviewer's request: re-read it, then reconcile the
|
|
18655
|
+
* findings with whatever in it is new to them, at the commit the review read.
|
|
18656
|
+
*
|
|
18657
|
+
* Answers 200 with `ok: false` rather than an error status when it does not work, for the reason the
|
|
18658
|
+
* update route does: a failure is an outcome the page has to explain in a sentence. 409 when an update
|
|
18659
|
+
* or another Re-check holds the review, which the reloaded page then shows running.
|
|
18519
18660
|
*
|
|
18520
|
-
*
|
|
18521
|
-
*
|
|
18522
|
-
* the conversation
|
|
18523
|
-
*
|
|
18661
|
+
* Saved in two ways, because two different things can come of it. A reassessment that was applied
|
|
18662
|
+
* goes on the way an update does, onto the review as it is on disk. Anything short of that changed
|
|
18663
|
+
* only the conversation and the "already raised" lines, and goes on the way a re-read always has,
|
|
18664
|
+
* through `saveRefresh`, which will not put an older conversation over a newer one.
|
|
18524
18665
|
*/
|
|
18525
18666
|
async function handleRefreshPr(res, workspace) {
|
|
18526
18667
|
const reattributing = JSON.stringify(workspace.session.result);
|
|
18527
|
-
|
|
18528
|
-
const startedAt = Date.now();
|
|
18668
|
+
const begun = Date.now();
|
|
18529
18669
|
let outcome;
|
|
18530
18670
|
try {
|
|
18531
|
-
outcome = await
|
|
18532
|
-
} catch {
|
|
18533
|
-
|
|
18671
|
+
outcome = await track(recheckConversation(workspace, { source: "workspace" }));
|
|
18672
|
+
} catch (error) {
|
|
18673
|
+
if (error instanceof UpdateAlreadyRunningError) return send(res, 409, { error: error.message });
|
|
18674
|
+
outcome = {
|
|
18675
|
+
at: Date.now(),
|
|
18676
|
+
ok: false,
|
|
18677
|
+
message: error instanceof RefreshUnavailableError || error instanceof ReviewBeingDeletedError ? error.message : "The findings could not be reassessed. Nothing about them has changed.",
|
|
18678
|
+
stop: "analysis-failed",
|
|
18679
|
+
fetchFailed: false,
|
|
18680
|
+
moved: false,
|
|
18681
|
+
...rereadSince(workspace, begun) ? { conversationOk: workspace.lastPrRefresh.ok } : {},
|
|
18682
|
+
fromSha: null,
|
|
18683
|
+
toSha: null,
|
|
18684
|
+
scope: "conversation"
|
|
18685
|
+
};
|
|
18534
18686
|
}
|
|
18535
|
-
|
|
18687
|
+
const applied = outcome.ok && outcome.stop === null;
|
|
18688
|
+
const cancelled = outcome.stop === "cancelled";
|
|
18689
|
+
workspace.lastRecheck = outcome;
|
|
18690
|
+
const reread = !applied && !cancelled ? rereadSince(workspace, begun) : void 0;
|
|
18691
|
+
if (reread) saveRefresh(workspace, reread, reattributing);
|
|
18692
|
+
const saved = mutateWorkspace(workspace.id, (fresh) => applied ? {
|
|
18693
|
+
...mergeUpdateInto(fresh, workspace),
|
|
18694
|
+
lastRecheck: outcome
|
|
18695
|
+
} : {
|
|
18696
|
+
...fresh,
|
|
18697
|
+
lastRecheck: outcome
|
|
18698
|
+
});
|
|
18699
|
+
const refresh = reread ?? (applied ? workspace.lastPrRefresh : void 0);
|
|
18536
18700
|
record({
|
|
18537
18701
|
event: "pr_conversation_rechecked",
|
|
18538
18702
|
reviewId: workspace.id,
|
|
18539
|
-
ok: outcome.
|
|
18540
|
-
changed:
|
|
18541
|
-
attributionsRestated:
|
|
18542
|
-
attributionsWithdrawn:
|
|
18703
|
+
ok: outcome.conversationOk === true,
|
|
18704
|
+
changed: refresh?.changes ? !isUnchanged(refresh.changes) : false,
|
|
18705
|
+
attributionsRestated: refresh?.attributionsRestated ?? 0,
|
|
18706
|
+
attributionsWithdrawn: refresh?.attributionsWithdrawn ?? 0,
|
|
18707
|
+
reassessment: reassessmentOf(outcome),
|
|
18708
|
+
resolved: outcome.counts?.resolved ?? 0,
|
|
18709
|
+
questionsAnswered: outcome.counts?.questionsAnswered ?? 0,
|
|
18710
|
+
newIssues: outcome.counts?.newIssues ?? 0
|
|
18543
18711
|
});
|
|
18544
|
-
send(res, 200, outcome);
|
|
18712
|
+
send(res, 200, saved?.lastRecheck ?? outcome);
|
|
18713
|
+
}
|
|
18714
|
+
/** The re-read this request made, if it made one: the copy's record, begun no earlier than the request. */
|
|
18715
|
+
function rereadSince(workspace, begun) {
|
|
18716
|
+
const refresh = workspace.lastPrRefresh;
|
|
18717
|
+
return refresh && (refresh.startedAt ?? refresh.at) >= begun ? refresh : void 0;
|
|
18718
|
+
}
|
|
18719
|
+
/** What a Re-check did to the findings, as the closed set the log records. */
|
|
18720
|
+
function reassessmentOf(outcome) {
|
|
18721
|
+
if (outcome.stop === "cancelled") return "cancelled";
|
|
18722
|
+
if (outcome.stop === "already-current") return "not-needed";
|
|
18723
|
+
if (outcome.stop === "analysis-failed") return "failed";
|
|
18724
|
+
if (outcome.stop !== null) return "skipped";
|
|
18725
|
+
return outcome.counts && findingsChanged(outcome.counts) ? "changed" : "unchanged";
|
|
18545
18726
|
}
|
|
18546
18727
|
/**
|
|
18547
18728
|
* Bring the whole review up to date with the checkout, at the reviewer's request.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prreviewbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.93.0",
|
|
4
4
|
"description": "Understand code changes before you approve them. Local branch and pull request reviews with Claude Code or Codex.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://prreviewbuddy.com",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bt as e}from"./chunk-DRtin0n-.js";var t=`button:not([disabled])`;function n(e){return e.querySelector(`[data-menu-panel]`)}function r(e){return e.querySelector(`[data-menu-trigger]`)}function i(e){let t=n(e);return t?[...t.querySelectorAll(`input:not([disabled]), textarea:not([disabled]), button:not([disabled])`)].filter(e=>!e.closest(`[hidden]`)):[]}function a(e){let r=n(e);return r?[...r.querySelectorAll(t)].filter(e=>!e.closest(`[hidden]`)):[]}var o=`data-menu-placement`,s=`data-menu-align`;function c(e){let t=e.ownerDocument.defaultView,n=0,r=t?.innerHeight??1/0,i=0,a=t?.innerWidth??1/0,o=e=>e!==`visible`&&e!==``;for(let s=e.parentElement;s&&t;s=s.parentElement){let e=t.getComputedStyle(s);if(!o(e.overflowY)&&!o(e.overflowX))continue;let c=s.getBoundingClientRect();o(e.overflowY)&&(n=Math.max(n,c.top),r=Math.min(r,c.bottom)),o(e.overflowX)&&(i=Math.max(i,c.left),a=Math.min(a,c.right))}return{top:n,bottom:r,left:i,right:a}}var l=8,u=120;function d(e){let t=n(e),i=r(e);if(!t||!i)return;t.removeAttribute(o),t.removeAttribute(s),t.style.removeProperty(`max-height`),t.style.removeProperty(`overflow-y`),t.style.removeProperty(`box-sizing`),t.scrollTop=0;let a=t.getBoundingClientRect(),d=i.getBoundingClientRect(),f=c(e);a.right>f.right-l&&d.right-a.width>=f.left+l&&t.setAttribute(s,`end`);let p=Math.max(0,a.top-d.bottom),m=f.bottom-d.bottom-p-l,h=d.top-f.top-p-l,g=a.height,_=g<=m?`below`:g<=h||h>m?`above`:`below`;_===`above`&&t.setAttribute(o,`above`);let v=_===`above`?h:m;g<=v||(t.style.boxSizing=`border-box`,t.style.maxHeight=`${Math.max(Math.floor(v),u)}px`,t.style.overflowY=`auto`)}function f(e){return!e.metaKey&&!e.ctrlKey&&!e.altKey&&!e.shiftKey?e.key===`j`?`ArrowDown`:e.key===`k`?`ArrowUp`:e.key:e.key}function p(t,o={}){let s=null,c=null,l=o.within?`${o.within} [data-menu]`:`[data-menu]`,u=e=>e&&(!o.within||e.closest(o.within))?e:null,p=(e,t={})=>{let i=n(e);if(!i||i.hidden)return;i.hidden=!0;let a=r(e);a?.setAttribute(`aria-expanded`,`false`);let l=s===e?c:null;s===e&&(s=null,c=null),o.onClose?.(e),t.returnFocus&&(l??(()=>a?.focus()))()},m=()=>{for(let e of t.querySelectorAll(l))p(e)},h=(e,t,i=null)=>{let l=n(e);l&&(m(),c=i,o.onOpen?.(e),l.hidden=!1,d(e),r(e)?.setAttribute(`aria-expanded`,`true`),s=e,t&&a(e)[0]?.focus())};return t.addEventListener(`click`,e=>{let t=e.target,r=u(t?.closest?.(`[data-menu]`)??null);if(!r){m();return}if(t?.closest(`[data-menu-trigger]`)){let t=e.detail===0;n(r)?.hidden?h(r,t):p(r);return}s&&s!==r&&p(s)}),t.addEventListener(`keydown`,t=>{if(s&&!s.isConnected&&(s=null),!s)return;let n=s;if(t.key===`Escape`){t.preventDefault(),p(n,{returnFocus:!0});return}if(!n.contains(t.target))return;if(t.key===`Tab`){if(n.hasAttribute(`data-menu-form`)){let e=i(n),r=e.indexOf(t.target);if(e.length>0){t.preventDefault(),e[(r+(t.shiftKey?-1:1)+e.length)%e.length].focus();return}}p(n);return}if(e(t.target))return;let r=a(n);if(r.length===0)return;let o=r.indexOf(t.target),c=f(t),l=c===`ArrowDown`?r[(o+1)%r.length]:c===`ArrowUp`?r[(o-1+r.length)%r.length]:c===`Home`?r[0]:c===`End`?r[r.length-1]:null;l&&(t.preventDefault(),l.focus(),l.scrollIntoView({block:`nearest`}))}),{open:(e,t={})=>h(e,t.focusFirst===!0,t.returnFocus??null),close:p,closeAll:m}}function m(e){return e<3e4?1e3:e<12e4?3e3:e<6e5?8e3:15e3}export{p as
|
|
1
|
+
import{bt as e}from"./chunk-DRtin0n-.js";var t=`button:not([disabled])`;function n(e){return e.querySelector(`[data-menu-panel]`)}function r(e){return e.querySelector(`[data-menu-trigger]`)}function i(e){let t=n(e);return t?[...t.querySelectorAll(`input:not([disabled]), textarea:not([disabled]), button:not([disabled])`)].filter(e=>!e.closest(`[hidden]`)):[]}function a(e){let r=n(e);return r?[...r.querySelectorAll(t)].filter(e=>!e.closest(`[hidden]`)):[]}var o=`data-menu-placement`,s=`data-menu-align`;function c(e){let t=e.ownerDocument.defaultView,n=0,r=t?.innerHeight??1/0,i=0,a=t?.innerWidth??1/0,o=e=>e!==`visible`&&e!==``;for(let s=e.parentElement;s&&t;s=s.parentElement){let e=t.getComputedStyle(s);if(!o(e.overflowY)&&!o(e.overflowX))continue;let c=s.getBoundingClientRect();o(e.overflowY)&&(n=Math.max(n,c.top),r=Math.min(r,c.bottom)),o(e.overflowX)&&(i=Math.max(i,c.left),a=Math.min(a,c.right))}return{top:n,bottom:r,left:i,right:a}}var l=8,u=120;function d(e){let t=n(e),i=r(e);if(!t||!i)return;t.removeAttribute(o),t.removeAttribute(s),t.style.removeProperty(`max-height`),t.style.removeProperty(`overflow-y`),t.style.removeProperty(`box-sizing`),t.scrollTop=0;let a=t.getBoundingClientRect(),d=i.getBoundingClientRect(),f=c(e);a.right>f.right-l&&d.right-a.width>=f.left+l&&t.setAttribute(s,`end`);let p=Math.max(0,a.top-d.bottom),m=f.bottom-d.bottom-p-l,h=d.top-f.top-p-l,g=a.height,_=g<=m?`below`:g<=h||h>m?`above`:`below`;_===`above`&&t.setAttribute(o,`above`);let v=_===`above`?h:m;g<=v||(t.style.boxSizing=`border-box`,t.style.maxHeight=`${Math.max(Math.floor(v),u)}px`,t.style.overflowY=`auto`)}function f(e){return!e.metaKey&&!e.ctrlKey&&!e.altKey&&!e.shiftKey?e.key===`j`?`ArrowDown`:e.key===`k`?`ArrowUp`:e.key:e.key}function p(t,o={}){let s=null,c=null,l=o.within?`${o.within} [data-menu]`:`[data-menu]`,u=e=>e&&(!o.within||e.closest(o.within))?e:null,p=(e,t={})=>{let i=n(e);if(!i||i.hidden)return;i.hidden=!0;let a=r(e);a?.setAttribute(`aria-expanded`,`false`);let l=s===e?c:null;s===e&&(s=null,c=null),o.onClose?.(e),t.returnFocus&&(l??(()=>a?.focus()))()},m=()=>{for(let e of t.querySelectorAll(l))p(e)},h=(e,t,i=null)=>{let l=n(e);l&&(m(),c=i,o.onOpen?.(e),l.hidden=!1,d(e),r(e)?.setAttribute(`aria-expanded`,`true`),s=e,t&&a(e)[0]?.focus())};return t.addEventListener(`click`,e=>{let t=e.target,r=u(t?.closest?.(`[data-menu]`)??null);if(!r){m();return}if(t?.closest(`[data-menu-trigger]`)){let t=e.detail===0;n(r)?.hidden?h(r,t):p(r);return}s&&s!==r&&p(s)}),t.addEventListener(`keydown`,t=>{if(s&&!s.isConnected&&(s=null),!s)return;let n=s;if(t.key===`Escape`){t.preventDefault(),p(n,{returnFocus:!0});return}if(!n.contains(t.target))return;if(t.key===`Tab`){if(n.hasAttribute(`data-menu-form`)){let e=i(n),r=e.indexOf(t.target);if(e.length>0){t.preventDefault(),e[(r+(t.shiftKey?-1:1)+e.length)%e.length].focus();return}}p(n);return}if(e(t.target))return;let r=a(n);if(r.length===0)return;let o=r.indexOf(t.target),c=f(t),l=c===`ArrowDown`?r[(o+1)%r.length]:c===`ArrowUp`?r[(o-1+r.length)%r.length]:c===`Home`?r[0]:c===`End`?r[r.length-1]:null;l&&(t.preventDefault(),l.focus(),l.scrollIntoView({block:`nearest`}))}),{open:(e,t={})=>h(e,t.focusFirst===!0,t.returnFocus??null),close:p,closeAll:m}}function m(e){return e===`done`||e===`failed`}function h(e,t){let n=e=>e.toLowerCase().replace(/\bthe\b/g,``).replace(/[^a-z]/g,``);return n(e)===n(t)}function g(e){return e<3e4?1e3:e<12e4?3e3:e<6e5?8e3:15e3}export{p as i,m as n,h as r,g as t};
|