pi-open-tui 0.4.2 → 0.4.4
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 +35 -0
- package/README.md +52 -120
- package/README.zh-CN.md +52 -120
- package/docs/configuration.md +248 -0
- package/docs/footer-segments.md +121 -0
- package/docs/implementation.md +185 -0
- package/extensions/open-tui/config.ts +28 -7
- package/extensions/open-tui/copy-buttons.ts +144 -63
- package/extensions/open-tui/footer-segments.ts +119 -0
- package/extensions/open-tui/footer.ts +129 -18
- package/extensions/open-tui/index.ts +8 -1
- package/extensions/open-tui/settings-command.ts +315 -63
- package/extensions/open-tui/tool-preview.ts +8 -2
- package/extensions/open-tui/tool-view.ts +184 -31
- package/extensions/open-tui/utils.ts +18 -6
- package/package.json +9 -8
|
@@ -2,6 +2,7 @@ import type { ExtensionContext, Theme, ThemeColor } from "@earendil-works/pi-cod
|
|
|
2
2
|
import { hostname as osHostname } from "node:os";
|
|
3
3
|
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
4
4
|
import type { OpenTuiConfig } from "./config.ts";
|
|
5
|
+
import type { FooterBuiltinSegment, FooterPosition, RegisteredFooterSegment } from "./footer-segments.ts";
|
|
5
6
|
import type { IconGlyphs } from "./icons.ts";
|
|
6
7
|
import { resolveGlyphs, resolveIconMode, runtimeSymbol } from "./icons.ts";
|
|
7
8
|
import type { GitStatus } from "./git.ts";
|
|
@@ -55,13 +56,14 @@ function renderBar(theme: Theme, pct: number, barWidth: number, ascii: boolean):
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
/** Compact context form:
|
|
59
|
-
function renderContextCompact(theme: Theme, ctx: ExtensionContext, glyphs: IconGlyphs): string {
|
|
59
|
+
/** Compact context form: percentage only, no bar, token counts, or icon when the bar is off. */
|
|
60
|
+
function renderContextCompact(theme: Theme, ctx: ExtensionContext, glyphs: IconGlyphs, showBar: boolean): string {
|
|
60
61
|
const contextUsage = ctx.getContextUsage();
|
|
61
62
|
const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
62
63
|
if (contextWindow <= 0) return "";
|
|
63
64
|
const contextPct = contextUsage?.percent ?? 0;
|
|
64
|
-
|
|
65
|
+
const pctText = theme.fg(stressColor(contextPct), `${contextPct.toFixed(1)}%`);
|
|
66
|
+
return showBar ? `${theme.fg(stressColor(contextPct), glyphs.context)} ${pctText}` : pctText;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function renderGitSegment(
|
|
@@ -148,6 +150,7 @@ function renderContextBar(
|
|
|
148
150
|
width: number,
|
|
149
151
|
glyphs: IconGlyphs,
|
|
150
152
|
iconMode: OpenTuiConfig["icons"]["mode"],
|
|
153
|
+
showBar: boolean,
|
|
151
154
|
): string {
|
|
152
155
|
const contextUsage = ctx.getContextUsage();
|
|
153
156
|
const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
@@ -160,6 +163,7 @@ function renderContextBar(
|
|
|
160
163
|
|
|
161
164
|
const pctText = theme.fg(stressColor(contextPct), `${contextPct.toFixed(1)}%`);
|
|
162
165
|
const ctxText = `${theme.fg("text", fmtTokens(contextTokens))}${theme.fg("dim", "/")}${theme.fg("text", fmtTokens(contextWindow))}`;
|
|
166
|
+
if (!showBar) return `${pctText} ${theme.fg("dim", "·")} ${ctxText}`;
|
|
163
167
|
const contextIcon = theme.fg(stressColor(contextPct), glyphs.context);
|
|
164
168
|
// Cap the bar at `width - reserved` (with a floor of 4) so the full form
|
|
165
169
|
// never forces the left segments out before compact/drop logic kicks in.
|
|
@@ -168,12 +172,12 @@ function renderContextBar(
|
|
|
168
172
|
return `${contextIcon} ${renderBar(theme, contextPct, barWidth, resolveIconMode(iconMode) === "ascii")} ${pctText} ${theme.fg("dim", "·")} ${ctxText}`;
|
|
169
173
|
}
|
|
170
174
|
|
|
171
|
-
function
|
|
175
|
+
function renderStatsSegments(
|
|
172
176
|
theme: Theme,
|
|
173
177
|
totals: UsageTotals,
|
|
174
178
|
glyphs: IconGlyphs,
|
|
175
179
|
segments: OpenTuiConfig["footerSegments"],
|
|
176
|
-
): string {
|
|
180
|
+
): { tokens: string; cost: string } {
|
|
177
181
|
const stats: string[] = [];
|
|
178
182
|
if (segments.tokens) {
|
|
179
183
|
stats.push(theme.fg("accent", `${glyphs.input} ${formatInputBreakdown(totals.input, totals.cacheRead)}`));
|
|
@@ -185,11 +189,10 @@ function renderStatsBlock(
|
|
|
185
189
|
stats.push(theme.fg(cacheHitColor(totals.latestCacheHitRate), `${glyphs.cacheHit} ${totals.latestCacheHitRate.toFixed(1)}%`));
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
|
-
|
|
189
|
-
stats.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return stats.join(` ${theme.fg("dim", "|")} `);
|
|
192
|
+
return {
|
|
193
|
+
tokens: stats.join(` ${theme.fg("dim", "|")} `),
|
|
194
|
+
cost: segments.cost ? theme.fg("warning", `${glyphs.cost} $${totals.cost.toFixed(3)}`) : "",
|
|
195
|
+
};
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
const ANSI_RESET = "\x1b[0m";
|
|
@@ -226,6 +229,7 @@ export interface FooterHooks {
|
|
|
226
229
|
setRequestRender: (fn: (() => void) | undefined) => void;
|
|
227
230
|
scheduleGitRefresh: () => void;
|
|
228
231
|
getLiveTps?: () => LiveTps | null;
|
|
232
|
+
getExtensionSegments?: () => readonly RegisteredFooterSegment[];
|
|
229
233
|
canEmbedInEditor?: () => boolean;
|
|
230
234
|
}
|
|
231
235
|
|
|
@@ -278,6 +282,84 @@ function fitInlineSegments(
|
|
|
278
282
|
).filter(Boolean).join(separator);
|
|
279
283
|
}
|
|
280
284
|
|
|
285
|
+
type FooterSide = "left" | "right";
|
|
286
|
+
type FooterLayoutMode = "plain" | "inline";
|
|
287
|
+
|
|
288
|
+
// Empty built-ins retain their slots so before/after contributions remain visible.
|
|
289
|
+
const FOOTER_ANCHORS: Record<FooterLayoutMode, Record<1 | 2, Record<FooterSide, readonly FooterBuiltinSegment[]>>> = {
|
|
290
|
+
plain: {
|
|
291
|
+
1: { left: ["cwd", "hostname", "sessionName", "git", "runtime", "timer"], right: ["context"] },
|
|
292
|
+
2: { left: ["model"], right: ["tokens", "cost"] },
|
|
293
|
+
},
|
|
294
|
+
inline: {
|
|
295
|
+
1: { left: ["sessionName", "git"], right: ["cwd", "hostname", "runtime", "timer", "context"] },
|
|
296
|
+
2: { left: ["model"], right: ["tokens", "cost"] },
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
function fitContributedRow(
|
|
301
|
+
row: 1 | 2,
|
|
302
|
+
mode: FooterLayoutMode,
|
|
303
|
+
builtins: Partial<Record<FooterBuiltinSegment, PrioritizedSegment>>,
|
|
304
|
+
contributions: readonly RegisteredFooterSegment[],
|
|
305
|
+
width: number,
|
|
306
|
+
theme: Theme,
|
|
307
|
+
): InlineFooterLine | undefined {
|
|
308
|
+
let contributed = false;
|
|
309
|
+
const collect = (side: FooterSide): PrioritizedSegment[] => {
|
|
310
|
+
const parts: PrioritizedSegment[] = [];
|
|
311
|
+
const append = (matches: (position: FooterPosition) => boolean) => {
|
|
312
|
+
for (const segment of contributions) {
|
|
313
|
+
if (!matches(segment.position)) continue;
|
|
314
|
+
contributed = true;
|
|
315
|
+
parts.push({
|
|
316
|
+
text: renderStatusText(theme, segment.text),
|
|
317
|
+
compactText: segment.compactText && visibleWidth(segment.compactText) > 0
|
|
318
|
+
? renderStatusText(theme, segment.compactText)
|
|
319
|
+
: undefined,
|
|
320
|
+
priority: segment.priority,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const atEdge = (edge: "start" | "end") => append((position) => "row" in position
|
|
325
|
+
&& position.row === row && position.side === side && (position.edge ?? "end") === edge);
|
|
326
|
+
atEdge("start");
|
|
327
|
+
for (const anchor of FOOTER_ANCHORS[mode][row][side]) {
|
|
328
|
+
append((position) => "before" in position && position.before === anchor);
|
|
329
|
+
const builtin = builtins[anchor];
|
|
330
|
+
if (builtin?.text) parts.push(builtin);
|
|
331
|
+
append((position) => "after" in position && position.after === anchor);
|
|
332
|
+
}
|
|
333
|
+
atEdge("end");
|
|
334
|
+
return parts;
|
|
335
|
+
};
|
|
336
|
+
const left = collect("left");
|
|
337
|
+
const right = collect("right");
|
|
338
|
+
if (!contributed) return undefined;
|
|
339
|
+
|
|
340
|
+
const leftSeparator = row === 1 && mode === "plain" ? " " : theme.fg("dim", " · ");
|
|
341
|
+
const rightSeparator = row === 1 ? leftSeparator : theme.fg("dim", " | ");
|
|
342
|
+
const fitted = fitSegmentsByPriority([...left, ...right], width, theme.fg("dim", "..."), {
|
|
343
|
+
compactOrder: "priority",
|
|
344
|
+
gapWidth: (previous, next) => {
|
|
345
|
+
if (previous < left.length && next >= left.length) return 1;
|
|
346
|
+
return visibleWidth(next < left.length ? leftSeparator : rightSeparator);
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
// Truncation can remove an input's trailing reset; terminate each surviving style.
|
|
350
|
+
const join = (parts: string[], separator: string) => parts.filter(Boolean)
|
|
351
|
+
.map((part) => `${part}${ANSI_RESET}`).join(separator);
|
|
352
|
+
return {
|
|
353
|
+
left: join(fitted.slice(0, left.length), leftSeparator),
|
|
354
|
+
right: join(fitted.slice(left.length), rightSeparator),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function alignContributedRow(line: InlineFooterLine, width: number, theme: Theme): string {
|
|
359
|
+
if (!line.right) return truncateToWidth(line.left, width, theme.fg("dim", "..."));
|
|
360
|
+
return alignRight(line.left, line.right, width, theme);
|
|
361
|
+
}
|
|
362
|
+
|
|
281
363
|
function renderFooterContent(
|
|
282
364
|
ctx: ExtensionContext,
|
|
283
365
|
getState: () => FooterState,
|
|
@@ -287,6 +369,7 @@ function renderFooterContent(
|
|
|
287
369
|
footerData: FooterDataLike,
|
|
288
370
|
width: number,
|
|
289
371
|
getLiveTps: () => LiveTps | null,
|
|
372
|
+
getExtensionSegments: () => readonly RegisteredFooterSegment[],
|
|
290
373
|
): { mainLines: [string, string]; inlineLines: InlineFooterLines; extensionLines: string[] } {
|
|
291
374
|
if (width <= 0) {
|
|
292
375
|
return {
|
|
@@ -310,6 +393,10 @@ function renderFooterContent(
|
|
|
310
393
|
}
|
|
311
394
|
const meta = getModelMeta();
|
|
312
395
|
const totals = getUsageTotals(ctx);
|
|
396
|
+
const builtins: Partial<Record<FooterBuiltinSegment, PrioritizedSegment>> = {};
|
|
397
|
+
const contributions = segments.extensionSegments
|
|
398
|
+
? getExtensionSegments().filter((segment) => visibleWidth(segment.text) > 0)
|
|
399
|
+
: [];
|
|
313
400
|
|
|
314
401
|
const leftParts: PrioritizedSegment[] = [];
|
|
315
402
|
const inlineTopLeftParts: PrioritizedSegment[] = [];
|
|
@@ -332,6 +419,7 @@ function renderFooterContent(
|
|
|
332
419
|
},
|
|
333
420
|
});
|
|
334
421
|
inlineTopRightParts.push(leftParts.at(-1)!);
|
|
422
|
+
builtins.cwd = leftParts.at(-1)!;
|
|
335
423
|
}
|
|
336
424
|
if (segments.hostname) {
|
|
337
425
|
const shortHost = shortHostname(osHostname());
|
|
@@ -341,6 +429,7 @@ function renderFooterContent(
|
|
|
341
429
|
priority: 3,
|
|
342
430
|
});
|
|
343
431
|
inlineTopRightParts.push(leftParts.at(-1)!);
|
|
432
|
+
builtins.hostname = leftParts.at(-1)!;
|
|
344
433
|
}
|
|
345
434
|
}
|
|
346
435
|
const sessionName = ctx.sessionManager.getSessionName();
|
|
@@ -352,18 +441,21 @@ function renderFooterContent(
|
|
|
352
441
|
if (segments.sessionName) {
|
|
353
442
|
leftParts.push(sessionPart);
|
|
354
443
|
inlineTopLeftParts.push(sessionPart);
|
|
444
|
+
builtins.sessionName = sessionPart;
|
|
355
445
|
}
|
|
356
446
|
}
|
|
357
447
|
const gitSeg = renderGitSegment(theme, state.git, glyphs, segments);
|
|
358
448
|
if (gitSeg) {
|
|
359
449
|
leftParts.push({ text: gitSeg, priority: 1 });
|
|
360
450
|
inlineTopLeftParts.push(leftParts.at(-1)!);
|
|
451
|
+
builtins.git = leftParts.at(-1)!;
|
|
361
452
|
}
|
|
362
453
|
if (segments.runtime) {
|
|
363
454
|
const runtimeSeg = renderRuntimeSegment(theme, state.runtime, config.icons.mode, segments.nerdIconSpacing);
|
|
364
455
|
if (runtimeSeg) {
|
|
365
456
|
leftParts.push({ text: runtimeSeg, priority: 0 });
|
|
366
457
|
inlineTopRightParts.push(leftParts.at(-1)!);
|
|
458
|
+
builtins.runtime = leftParts.at(-1)!;
|
|
367
459
|
}
|
|
368
460
|
}
|
|
369
461
|
const timerSeg = renderTimerSegment(theme, state, glyphs);
|
|
@@ -377,6 +469,7 @@ function renderFooterContent(
|
|
|
377
469
|
// Narrow widths shed the live TPS before the timer itself.
|
|
378
470
|
leftParts.push({ text, compactText: liveTps === null ? undefined : timerSeg, priority: 3 });
|
|
379
471
|
inlineTopRightParts.push(leftParts.at(-1)!);
|
|
472
|
+
builtins.timer = leftParts.at(-1)!;
|
|
380
473
|
}
|
|
381
474
|
|
|
382
475
|
// The context bar competes with the left segments for the same row:
|
|
@@ -384,8 +477,8 @@ function renderFooterContent(
|
|
|
384
477
|
let contextText = "";
|
|
385
478
|
let contextCompact: string | undefined;
|
|
386
479
|
if (segments.context) {
|
|
387
|
-
contextText = renderContextBar(theme, ctx, width, glyphs, config.icons.mode);
|
|
388
|
-
const compact = renderContextCompact(theme, ctx, glyphs);
|
|
480
|
+
contextText = renderContextBar(theme, ctx, width, glyphs, config.icons.mode, segments.contextBar);
|
|
481
|
+
const compact = renderContextCompact(theme, ctx, glyphs, segments.contextBar);
|
|
389
482
|
if (compact && visibleWidth(compact) < visibleWidth(contextText)) {
|
|
390
483
|
contextCompact = compact;
|
|
391
484
|
}
|
|
@@ -396,16 +489,20 @@ function renderFooterContent(
|
|
|
396
489
|
allParts.push({ text: contextText, compactText: contextCompact, priority: 0 });
|
|
397
490
|
// Context stays at the far right; cwd is the first item in this group.
|
|
398
491
|
inlineTopRightParts.push({ text: contextText, compactText: contextCompact, priority: 0 });
|
|
492
|
+
builtins.context = allParts.at(-1)!;
|
|
399
493
|
}
|
|
400
494
|
|
|
401
495
|
const fitted = fitSegmentsByPriority(allParts, width, theme.fg("dim", "..."));
|
|
402
496
|
const fittedContext = contextText ? fitted.pop() ?? "" : "";
|
|
403
|
-
const
|
|
497
|
+
const contributedTop = fitContributedRow(1, "plain", builtins, contributions, width, theme);
|
|
498
|
+
const line1 = contributedTop
|
|
499
|
+
? alignContributedRow(contributedTop, width, theme)
|
|
500
|
+
: alignRight(fitted.filter(Boolean).join(" "), fittedContext, width, theme);
|
|
404
501
|
|
|
405
502
|
const inlineLeftBudget = Math.floor(width * 0.45);
|
|
406
503
|
const inlineTopLeft = fitInlineSegments(inlineTopLeftParts, inlineLeftBudget, theme);
|
|
407
504
|
const inlineRightBudget = Math.max(0, width - visibleWidth(inlineTopLeft) - (inlineTopLeft ? 1 : 0));
|
|
408
|
-
const inlineTop: InlineFooterLine = {
|
|
505
|
+
const inlineTop: InlineFooterLine = fitContributedRow(1, "inline", builtins, contributions, width, theme) ?? {
|
|
409
506
|
left: inlineTopLeft,
|
|
410
507
|
right: fitInlineSegments(inlineTopRightParts, inlineRightBudget, theme),
|
|
411
508
|
};
|
|
@@ -420,9 +517,16 @@ function renderFooterContent(
|
|
|
420
517
|
modelParts.push(theme.fg(effortColor(meta.effort), `${glyphs.thinking} ${meta.effort}`));
|
|
421
518
|
}
|
|
422
519
|
const modelBlock = modelParts.join(theme.fg("dim", " · "));
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
|
|
520
|
+
const stats = renderStatsSegments(theme, totals, glyphs, segments);
|
|
521
|
+
const statsBlock = [stats.tokens, stats.cost].filter(Boolean).join(` ${theme.fg("dim", "|")} `);
|
|
522
|
+
builtins.model = { text: modelBlock, priority: 0 };
|
|
523
|
+
builtins.tokens = { text: stats.tokens, priority: 2 };
|
|
524
|
+
builtins.cost = { text: stats.cost, priority: 1 };
|
|
525
|
+
const contributedBottom = fitContributedRow(2, "plain", builtins, contributions, width, theme);
|
|
526
|
+
const inlineBottom = contributedBottom ?? fitInlineLine(modelBlock, statsBlock, width, theme);
|
|
527
|
+
const line2 = contributedBottom
|
|
528
|
+
? alignContributedRow(contributedBottom, width, theme)
|
|
529
|
+
: alignRight(inlineBottom.left, inlineBottom.right, width, theme);
|
|
426
530
|
const mainLines: [string, string] = [line1, line2]
|
|
427
531
|
.map((line) => truncateToWidth(line, width, theme.fg("dim", "..."))) as [string, string];
|
|
428
532
|
const extensionLines = segments.extensionStatuses
|
|
@@ -439,8 +543,11 @@ export function installFooter(
|
|
|
439
543
|
hooks: FooterHooks,
|
|
440
544
|
): FooterHandle {
|
|
441
545
|
let renderInline: (width: number) => InlineFooterLines | undefined = () => undefined;
|
|
546
|
+
let mounted = false;
|
|
442
547
|
|
|
443
548
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
549
|
+
mounted = true;
|
|
550
|
+
let disposed = false;
|
|
444
551
|
hooks.setRequestRender(() => tui.requestRender());
|
|
445
552
|
const unsubBranch = footerData.onBranchChange(() => {
|
|
446
553
|
hooks.scheduleGitRefresh();
|
|
@@ -455,12 +562,16 @@ export function installFooter(
|
|
|
455
562
|
footerData,
|
|
456
563
|
width,
|
|
457
564
|
hooks.getLiveTps ?? (() => null),
|
|
565
|
+
hooks.getExtensionSegments ?? (() => []),
|
|
458
566
|
);
|
|
459
567
|
|
|
460
568
|
renderInline = (width) => getContent(width).inlineLines;
|
|
461
569
|
|
|
462
570
|
return {
|
|
463
571
|
dispose() {
|
|
572
|
+
if (disposed) return;
|
|
573
|
+
disposed = true;
|
|
574
|
+
mounted = false;
|
|
464
575
|
unsubBranch();
|
|
465
576
|
hooks.setRequestRender(undefined);
|
|
466
577
|
renderInline = () => undefined;
|
|
@@ -478,7 +589,7 @@ export function installFooter(
|
|
|
478
589
|
return {
|
|
479
590
|
renderInline: (width) => renderInline(width),
|
|
480
591
|
cleanup() {
|
|
481
|
-
ctx.ui.setFooter(undefined);
|
|
592
|
+
if (mounted) ctx.ui.setFooter(undefined);
|
|
482
593
|
},
|
|
483
594
|
};
|
|
484
595
|
}
|
|
@@ -3,6 +3,7 @@ import { type OpenTuiConfig, DEFAULT_CONFIG, ensureConfigExists, loadConfig, sav
|
|
|
3
3
|
import { installCopyButtons, markCopyScope } from "./copy-buttons.ts";
|
|
4
4
|
import { installEditor } from "./editor.ts";
|
|
5
5
|
import { installFooter } from "./footer.ts";
|
|
6
|
+
import { createFooterSegments } from "./footer-segments.ts";
|
|
6
7
|
import { installHeader } from "./header.ts";
|
|
7
8
|
import { emptyGitStatus, readGitStatus } from "./git.ts";
|
|
8
9
|
import { readRuntimeInfo } from "./runtime.ts";
|
|
@@ -52,6 +53,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
52
53
|
let editor: ReturnType<typeof installEditor> | undefined;
|
|
53
54
|
let copyButtons: ReturnType<typeof installCopyButtons> | undefined;
|
|
54
55
|
let pendingUiChange: PendingUiChange | undefined;
|
|
56
|
+
const footerSegments = createFooterSegments(pi.events, () => requestFooterRender?.());
|
|
55
57
|
|
|
56
58
|
// thinking-peek state (rendered by Pi inside its hidden-thinking block)
|
|
57
59
|
const peek: PeekState = createPeekState();
|
|
@@ -114,11 +116,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
114
116
|
{
|
|
115
117
|
setRequestRender: (fn) => {
|
|
116
118
|
requestFooterRender = fn ?? undefined;
|
|
119
|
+
if (!fn) footerSegments.deactivate();
|
|
120
|
+
else if (active && sessionLifecycle.isCurrent()) footerSegments.activate();
|
|
117
121
|
},
|
|
118
122
|
scheduleGitRefresh: () => {
|
|
119
123
|
void scheduleGitRefresh(ctx);
|
|
120
124
|
},
|
|
121
125
|
getLiveTps: () => turnTelemetry.getLiveTps(),
|
|
126
|
+
getExtensionSegments: footerSegments.getSegments,
|
|
122
127
|
canEmbedInEditor: () => editor?.isFramed() ?? false,
|
|
123
128
|
},
|
|
124
129
|
);
|
|
@@ -138,6 +143,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
138
143
|
isEnabled: () => config.copyButtons,
|
|
139
144
|
});
|
|
140
145
|
active = true;
|
|
146
|
+
if (requestFooterRender) footerSegments.activate();
|
|
141
147
|
}
|
|
142
148
|
};
|
|
143
149
|
|
|
@@ -237,6 +243,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
237
243
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
238
244
|
sessionLifecycle.shutdown();
|
|
239
245
|
stopWorkingTimer();
|
|
246
|
+
footerSegments.deactivate();
|
|
240
247
|
if (active) {
|
|
241
248
|
uninstallUi(ctx);
|
|
242
249
|
}
|
|
@@ -373,7 +380,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
373
380
|
config = loadConfig();
|
|
374
381
|
configLoaded = true;
|
|
375
382
|
}
|
|
376
|
-
return config.enabled ? config.toolView.mode
|
|
383
|
+
return config.enabled ? config.toolView : { ...config.toolView, mode: "native" };
|
|
377
384
|
},
|
|
378
385
|
() => resolveGlyphs(config.icons.mode),
|
|
379
386
|
);
|