pi-ui-extend 0.1.9 → 0.1.11
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/README.md +23 -2
- package/dist/app/app.d.ts +4 -0
- package/dist/app/app.js +74 -7
- package/dist/app/cli/install.d.ts +2 -0
- package/dist/app/cli/install.js +16 -1
- package/dist/app/commands/command-controller.js +4 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-model-actions.d.ts +5 -0
- package/dist/app/commands/command-model-actions.js +104 -0
- package/dist/app/commands/command-navigation-actions.d.ts +6 -1
- package/dist/app/commands/command-navigation-actions.js +37 -14
- package/dist/app/commands/command-registry.d.ts +4 -0
- package/dist/app/commands/command-registry.js +32 -0
- package/dist/app/commands/command-session-actions.d.ts +1 -0
- package/dist/app/commands/command-session-actions.js +15 -5
- package/dist/app/commands/shell-controller.d.ts +1 -0
- package/dist/app/commands/shell-controller.js +1 -1
- package/dist/app/constants.d.ts +1 -1
- package/dist/app/constants.js +1 -1
- package/dist/app/icons.js +1 -1
- package/dist/app/input/autocomplete-controller.d.ts +52 -0
- package/dist/app/input/autocomplete-controller.js +352 -0
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +21 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +2 -0
- package/dist/app/input/input-paste-handler.d.ts +1 -0
- package/dist/app/input/input-paste-handler.js +22 -18
- package/dist/app/input/voice-controller.d.ts +2 -0
- package/dist/app/input/voice-controller.js +27 -15
- package/dist/app/model/model-usage-status.d.ts +9 -0
- package/dist/app/model/model-usage-status.js +124 -34
- package/dist/app/popup/popup-action-controller.js +1 -1
- package/dist/app/process.d.ts +17 -0
- package/dist/app/process.js +68 -0
- package/dist/app/rendering/conversation-entry-renderer.js +17 -6
- package/dist/app/rendering/conversation-tool-renderer.js +3 -2
- package/dist/app/rendering/editor-layout-renderer.d.ts +1 -0
- package/dist/app/rendering/editor-layout-renderer.js +11 -1
- package/dist/app/rendering/message-content.js +65 -7
- package/dist/app/rendering/render-controller.js +6 -1
- package/dist/app/rendering/render-text.d.ts +3 -0
- package/dist/app/rendering/render-text.js +51 -3
- package/dist/app/rendering/status-line-renderer.d.ts +5 -1
- package/dist/app/rendering/status-line-renderer.js +69 -25
- package/dist/app/rendering/tool-block-renderer.js +13 -31
- package/dist/app/runtime.d.ts +6 -1
- package/dist/app/runtime.js +35 -2
- package/dist/app/screen/clipboard.d.ts +2 -2
- package/dist/app/screen/clipboard.js +13 -18
- package/dist/app/screen/mouse-controller.d.ts +5 -2
- package/dist/app/screen/mouse-controller.js +16 -1
- package/dist/app/screen/screen-styler.d.ts +4 -1
- package/dist/app/screen/screen-styler.js +3 -2
- package/dist/app/screen/status-controller.d.ts +3 -0
- package/dist/app/screen/status-controller.js +23 -8
- package/dist/app/session/queued-message-controller.d.ts +7 -1
- package/dist/app/session/queued-message-controller.js +32 -21
- package/dist/app/session/resume-session-loader.d.ts +15 -0
- package/dist/app/session/resume-session-loader.js +204 -0
- package/dist/app/session/session-event-controller.d.ts +5 -1
- package/dist/app/session/session-event-controller.js +72 -5
- package/dist/app/session/session-history.js +4 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +5 -0
- package/dist/app/session/session-lifecycle-controller.js +9 -1
- package/dist/app/session/tabs-controller.d.ts +10 -1
- package/dist/app/session/tabs-controller.js +101 -5
- package/dist/app/terminal/nerd-font-controller.js +16 -17
- package/dist/app/terminal/terminal-controller.d.ts +1 -0
- package/dist/app/terminal/terminal-controller.js +1 -0
- package/dist/app/types.d.ts +14 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -1
- package/dist/app/workspace/workspace-actions-controller.js +3 -3
- package/dist/app/workspace/workspace-undo.d.ts +1 -1
- package/dist/app/workspace/workspace-undo.js +22 -20
- package/dist/config.d.ts +27 -0
- package/dist/config.js +174 -1
- package/dist/default-pix-config.js +38 -353
- package/dist/input-editor.d.ts +7 -1
- package/dist/input-editor.js +47 -6
- package/dist/markdown-format.d.ts +1 -0
- package/dist/markdown-format.js +26 -1
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +45 -195
- package/external/pi-tools-suite/src/lib/lsp.ts +2 -1
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +8 -7
- package/external/pi-tools-suite/src/lsp/manager.ts +4 -4
- package/external/pi-tools-suite/src/repo-discovery/index.ts +49 -2
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +9 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +1 -1
- package/package.json +1 -1
|
@@ -9,13 +9,10 @@ const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
|
9
9
|
const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
10
10
|
const ZHIPU_QUOTA_URL = "https://bigmodel.cn/api/monitor/usage/quota/limit";
|
|
11
11
|
const GOOGLE_QUOTA_API_URL = "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels";
|
|
12
|
-
const GOOGLE_TOKEN_REFRESH_URL = "https://oauth2.googleapis.com/token";
|
|
13
12
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
14
13
|
const DAY_SECONDS = 86_400;
|
|
15
14
|
const HOUR_SECONDS = 3_600;
|
|
16
15
|
const DEFAULT_ANTIGRAVITY_PROJECT_ID = "rising-fact-p41fc";
|
|
17
|
-
const GOOGLE_CLIENT_ID = process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID ?? process.env.ANTIGRAVITY_GOOGLE_CLIENT_ID ?? "";
|
|
18
|
-
const GOOGLE_CLIENT_SECRET = process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET ?? process.env.ANTIGRAVITY_GOOGLE_CLIENT_SECRET ?? "";
|
|
19
16
|
const OPENAI_QUOTA_PROVIDERS = new Set(["openai", "openai-codex"]);
|
|
20
17
|
const ZHIPU_QUOTA_PROVIDERS = new Set(["zai", "zhipuai-coding-plan"]);
|
|
21
18
|
const ANTIGRAVITY_QUOTA_PROVIDERS = new Set(["antigravity", "google-antigravity"]);
|
|
@@ -401,8 +398,13 @@ export function googleAntigravityUsageStatusFromResponse(data, descriptor, now =
|
|
|
401
398
|
};
|
|
402
399
|
}
|
|
403
400
|
async function queryGoogleAntigravityModelUsage(descriptor) {
|
|
404
|
-
const
|
|
405
|
-
const
|
|
401
|
+
const now = Date.now();
|
|
402
|
+
const cachedResponse = googleQuotaResponseFromCachedQuota(descriptor.account.cachedQuota, descriptor.account.cachedQuotaUpdatedAt, now);
|
|
403
|
+
if (cachedResponse)
|
|
404
|
+
return googleAntigravityUsageStatusFromResponse(cachedResponse, descriptor, now);
|
|
405
|
+
if (!descriptor.account.accessToken)
|
|
406
|
+
return undefined;
|
|
407
|
+
const response = await fetchGoogleAntigravityQuota(descriptor.account.accessToken, descriptor.account.projectId);
|
|
406
408
|
return googleAntigravityUsageStatusFromResponse(response, descriptor);
|
|
407
409
|
}
|
|
408
410
|
const GOOGLE_ACCOUNT_QUOTA_WINDOWS = [
|
|
@@ -417,10 +419,12 @@ async function queryGoogleAntigravityAccountUsage(now) {
|
|
|
417
419
|
const results = await Promise.all(accounts.map(async (account) => {
|
|
418
420
|
const accountLabel = account.email ?? maskCredential(account.refreshToken);
|
|
419
421
|
try {
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
422
|
+
const response = account.cachedQuota ? googleQuotaResponseFromCachedQuota(account.cachedQuota, account.cachedQuotaUpdatedAt, now) : undefined;
|
|
423
|
+
const windows = response ? googleAccountWindowsFromResponse(response, now) : [];
|
|
424
|
+
if (windows.length === 0 && account.accessToken) {
|
|
425
|
+
const liveResponse = await fetchGoogleAntigravityQuota(account.accessToken, account.projectId);
|
|
426
|
+
windows.push(...googleAccountWindowsFromResponse(liveResponse, now));
|
|
427
|
+
}
|
|
424
428
|
return {
|
|
425
429
|
account: accountLabel,
|
|
426
430
|
windows,
|
|
@@ -449,15 +453,20 @@ function readAllAntigravityQuotaAccounts() {
|
|
|
449
453
|
return [];
|
|
450
454
|
const accounts = storedAntigravityAccounts(credential);
|
|
451
455
|
if (accounts.length > 0) {
|
|
456
|
+
const activeIndex = clampAccountIndex(credential.activeIndex, accounts.length);
|
|
457
|
+
const activeAccess = antigravityAccessFromCredential(credential);
|
|
452
458
|
return accounts.map((account, accountIndex) => antigravityQuotaAccount(account, {
|
|
453
459
|
...(credential.email ? { fallbackEmail: credential.email } : {}),
|
|
460
|
+
...(accountIndex === activeIndex && activeAccess ? { accessToken: activeAccess.accessToken } : {}),
|
|
454
461
|
accountIndex,
|
|
455
462
|
accountCount: accounts.length,
|
|
456
463
|
})).filter((account) => account !== undefined);
|
|
457
464
|
}
|
|
458
465
|
const fallbackAccount = antigravityAccountFromCredential(credential);
|
|
466
|
+
const fallbackAccess = antigravityAccessFromCredential(credential);
|
|
459
467
|
const account = fallbackAccount ? antigravityQuotaAccount(fallbackAccount, {
|
|
460
468
|
...(credential.email ? { fallbackEmail: credential.email } : {}),
|
|
469
|
+
...(fallbackAccess ? { accessToken: fallbackAccess.accessToken } : {}),
|
|
461
470
|
}) : undefined;
|
|
462
471
|
return account ? [account] : [];
|
|
463
472
|
}
|
|
@@ -480,12 +489,15 @@ function antigravityAccountFromCredential(credential) {
|
|
|
480
489
|
const refresh = splitAntigravityRefresh(credential.refresh);
|
|
481
490
|
if (!refresh.refreshToken)
|
|
482
491
|
return undefined;
|
|
492
|
+
const activeStoredAccount = credential.accounts?.[clampAccountIndex(credential.activeIndex, credential.accounts.length)];
|
|
483
493
|
return {
|
|
484
494
|
refreshToken: refresh.refreshToken,
|
|
485
495
|
projectId: refresh.projectId || refresh.managedProjectId || DEFAULT_ANTIGRAVITY_PROJECT_ID,
|
|
486
496
|
enabled: true,
|
|
487
497
|
...(credential.email ? { email: credential.email } : {}),
|
|
488
498
|
...(refresh.managedProjectId ? { managedProjectId: refresh.managedProjectId } : {}),
|
|
499
|
+
...(activeStoredAccount?.cachedQuota ? { cachedQuota: activeStoredAccount.cachedQuota } : {}),
|
|
500
|
+
...(typeof activeStoredAccount?.cachedQuotaUpdatedAt === "number" ? { cachedQuotaUpdatedAt: activeStoredAccount.cachedQuotaUpdatedAt } : {}),
|
|
489
501
|
};
|
|
490
502
|
}
|
|
491
503
|
function antigravityQuotaAccount(account, options = {}) {
|
|
@@ -498,11 +510,65 @@ function antigravityQuotaAccount(account, options = {}) {
|
|
|
498
510
|
refreshToken,
|
|
499
511
|
projectId,
|
|
500
512
|
cacheKey: email ? email.toLowerCase() : shortHash(refreshToken),
|
|
513
|
+
...(options.accessToken ? { accessToken: options.accessToken } : {}),
|
|
514
|
+
...(account.cachedQuota ? { cachedQuota: account.cachedQuota } : {}),
|
|
515
|
+
...(typeof account.cachedQuotaUpdatedAt === "number" ? { cachedQuotaUpdatedAt: account.cachedQuotaUpdatedAt } : {}),
|
|
501
516
|
...(email ? { email } : {}),
|
|
502
517
|
...(typeof options.accountIndex === "number" ? { accountIndex: options.accountIndex } : {}),
|
|
503
518
|
...(typeof options.accountCount === "number" ? { accountCount: options.accountCount } : {}),
|
|
504
519
|
};
|
|
505
520
|
}
|
|
521
|
+
function googleQuotaResponseFromCachedQuota(cachedQuota, cachedQuotaUpdatedAt, now = Date.now()) {
|
|
522
|
+
if (!cachedQuota)
|
|
523
|
+
return undefined;
|
|
524
|
+
const models = {};
|
|
525
|
+
addCachedQuotaModels(models, cachedQuota.claude, ["claude-opus-4-6-thinking", "claude-sonnet-4-6"], cachedQuotaUpdatedAt, now);
|
|
526
|
+
addCachedQuotaModels(models, cachedQuota["gemini-flash"], ["gemini-2.5-flash", "gemini-3-flash"], cachedQuotaUpdatedAt, now);
|
|
527
|
+
addCachedQuotaModels(models, cachedQuota["gemini-pro"], ["gemini-3.1-pro-low"], cachedQuotaUpdatedAt, now);
|
|
528
|
+
return Object.keys(models).length > 0 ? { models } : undefined;
|
|
529
|
+
}
|
|
530
|
+
function addCachedQuotaModels(models, quota, quotaModelKeys, cachedQuotaUpdatedAt, now) {
|
|
531
|
+
if (!quota || !Number.isFinite(quota.remainingFraction))
|
|
532
|
+
return;
|
|
533
|
+
const remainingFraction = quota.remainingFraction;
|
|
534
|
+
const resetTime = cachedQuotaResetTimeForDisplay(quota.resetTime, cachedQuotaUpdatedAt, now);
|
|
535
|
+
for (const quotaModelKey of quotaModelKeys) {
|
|
536
|
+
models[quotaModelKey] = {
|
|
537
|
+
quotaInfo: {
|
|
538
|
+
remainingFraction,
|
|
539
|
+
...(resetTime ? { resetTime } : {}),
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function cachedQuotaResetTimeForDisplay(resetTime, cachedQuotaUpdatedAt, now) {
|
|
545
|
+
if (!resetTime)
|
|
546
|
+
return undefined;
|
|
547
|
+
const resetAt = Date.parse(resetTime);
|
|
548
|
+
if (!Number.isFinite(resetAt) || resetAt > now)
|
|
549
|
+
return resetTime;
|
|
550
|
+
const cachedAt = normalizeTimestampMillis(cachedQuotaUpdatedAt);
|
|
551
|
+
if (!Number.isFinite(cachedAt) || resetAt <= cachedAt)
|
|
552
|
+
return resetTime;
|
|
553
|
+
return new Date(now + (resetAt - cachedAt)).toISOString();
|
|
554
|
+
}
|
|
555
|
+
function normalizeTimestampMillis(value) {
|
|
556
|
+
if (!Number.isFinite(value))
|
|
557
|
+
return Number.NaN;
|
|
558
|
+
const timestamp = value;
|
|
559
|
+
return timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp;
|
|
560
|
+
}
|
|
561
|
+
function antigravityAccessFromCredential(credential) {
|
|
562
|
+
if (credential.type !== "oauth" || !credential.access || isExpired(credential))
|
|
563
|
+
return undefined;
|
|
564
|
+
const [accessToken = "", projectId = ""] = credential.access.split("|");
|
|
565
|
+
if (!accessToken)
|
|
566
|
+
return undefined;
|
|
567
|
+
return {
|
|
568
|
+
accessToken,
|
|
569
|
+
...(projectId ? { projectId } : {}),
|
|
570
|
+
};
|
|
571
|
+
}
|
|
506
572
|
function splitAntigravityRefresh(refresh) {
|
|
507
573
|
const [refreshToken = "", projectId = "", managedProjectId = ""] = refresh.split("|");
|
|
508
574
|
return {
|
|
@@ -519,26 +585,6 @@ function clampAccountIndex(index, accountCount) {
|
|
|
519
585
|
function shortHash(value) {
|
|
520
586
|
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
521
587
|
}
|
|
522
|
-
async function refreshGoogleAccessToken(refreshToken) {
|
|
523
|
-
if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
|
|
524
|
-
throw new Error("Antigravity Google OAuth credentials are not configured; set PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID and PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET.");
|
|
525
|
-
}
|
|
526
|
-
const response = await fetchWithTimeout(GOOGLE_TOKEN_REFRESH_URL, {
|
|
527
|
-
method: "POST",
|
|
528
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
529
|
-
body: new URLSearchParams({
|
|
530
|
-
client_id: GOOGLE_CLIENT_ID,
|
|
531
|
-
client_secret: GOOGLE_CLIENT_SECRET,
|
|
532
|
-
refresh_token: refreshToken,
|
|
533
|
-
grant_type: "refresh_token",
|
|
534
|
-
}),
|
|
535
|
-
});
|
|
536
|
-
if (!response.ok) {
|
|
537
|
-
const errorText = await response.text();
|
|
538
|
-
throw new Error(`Google token refresh failed (${response.status}): ${errorText}`);
|
|
539
|
-
}
|
|
540
|
-
return response.json();
|
|
541
|
-
}
|
|
542
588
|
async function fetchGoogleAntigravityQuota(accessToken, projectId) {
|
|
543
589
|
const response = await fetchWithTimeout(GOOGLE_QUOTA_API_URL, {
|
|
544
590
|
method: "POST",
|
|
@@ -633,18 +679,51 @@ function selectOpenAIRateLimitForModel(data, modelKey) {
|
|
|
633
679
|
return false;
|
|
634
680
|
return openAIModelMatchesAdditionalLimit(modelKey, limit);
|
|
635
681
|
});
|
|
682
|
+
// Prefer exact named per-model buckets when the API exposes them, but keep the
|
|
683
|
+
// top-level bucket as a fallback. Some Codex responses currently expose a
|
|
684
|
+
// usable selected-model/account bucket only at the top level while also
|
|
685
|
+
// listing unrelated named additional buckets; hiding the fallback makes the
|
|
686
|
+
// status bar disappear completely for those models.
|
|
636
687
|
return additionalLimit?.rate_limit ?? data.rate_limit;
|
|
637
688
|
}
|
|
638
689
|
function openAIModelMatchesAdditionalLimit(modelKey, limit) {
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
690
|
+
const modelId = modelKey.split("/").at(-1) ?? modelKey;
|
|
691
|
+
return openAIModelIdMatchesLimitCandidate(modelId, limit.limit_name)
|
|
692
|
+
|| (limit.metered_feature ? openAIModelIdMatchesLimitCandidate(modelId, limit.metered_feature) : false);
|
|
693
|
+
}
|
|
694
|
+
function openAIModelIdMatchesLimitCandidate(modelId, candidate) {
|
|
695
|
+
const modelTokens = openAILimitTokens(modelId);
|
|
696
|
+
const candidateTokens = openAILimitTokens(candidate);
|
|
697
|
+
if (modelTokens.length === 0 || candidateTokens.length === 0)
|
|
698
|
+
return false;
|
|
699
|
+
if (containsTokenSequence(candidateTokens, modelTokens))
|
|
700
|
+
return true;
|
|
701
|
+
// Support compact names such as o4mini while avoiding prefix matches such as
|
|
702
|
+
// gpt-5 accidentally matching gpt-5.5.
|
|
703
|
+
return normalizeOpenAILimitName(candidate) === normalizeOpenAILimitName(modelId);
|
|
644
704
|
}
|
|
645
705
|
function normalizeOpenAILimitName(value) {
|
|
646
706
|
return value.toLowerCase().replace(/[^a-z0-9]+/gu, "");
|
|
647
707
|
}
|
|
708
|
+
function openAILimitTokens(value) {
|
|
709
|
+
return value.toLowerCase().split(/[^a-z0-9]+/u).filter((token) => token.length > 0);
|
|
710
|
+
}
|
|
711
|
+
function containsTokenSequence(tokens, sequence) {
|
|
712
|
+
if (sequence.length > tokens.length)
|
|
713
|
+
return false;
|
|
714
|
+
for (let start = 0; start <= tokens.length - sequence.length; start += 1) {
|
|
715
|
+
let matches = true;
|
|
716
|
+
for (let offset = 0; offset < sequence.length; offset += 1) {
|
|
717
|
+
if (tokens[start + offset] !== sequence[offset]) {
|
|
718
|
+
matches = false;
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (matches)
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
648
727
|
function selectWeeklyWindow(windows) {
|
|
649
728
|
return windows
|
|
650
729
|
.filter((window) => window.limit_window_seconds >= 6 * DAY_SECONDS)
|
|
@@ -683,6 +762,11 @@ function googleAccountWindowFromResponse(data, label, quotaModelKey, now) {
|
|
|
683
762
|
windowSeconds: Math.max(0, Math.round((resetAt - now) / 1000)),
|
|
684
763
|
};
|
|
685
764
|
}
|
|
765
|
+
function googleAccountWindowsFromResponse(data, now) {
|
|
766
|
+
return GOOGLE_ACCOUNT_QUOTA_WINDOWS
|
|
767
|
+
.map((window) => googleAccountWindowFromResponse(data, window.label, window.quotaModelKey, now))
|
|
768
|
+
.filter((window) => window !== undefined);
|
|
769
|
+
}
|
|
686
770
|
function clampPercent(percent) {
|
|
687
771
|
return Math.max(0, Math.min(100, percent));
|
|
688
772
|
}
|
|
@@ -706,6 +790,8 @@ function formatQuotaBar(percent, width) {
|
|
|
706
790
|
return `${"█".repeat(filled)}${"░".repeat(width - filled)}`;
|
|
707
791
|
}
|
|
708
792
|
function formatDurationLong(resetAt, now) {
|
|
793
|
+
if (resetAt <= now)
|
|
794
|
+
return "reset";
|
|
709
795
|
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
710
796
|
const days = Math.floor(totalMinutes / 1440);
|
|
711
797
|
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
@@ -717,6 +803,8 @@ function formatDurationLong(resetAt, now) {
|
|
|
717
803
|
return `${minutes}m`;
|
|
718
804
|
}
|
|
719
805
|
function formatDurationShort(resetAt, now) {
|
|
806
|
+
if (resetAt <= now)
|
|
807
|
+
return "reset";
|
|
720
808
|
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
721
809
|
const days = Math.floor(totalMinutes / 1440);
|
|
722
810
|
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
@@ -737,6 +825,8 @@ function formatUsageWindow(_prefix, window, now) {
|
|
|
737
825
|
return `${window.remainingPercent}% ${formatCompactProgressBar(window.remainingPercent)} ${formatResetCountdown(window.resetAt, now)}`;
|
|
738
826
|
}
|
|
739
827
|
function formatResetCountdown(resetAt, now) {
|
|
828
|
+
if (resetAt <= now)
|
|
829
|
+
return "reset";
|
|
740
830
|
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
741
831
|
const days = Math.floor(totalMinutes / 1440);
|
|
742
832
|
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
@@ -148,7 +148,7 @@ export class AppPopupActionController {
|
|
|
148
148
|
this.host.render();
|
|
149
149
|
try {
|
|
150
150
|
if (selected.value === "copy") {
|
|
151
|
-
this.workspaceActions.copyUserMessage(selected.entryId);
|
|
151
|
+
await this.workspaceActions.copyUserMessage(selected.entryId);
|
|
152
152
|
return true;
|
|
153
153
|
}
|
|
154
154
|
if (selected.value === "fork") {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type AsyncProcessResult = {
|
|
2
|
+
status: number | null;
|
|
3
|
+
signal: NodeJS.Signals | null;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
error?: Error;
|
|
7
|
+
timedOut?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type RunProcessOptions = {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
input?: string;
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
maxBufferBytes?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function runProcess(command: string, args?: readonly string[], options?: RunProcessOptions): Promise<AsyncProcessResult>;
|
|
17
|
+
export declare function commandExists(command: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
const DEFAULT_MAX_BUFFER_BYTES = 1024 * 1024;
|
|
3
|
+
export async function runProcess(command, args = [], options = {}) {
|
|
4
|
+
const maxBufferBytes = Math.max(1, options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES);
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
let stdout = "";
|
|
7
|
+
let stderr = "";
|
|
8
|
+
let error;
|
|
9
|
+
let timedOut = false;
|
|
10
|
+
const child = spawn(command, [...args], {
|
|
11
|
+
cwd: options.cwd,
|
|
12
|
+
env: options.env,
|
|
13
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
14
|
+
});
|
|
15
|
+
const append = (current, chunk) => {
|
|
16
|
+
const next = `${current}${chunk.toString("utf8")}`;
|
|
17
|
+
return next.length > maxBufferBytes ? next.slice(-maxBufferBytes) : next;
|
|
18
|
+
};
|
|
19
|
+
const timer = options.timeoutMs === undefined
|
|
20
|
+
? undefined
|
|
21
|
+
: setTimeout(() => {
|
|
22
|
+
timedOut = true;
|
|
23
|
+
child.kill("SIGTERM");
|
|
24
|
+
}, options.timeoutMs);
|
|
25
|
+
timer?.unref?.();
|
|
26
|
+
child.stdout.on("data", (chunk) => {
|
|
27
|
+
stdout = append(stdout, chunk);
|
|
28
|
+
});
|
|
29
|
+
child.stderr.on("data", (chunk) => {
|
|
30
|
+
stderr = append(stderr, chunk);
|
|
31
|
+
});
|
|
32
|
+
child.once("error", (err) => {
|
|
33
|
+
error = err;
|
|
34
|
+
});
|
|
35
|
+
child.once("close", (status, signal) => {
|
|
36
|
+
if (timer)
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
resolve({
|
|
39
|
+
status,
|
|
40
|
+
signal,
|
|
41
|
+
stdout,
|
|
42
|
+
stderr,
|
|
43
|
+
...(error === undefined ? {} : { error }),
|
|
44
|
+
...(timedOut ? { timedOut } : {}),
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
if (options.input === undefined)
|
|
48
|
+
child.stdin.end();
|
|
49
|
+
else
|
|
50
|
+
child.stdin.end(options.input);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export async function commandExists(command, env = process.env) {
|
|
54
|
+
if (process.platform === "win32") {
|
|
55
|
+
const names = [command, command.replace(/\.exe$/iu, ".cmd"), command.replace(/\.exe$/iu, ".bat")];
|
|
56
|
+
for (const name of names) {
|
|
57
|
+
const result = await runProcess("where", [name], { env, maxBufferBytes: 256 });
|
|
58
|
+
if (result.status === 0)
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const result = await runProcess("sh", ["-lc", `command -v ${shellQuote(command)}`], { env, maxBufferBytes: 256 });
|
|
64
|
+
return result.status === 0;
|
|
65
|
+
}
|
|
66
|
+
function shellQuote(value) {
|
|
67
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
68
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applyOutputFilters } from "../../config.js";
|
|
2
2
|
import { renderMarkdownTextLines } from "../../markdown-format.js";
|
|
3
3
|
import { attachImageClickTargets } from "../screen/image-click-targets.js";
|
|
4
|
+
import { APP_ICONS } from "../icons.js";
|
|
4
5
|
import { horizontalPaddingLayout, padHorizontalText, wrapText } from "./render-text.js";
|
|
5
6
|
import { renderConversationShellEntry } from "./conversation-shell-renderer.js";
|
|
6
7
|
import { renderConversationToolEntry, renderThinkingEntry } from "./conversation-tool-renderer.js";
|
|
@@ -14,10 +15,11 @@ export function renderConversationEntry(entry, width, options) {
|
|
|
14
15
|
...(syntaxHighlight === undefined ? {} : { syntaxHighlight }),
|
|
15
16
|
...(entryId === undefined ? {} : { target: { kind: "user-message", id: entryId } }),
|
|
16
17
|
});
|
|
17
|
-
const queuedLine = (text, entryId) => ({
|
|
18
|
+
const queuedLine = (text, entryId, segments) => ({
|
|
18
19
|
text: padHorizontalText(text, width),
|
|
19
20
|
variant: "muted",
|
|
20
21
|
backgroundOverride: options.colors.userMessageBackground,
|
|
22
|
+
...(segments && segments.length > 0 ? { segments: segments.map((segment) => ({ ...segment, start: segment.start + userContentLeft, end: segment.end + userContentLeft })) } : {}),
|
|
21
23
|
target: { kind: "queue-message", id: entryId },
|
|
22
24
|
});
|
|
23
25
|
const userMessageLines = (userEntry) => {
|
|
@@ -29,11 +31,20 @@ export function renderConversationEntry(entry, width, options) {
|
|
|
29
31
|
lines.push(userLine("", userEntry.id));
|
|
30
32
|
return attachImageClickTargets(lines, userEntry.id, userEntry.images, { foreground: options.colors.info, underline: true });
|
|
31
33
|
};
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const queuedMessagePrefix = (queuedEntry) => {
|
|
35
|
+
const label = queuedEntry.queueSource === "sdk-steering"
|
|
36
|
+
? "steer"
|
|
37
|
+
: queuedEntry.queueSource === "sdk-follow-up"
|
|
38
|
+
? "follow"
|
|
39
|
+
: "queued";
|
|
40
|
+
return `${APP_ICONS.timerSand} ${label}:`;
|
|
41
|
+
};
|
|
42
|
+
const queuedMessageLines = (queuedEntry) => {
|
|
43
|
+
const icon = APP_ICONS.timerSand;
|
|
44
|
+
const prefix = queuedMessagePrefix(queuedEntry);
|
|
45
|
+
const contentLines = wrapText(`${prefix} ${queuedEntry.text}`, userContentWidth);
|
|
46
|
+
return contentLines.map((text, index) => queuedLine(text, queuedEntry.id, index === 0 ? [{ start: 0, end: icon.length, foreground: options.colors.info }] : undefined));
|
|
47
|
+
};
|
|
37
48
|
switch (entry.kind) {
|
|
38
49
|
case "system":
|
|
39
50
|
return wrapText(`system: ${entry.text}`, width).map((text) => ({ text, variant: "muted" }));
|
|
@@ -49,12 +49,13 @@ export function renderThinkingEntry(entry, width, options) {
|
|
|
49
49
|
const rule = resolveThinkingToolRule(options.pixConfig);
|
|
50
50
|
const markdownText = entry.text ? formatMarkdownTables(entry.text, Math.max(1, width - 2)) : "";
|
|
51
51
|
const expandedText = trimTrailingBlankLines(markdownText);
|
|
52
|
-
const compactExpandedText = options.superCompactTools ? removeBlankLines(expandedText) : expandedText;
|
|
53
52
|
const forceExpanded = Boolean(options.allThinkingExpanded);
|
|
53
|
+
const compactExpandedText = options.superCompactTools && forceExpanded ? removeBlankLines(expandedText) : expandedText;
|
|
54
|
+
const expanded = forceExpanded || (entry.expanded && expandedText.trim().length > 0);
|
|
54
55
|
return renderToolBlock({
|
|
55
56
|
id: entry.id,
|
|
56
57
|
toolName: THINKING_TOOL_NAME,
|
|
57
|
-
expanded
|
|
58
|
+
expanded,
|
|
58
59
|
status: entry.status,
|
|
59
60
|
isError: false,
|
|
60
61
|
output: markdownText,
|
|
@@ -10,6 +10,7 @@ export type EditorLayoutRendererHost = {
|
|
|
10
10
|
readonly subagentsPanelExpanded: boolean;
|
|
11
11
|
readonly subagentsWidgetState: SubagentsWidgetState | undefined;
|
|
12
12
|
readonly voicePartialText: string | undefined;
|
|
13
|
+
readonly autocompleteSuggestion: string | undefined;
|
|
13
14
|
renderExtensionInputComponent(width: number): string[] | undefined;
|
|
14
15
|
extensionInputUsesEditor(): boolean;
|
|
15
16
|
widgetTuiHandle(): WidgetTuiHandle;
|
|
@@ -145,7 +145,7 @@ export class EditorLayoutRenderer {
|
|
|
145
145
|
? this.limitExtensionInputLines(extensionLines, Math.max(0, maxRows - (usesEditor ? 1 : 0)))
|
|
146
146
|
: [];
|
|
147
147
|
const editorMaxRows = usesEditor ? Math.max(1, maxRows - customLines.length) : 1;
|
|
148
|
-
const rendered = this.host.inputEditor.render(contentWidth, editorMaxRows, "", "");
|
|
148
|
+
const rendered = this.host.inputEditor.render(contentWidth, editorMaxRows, "", "", usesEditor ? this.host.autocompleteSuggestion ?? "" : "");
|
|
149
149
|
const visibleLines = rendered.visualLines.slice(rendered.scrollOffset, rendered.scrollOffset + editorMaxRows);
|
|
150
150
|
const scrollBar = usesEditor
|
|
151
151
|
? inputScrollBarMetrics(rendered.visualLines.length, visibleLines.length, rendered.scrollOffset)
|
|
@@ -157,6 +157,12 @@ export class EditorLayoutRenderer {
|
|
|
157
157
|
end: span.end + left,
|
|
158
158
|
})))
|
|
159
159
|
: [];
|
|
160
|
+
const editorSuggestionSpans = usesEditor
|
|
161
|
+
? visibleLines.map((vl) => (vl.suggestionSpans ?? []).map((span) => ({
|
|
162
|
+
start: span.start + left,
|
|
163
|
+
end: span.end + left,
|
|
164
|
+
})))
|
|
165
|
+
: [];
|
|
160
166
|
const paddedCustomLines = customLines.map((line) => frameInputLine(padHorizontalText(line, width)));
|
|
161
167
|
return {
|
|
162
168
|
lines: [...paddedCustomLines, ...editorLines],
|
|
@@ -172,6 +178,10 @@ export class EditorLayoutRenderer {
|
|
|
172
178
|
...paddedCustomLines.map(() => []),
|
|
173
179
|
...editorTagSpans,
|
|
174
180
|
],
|
|
181
|
+
suggestionSpans: [
|
|
182
|
+
...paddedCustomLines.map(() => []),
|
|
183
|
+
...editorSuggestionSpans,
|
|
184
|
+
],
|
|
175
185
|
};
|
|
176
186
|
}
|
|
177
187
|
limitExtensionInputLines(lines, maxRows) {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { isRecord } from "../guards.js";
|
|
2
|
+
const MAX_FORMAT_STRING_CHARS = 256 * 1024;
|
|
3
|
+
const MAX_RENDERED_CONTENT_CHARS = 512 * 1024;
|
|
4
|
+
const MAX_STRUCTURED_DEPTH = 8;
|
|
5
|
+
const MAX_STRUCTURED_ARRAY_ITEMS = 200;
|
|
6
|
+
const MAX_STRUCTURED_OBJECT_KEYS = 200;
|
|
7
|
+
const TRUNCATED_MARKER = "\n[… truncated …]";
|
|
2
8
|
export function stringifyUnknown(value) {
|
|
3
9
|
if (typeof value === "string")
|
|
4
10
|
return value;
|
|
@@ -13,7 +19,7 @@ export function stringifyUnknown(value) {
|
|
|
13
19
|
return name;
|
|
14
20
|
}
|
|
15
21
|
try {
|
|
16
|
-
return JSON.stringify(value, null, 2);
|
|
22
|
+
return JSON.stringify(normalizeStructuredValue(value), null, 2);
|
|
17
23
|
}
|
|
18
24
|
catch {
|
|
19
25
|
return String(value);
|
|
@@ -24,8 +30,10 @@ export function formatStructuredText(value) {
|
|
|
24
30
|
const trimmed = value.trim();
|
|
25
31
|
if (!trimmed)
|
|
26
32
|
return "(empty)";
|
|
33
|
+
if (trimmed.length > MAX_FORMAT_STRING_CHARS)
|
|
34
|
+
return truncateText(value, MAX_FORMAT_STRING_CHARS);
|
|
27
35
|
try {
|
|
28
|
-
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
36
|
+
return JSON.stringify(normalizeStructuredValue(JSON.parse(trimmed)), null, 2);
|
|
29
37
|
}
|
|
30
38
|
catch {
|
|
31
39
|
return value;
|
|
@@ -36,25 +44,40 @@ export function formatStructuredText(value) {
|
|
|
36
44
|
export function renderContent(content) {
|
|
37
45
|
const parts = [];
|
|
38
46
|
let imageCount = 0;
|
|
47
|
+
let renderedChars = 0;
|
|
48
|
+
const pushPart = (part) => {
|
|
49
|
+
const remaining = MAX_RENDERED_CONTENT_CHARS - renderedChars;
|
|
50
|
+
if (remaining <= 0)
|
|
51
|
+
return false;
|
|
52
|
+
const next = part.length > remaining ? truncateText(part, remaining) : part;
|
|
53
|
+
parts.push(next);
|
|
54
|
+
renderedChars += next.length;
|
|
55
|
+
return part.length <= remaining;
|
|
56
|
+
};
|
|
39
57
|
for (const item of content) {
|
|
40
58
|
if (!isRecord(item)) {
|
|
41
|
-
|
|
59
|
+
if (!pushPart(stringifyUnknown(item)))
|
|
60
|
+
break;
|
|
42
61
|
continue;
|
|
43
62
|
}
|
|
44
63
|
if (isImageContent(item)) {
|
|
45
64
|
imageCount += 1;
|
|
46
|
-
|
|
65
|
+
if (!pushPart(imageContentLabel(item, imageCount)))
|
|
66
|
+
break;
|
|
47
67
|
continue;
|
|
48
68
|
}
|
|
49
69
|
if (typeof item.text === "string") {
|
|
50
|
-
|
|
70
|
+
if (!pushPart(item.text))
|
|
71
|
+
break;
|
|
51
72
|
continue;
|
|
52
73
|
}
|
|
53
74
|
if (typeof item.thinking === "string") {
|
|
54
|
-
|
|
75
|
+
if (!pushPart(item.thinking))
|
|
76
|
+
break;
|
|
55
77
|
continue;
|
|
56
78
|
}
|
|
57
|
-
|
|
79
|
+
if (!pushPart(stringifyUnknown(item)))
|
|
80
|
+
break;
|
|
58
81
|
}
|
|
59
82
|
return parts.join("\n");
|
|
60
83
|
}
|
|
@@ -113,3 +136,38 @@ export function submittedUserDisplayText(displayText, promptText, images) {
|
|
|
113
136
|
return userImageLabels(images.length);
|
|
114
137
|
return promptText.trimEnd();
|
|
115
138
|
}
|
|
139
|
+
function truncateText(text, maxChars) {
|
|
140
|
+
if (text.length <= maxChars)
|
|
141
|
+
return text;
|
|
142
|
+
return `${text.slice(0, Math.max(0, maxChars))}${TRUNCATED_MARKER}`;
|
|
143
|
+
}
|
|
144
|
+
function normalizeStructuredValue(value, depth = 0, seen = new WeakSet()) {
|
|
145
|
+
if (typeof value === "string")
|
|
146
|
+
return truncateText(value, MAX_FORMAT_STRING_CHARS);
|
|
147
|
+
if (!value || typeof value !== "object")
|
|
148
|
+
return value;
|
|
149
|
+
if (depth >= MAX_STRUCTURED_DEPTH)
|
|
150
|
+
return "[… truncated: depth limit …]";
|
|
151
|
+
if (seen.has(value))
|
|
152
|
+
return "[… circular …]";
|
|
153
|
+
seen.add(value);
|
|
154
|
+
if (Array.isArray(value)) {
|
|
155
|
+
const items = value.slice(0, MAX_STRUCTURED_ARRAY_ITEMS).map((item) => normalizeStructuredValue(item, depth + 1, seen));
|
|
156
|
+
if (value.length > MAX_STRUCTURED_ARRAY_ITEMS)
|
|
157
|
+
items.push(`[… ${value.length - MAX_STRUCTURED_ARRAY_ITEMS} more items …]`);
|
|
158
|
+
return items;
|
|
159
|
+
}
|
|
160
|
+
if (value instanceof Error)
|
|
161
|
+
return value.message || value.name;
|
|
162
|
+
const output = {};
|
|
163
|
+
let count = 0;
|
|
164
|
+
for (const [key, child] of Object.entries(value)) {
|
|
165
|
+
if (count >= MAX_STRUCTURED_OBJECT_KEYS) {
|
|
166
|
+
output["…"] = "truncated: object key limit";
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
output[key] = normalizeStructuredValue(child, depth + 1, seen);
|
|
170
|
+
count += 1;
|
|
171
|
+
}
|
|
172
|
+
return output;
|
|
173
|
+
}
|
|
@@ -68,8 +68,11 @@ export class AppRenderController {
|
|
|
68
68
|
this.deps.mouseController.statusThinkingTarget = undefined;
|
|
69
69
|
this.deps.mouseController.statusContextTarget = undefined;
|
|
70
70
|
this.deps.mouseController.statusModelUsageTarget = undefined;
|
|
71
|
+
this.deps.mouseController.statusDraftQueueTarget = undefined;
|
|
72
|
+
this.deps.mouseController.statusUserJumpTarget = undefined;
|
|
71
73
|
this.deps.mouseController.statusThinkingExpandTarget = undefined;
|
|
72
74
|
this.deps.mouseController.statusCompactToolsTarget = undefined;
|
|
75
|
+
this.deps.mouseController.statusTerminalBellSoundTarget = undefined;
|
|
73
76
|
this.deps.mouseController.statusSessionTarget = undefined;
|
|
74
77
|
this.deps.mouseController.statusPromptEnhancerTarget = undefined;
|
|
75
78
|
this.deps.mouseController.statusVoiceMicTarget = undefined;
|
|
@@ -165,10 +168,11 @@ export class AppRenderController {
|
|
|
165
168
|
for (let index = 0; index < renderedInput.lines.length; index += 1) {
|
|
166
169
|
const inputLine = renderedInput.lines[index] ?? "";
|
|
167
170
|
const tagSpans = renderedInput.tagSpans[index];
|
|
171
|
+
const suggestionSpans = renderedInput.suggestionSpans?.[index] ?? [];
|
|
168
172
|
const row = toScreenRow(inputStartRow + index);
|
|
169
173
|
this.deps.mouseController.renderedRowTexts.set(row, inputLine);
|
|
170
174
|
const tagColor = this.deps.theme.colors.accent;
|
|
171
|
-
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, columns, tagColor, this.deps.theme.colors.inputBorder);
|
|
175
|
+
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, suggestionSpans, columns, tagColor, this.deps.theme.colors.muted, this.deps.theme.colors.inputBorder);
|
|
172
176
|
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, styledLine));
|
|
173
177
|
}
|
|
174
178
|
if (renderedInput.scrollBar && columns > 0) {
|
|
@@ -291,6 +295,7 @@ export class AppRenderController {
|
|
|
291
295
|
this.deps.mouseController.statusThinkingTarget = this.deps.statusLineRenderer.thinkingTarget(statusLayout.text, statusRow);
|
|
292
296
|
this.deps.mouseController.statusContextTarget = this.deps.statusLineRenderer.contextTarget(statusLayout.text, statusRow, statusLayout);
|
|
293
297
|
this.deps.mouseController.statusModelUsageTarget = this.deps.statusLineRenderer.modelUsageTarget(statusLayout.text, statusRow, statusLayout);
|
|
298
|
+
this.deps.mouseController.statusDraftQueueTarget = this.deps.statusLineRenderer.draftQueueTarget?.(statusLayout, statusRow);
|
|
294
299
|
this.deps.mouseController.statusUserJumpTarget = this.deps.statusLineRenderer.userJumpTarget?.(statusLayout, statusRow);
|
|
295
300
|
this.deps.mouseController.statusThinkingExpandTarget = this.deps.statusLineRenderer.thinkingExpandTarget?.(statusLayout, statusRow);
|
|
296
301
|
this.deps.mouseController.statusCompactToolsTarget = this.deps.statusLineRenderer.compactToolsTarget?.(statusLayout, statusRow);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Theme } from "../../theme.js";
|
|
2
2
|
import type { ToolStatusEntry } from "../types.js";
|
|
3
3
|
export declare function sanitizeText(text: string): string;
|
|
4
|
+
export declare function alertIconPrefixLength(text: string): number | undefined;
|
|
4
5
|
export declare function normalizePastedTextForDuplicateKey(text: string): string;
|
|
5
6
|
export declare function shortHash(text: string): string;
|
|
6
7
|
export declare function hasLspDiagnosticsAfterMutation(output: string): boolean;
|
|
7
8
|
export declare function hasToolLspDiagnosticsAfterMutation(entry: ToolStatusEntry): boolean;
|
|
9
|
+
export declare function lspDiagnosticSeverityForLine(line: string): "error" | "warning" | "hint" | undefined;
|
|
10
|
+
export declare function toolLspDiagnosticsAfterMutationSeverity(entry: ToolStatusEntry): "error" | "warning" | undefined;
|
|
8
11
|
export declare function toolStatusIcon(entry: ToolStatusEntry): string;
|
|
9
12
|
export declare function toolStatusIconColor(entry: ToolStatusEntry, colors: Theme["colors"]): string;
|
|
10
13
|
export declare function wrapLine(text: string, width: number): string[];
|