pi-cc-extensions 0.8.51 → 0.8.52
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.en.md +9 -12
- package/README.md +9 -12
- package/extensions/config/config.ts +9 -4
- package/extensions/config/panel.ts +112 -20
- package/extensions/feature/agent-autocomplete.ts +3 -1
- package/extensions/feature/agent-summary.ts +2 -2
- package/extensions/feature/compact-thinking.ts +109 -48
- package/extensions/feature/context.ts +1 -1
- package/extensions/feature/session-reference-core.ts +3 -2
- package/extensions/feature/session-reference.ts +49 -21
- package/extensions/index.ts +12 -0
- package/extensions/renderer/compact-mode.ts +949 -0
- package/extensions/renderer/index.ts +73 -17
- package/extensions/renderer/message-display.ts +1 -1
- package/extensions/renderer/mouse-hover.ts +130 -0
- package/extensions/renderer/mouse-interaction.ts +74 -532
- package/extensions/renderer/mouse-layout.ts +160 -0
- package/extensions/renderer/mouse-packets.ts +143 -0
- package/extensions/renderer/mouse-scroll.ts +184 -0
- package/extensions/renderer/tool-diff/diff-renderer.ts +59 -2
- package/extensions/renderer/tool-grouping.ts +9 -2
- package/extensions/renderer/tool-result.ts +24 -8
- package/package.json +1 -1
- package/themes/cc-dark.json +1 -0
- package/themes/cc-light.json +2 -2
|
@@ -13,6 +13,13 @@ import {
|
|
|
13
13
|
} from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { getKeybindings } from "@earendil-works/pi-tui";
|
|
15
15
|
import { Markdown, Spacer, Text, type Component } from "@earendil-works/pi-tui";
|
|
16
|
+
import {
|
|
17
|
+
animateCompactThinkingText,
|
|
18
|
+
formatThoughtDuration,
|
|
19
|
+
styleCompactThinkingText,
|
|
20
|
+
} from "../renderer/compact-mode.ts";
|
|
21
|
+
// 保持导出兼容:渲染函数已并入 renderer/compact-mode.ts,这里 re-export。
|
|
22
|
+
export { animateCompactThinkingText, formatThoughtDuration, styleCompactThinkingText };
|
|
16
23
|
|
|
17
24
|
// pi-tui 类型声明中 TUI 的 re-export 解析失败,本地用最小结构化类型(只用到 requestRender)。
|
|
18
25
|
type RenderTui = { requestRender(force?: boolean): void };
|
|
@@ -28,6 +35,18 @@ export type CompactThinkingConfig = {
|
|
|
28
35
|
|
|
29
36
|
export type CompactThinkingController = {
|
|
30
37
|
updateConfig(next: CompactThinkingConfig): void;
|
|
38
|
+
/**
|
|
39
|
+
* 只读查询:某条 assistant message 的可见思考总时长(ms)。
|
|
40
|
+
* 供 compact 渲染层生成 `Thought for 8s` 摘要,不建立第二套计时器。
|
|
41
|
+
* 无记录且无进行中思考时返回 undefined。
|
|
42
|
+
*/
|
|
43
|
+
getMessageThinkingDurationMs?(messageTimestamp: number): number | undefined;
|
|
44
|
+
/** 当前消息是否仍处于 compact-thinking 的活动思考态。 */
|
|
45
|
+
isMessageThinkingActive?(messageTimestamp: number): boolean;
|
|
46
|
+
/** 复用 compact-thinking 配置的动画帧,不建立独立动画计时器。 */
|
|
47
|
+
getThinkingAnimationFrame?(): number;
|
|
48
|
+
/** compact 摘要保持 loading 时复用 compact-thinking 的动画循环。 */
|
|
49
|
+
setCompactSummaryActive?(active: boolean): void;
|
|
31
50
|
};
|
|
32
51
|
|
|
33
52
|
type DurationEntryData = {
|
|
@@ -86,6 +105,16 @@ const config: CompactThinkingConfig = {
|
|
|
86
105
|
};
|
|
87
106
|
|
|
88
107
|
const DURATION_ENTRY_TYPE = "compact-thinking-duration";
|
|
108
|
+
const COMPACT_THINKING_PATCH_KEY = Symbol.for("pi.ccstyle.compact-thinking-update");
|
|
109
|
+
const PROTOTYPE_ORIGINAL_KEY = Symbol.for("pi.ccstyle.prototype-original");
|
|
110
|
+
|
|
111
|
+
/** 当前激活 session 的只读查询委托(compact 渲染层使用)。 */
|
|
112
|
+
type ThinkingDurationQuery = (messageTimestamp: number) => number | undefined;
|
|
113
|
+
type ThinkingActiveQuery = (messageTimestamp: number) => boolean;
|
|
114
|
+
let activeThinkingQuery: ThinkingDurationQuery | undefined;
|
|
115
|
+
let activeThinkingStateQuery: ThinkingActiveQuery | undefined;
|
|
116
|
+
let activeThinkingAnimationFrameQuery: (() => number) | undefined;
|
|
117
|
+
let activeCompactSummarySetter: ((active: boolean) => void) | undefined;
|
|
89
118
|
|
|
90
119
|
function restoreDurationEntries(
|
|
91
120
|
entries: Array<{ type: string; customType?: string; data?: unknown }>,
|
|
@@ -124,18 +153,6 @@ function restoreDurationEntries(
|
|
|
124
153
|
}
|
|
125
154
|
}
|
|
126
155
|
|
|
127
|
-
function formatThoughtDuration(durationMs: number) {
|
|
128
|
-
if (durationMs < 1_000) {
|
|
129
|
-
return `${Math.max(1, Math.round(durationMs))}ms`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const totalSeconds = Math.max(1, Math.round(durationMs / 1_000));
|
|
133
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
134
|
-
const seconds = totalSeconds % 60;
|
|
135
|
-
if (minutes === 0) return `${seconds}s`;
|
|
136
|
-
return seconds === 0 ? `${minutes}m` : `${minutes}m ${seconds}s`;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
156
|
function getThinkingToggleHint() {
|
|
140
157
|
const keys = getKeybindings().getKeys("app.thinking.toggle");
|
|
141
158
|
return keys.length > 0 ? `${keys.join("/")} to expand` : undefined;
|
|
@@ -253,6 +270,21 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
253
270
|
const prototype = AssistantMessageComponent.prototype as PatchedPrototype;
|
|
254
271
|
const originalUpdateContent = prototype.updateContent;
|
|
255
272
|
|
|
273
|
+
activeThinkingStateQuery = (messageTimestamp) =>
|
|
274
|
+
activeThinking?.messageTimestamp === messageTimestamp;
|
|
275
|
+
activeThinkingAnimationFrameQuery = () => animationFrame;
|
|
276
|
+
activeThinkingQuery = (messageTimestamp) => {
|
|
277
|
+
let total = 0;
|
|
278
|
+
const durations = completedDurations.get(messageTimestamp);
|
|
279
|
+
if (durations) {
|
|
280
|
+
for (const duration of durations.values()) total += duration;
|
|
281
|
+
}
|
|
282
|
+
if (activeThinking?.messageTimestamp === messageTimestamp) {
|
|
283
|
+
total += Math.max(1, Date.now() - activeThinking.startedAt);
|
|
284
|
+
}
|
|
285
|
+
return total > 0 ? total : undefined;
|
|
286
|
+
};
|
|
287
|
+
|
|
256
288
|
const completedDurations = new Map<number, Map<number, number>>();
|
|
257
289
|
const renderedComponents = new Set<AssistantMessageComponentLike>();
|
|
258
290
|
const streamingComponents = new Set<AssistantMessageComponentLike>();
|
|
@@ -263,6 +295,7 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
263
295
|
let latestComponentTimestamp: number | undefined;
|
|
264
296
|
let animationTimer: ReturnType<typeof setInterval> | undefined;
|
|
265
297
|
let animationFrame = 0;
|
|
298
|
+
let compactSummaryActive = false;
|
|
266
299
|
let patchInstalled = true;
|
|
267
300
|
|
|
268
301
|
function refreshRenderedComponents() {
|
|
@@ -274,35 +307,20 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
274
307
|
}
|
|
275
308
|
|
|
276
309
|
function thinkingStyle(text: string) {
|
|
277
|
-
return
|
|
310
|
+
return styleCompactThinkingText(text, activeTheme);
|
|
278
311
|
}
|
|
279
312
|
|
|
280
313
|
function summaryTitleStyle(text: string) {
|
|
281
|
-
return activeTheme
|
|
282
|
-
? activeTheme.italic(activeTheme.bold(activeTheme.fg("thinkingText", text)))
|
|
283
|
-
: text;
|
|
314
|
+
return styleCompactThinkingText(text, activeTheme, true);
|
|
284
315
|
}
|
|
285
316
|
|
|
286
317
|
function animatedText(text: string, baseStyle: (value: string) => string, animate: boolean) {
|
|
287
|
-
if (!animate
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const end = start + highlightWidth;
|
|
294
|
-
const before = characters.slice(0, Math.max(0, start)).join("");
|
|
295
|
-
const highlighted = characters
|
|
296
|
-
.slice(Math.max(0, start), Math.min(characters.length, end))
|
|
297
|
-
.join("");
|
|
298
|
-
const after = characters.slice(Math.max(0, end)).join("");
|
|
299
|
-
|
|
300
|
-
return (
|
|
301
|
-
baseStyle(before) +
|
|
302
|
-
(highlighted
|
|
303
|
-
? activeTheme.italic(activeTheme.bold(activeTheme.fg("text", highlighted)))
|
|
304
|
-
: "") +
|
|
305
|
-
baseStyle(after)
|
|
318
|
+
if (!animate) return baseStyle(text);
|
|
319
|
+
return animateCompactThinkingText(
|
|
320
|
+
text,
|
|
321
|
+
activeTheme,
|
|
322
|
+
animationFrame,
|
|
323
|
+
baseStyle === summaryTitleStyle,
|
|
306
324
|
);
|
|
307
325
|
}
|
|
308
326
|
|
|
@@ -506,6 +524,33 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
506
524
|
}
|
|
507
525
|
}
|
|
508
526
|
};
|
|
527
|
+
const installedUpdateContent = prototype.updateContent;
|
|
528
|
+
(installedUpdateContent as any)[COMPACT_THINKING_PATCH_KEY] = true;
|
|
529
|
+
|
|
530
|
+
function ensureAnimationTimer() {
|
|
531
|
+
if (animationTimer) return;
|
|
532
|
+
animationTimer = setInterval(() => {
|
|
533
|
+
animationFrame++;
|
|
534
|
+
for (const component of streamingComponents) {
|
|
535
|
+
const self = component as unknown as AssistantInternals;
|
|
536
|
+
if (self.lastMessage) self.updateContent(self.lastMessage);
|
|
537
|
+
}
|
|
538
|
+
activeTui?.requestRender();
|
|
539
|
+
}, config.animationIntervalMs);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function stopAnimationTimerIfIdle() {
|
|
543
|
+
if (activeThinking || compactSummaryActive || !animationTimer) return;
|
|
544
|
+
clearInterval(animationTimer);
|
|
545
|
+
animationTimer = undefined;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
activeCompactSummarySetter = (active) => {
|
|
549
|
+
compactSummaryActive = active;
|
|
550
|
+
if (active) ensureAnimationTimer();
|
|
551
|
+
else stopAnimationTimerIfIdle();
|
|
552
|
+
activeTui?.requestRender();
|
|
553
|
+
};
|
|
509
554
|
|
|
510
555
|
function startThinking(message: AssistantMessage, contentIndex: number) {
|
|
511
556
|
activeThinking = {
|
|
@@ -528,14 +573,8 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
528
573
|
}
|
|
529
574
|
|
|
530
575
|
if (animationTimer) clearInterval(animationTimer);
|
|
531
|
-
animationTimer =
|
|
532
|
-
|
|
533
|
-
for (const component of streamingComponents) {
|
|
534
|
-
const self = component as unknown as AssistantInternals;
|
|
535
|
-
if (self.lastMessage) self.updateContent(self.lastMessage);
|
|
536
|
-
}
|
|
537
|
-
activeTui?.requestRender();
|
|
538
|
-
}, config.animationIntervalMs);
|
|
576
|
+
animationTimer = undefined;
|
|
577
|
+
ensureAnimationTimer();
|
|
539
578
|
}
|
|
540
579
|
|
|
541
580
|
function finishThinking() {
|
|
@@ -555,8 +594,7 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
555
594
|
} as DurationEntryData);
|
|
556
595
|
|
|
557
596
|
activeThinking = undefined;
|
|
558
|
-
|
|
559
|
-
animationTimer = undefined;
|
|
597
|
+
stopAnimationTimerIfIdle();
|
|
560
598
|
|
|
561
599
|
const components = [...streamingComponents];
|
|
562
600
|
streamingComponents.clear();
|
|
@@ -589,7 +627,7 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
589
627
|
content?.type === "toolCall" &&
|
|
590
628
|
(content.name === "Agent" ||
|
|
591
629
|
content.name === "Agents" ||
|
|
592
|
-
content.
|
|
630
|
+
content.arguments?.subagent_type != null),
|
|
593
631
|
)
|
|
594
632
|
);
|
|
595
633
|
}
|
|
@@ -633,7 +671,9 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
633
671
|
// OpenAI-compatible providers may not close reasoning until the response ends.
|
|
634
672
|
pi.on("tool_execution_start", (event: any) => {
|
|
635
673
|
if (event.toolName === "Agent" || event.toolName === "Agents") {
|
|
636
|
-
|
|
674
|
+
const lastMessage = (latestComponent as unknown as { lastMessage?: AssistantMessage })
|
|
675
|
+
?.lastMessage;
|
|
676
|
+
resumeAgentThinking(lastMessage);
|
|
637
677
|
} else {
|
|
638
678
|
finishThinking();
|
|
639
679
|
}
|
|
@@ -674,6 +714,11 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
674
714
|
|
|
675
715
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
676
716
|
finishThinking();
|
|
717
|
+
activeThinkingQuery = undefined;
|
|
718
|
+
activeThinkingStateQuery = undefined;
|
|
719
|
+
activeThinkingAnimationFrameQuery = undefined;
|
|
720
|
+
activeCompactSummarySetter = undefined;
|
|
721
|
+
compactSummaryActive = false;
|
|
677
722
|
if (animationTimer) clearInterval(animationTimer);
|
|
678
723
|
animationTimer = undefined;
|
|
679
724
|
activeTui = undefined;
|
|
@@ -686,7 +731,11 @@ function compactThinking(pi: ExtensionAPI) {
|
|
|
686
731
|
if (ctx.mode === "tui") ctx.ui.setWidget(WIDGET_ID, undefined);
|
|
687
732
|
|
|
688
733
|
if (patchInstalled) {
|
|
689
|
-
prototype.updateContent
|
|
734
|
+
if (prototype.updateContent === installedUpdateContent) {
|
|
735
|
+
const wrappedOriginal = (originalUpdateContent as any)[PROTOTYPE_ORIGINAL_KEY];
|
|
736
|
+
prototype.updateContent =
|
|
737
|
+
typeof wrappedOriginal === "function" ? wrappedOriginal : originalUpdateContent;
|
|
738
|
+
}
|
|
690
739
|
patchInstalled = false;
|
|
691
740
|
}
|
|
692
741
|
});
|
|
@@ -795,5 +844,17 @@ export function installCompactThinking(
|
|
|
795
844
|
Object.assign(initialConfig, next);
|
|
796
845
|
Object.assign(config, next);
|
|
797
846
|
},
|
|
847
|
+
getMessageThinkingDurationMs(messageTimestamp) {
|
|
848
|
+
return activeThinkingQuery?.(messageTimestamp);
|
|
849
|
+
},
|
|
850
|
+
isMessageThinkingActive(messageTimestamp) {
|
|
851
|
+
return activeThinkingStateQuery?.(messageTimestamp) ?? false;
|
|
852
|
+
},
|
|
853
|
+
getThinkingAnimationFrame() {
|
|
854
|
+
return activeThinkingAnimationFrameQuery?.() ?? 0;
|
|
855
|
+
},
|
|
856
|
+
setCompactSummaryActive(active) {
|
|
857
|
+
activeCompactSummarySetter?.(active);
|
|
858
|
+
},
|
|
798
859
|
};
|
|
799
860
|
}
|
|
@@ -639,7 +639,7 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
|
|
|
639
639
|
);
|
|
640
640
|
|
|
641
641
|
if (!action) break;
|
|
642
|
-
const preview = previewByKey.get(action);
|
|
642
|
+
const preview = previewByKey.get(action as PreviewKey);
|
|
643
643
|
if (!preview) continue;
|
|
644
644
|
|
|
645
645
|
await showTextPreview(ctx, preview.title, preview.content);
|
|
@@ -2,7 +2,7 @@ export const SESSION_REFERENCE_CUSTOM_TYPE = "session-reference";
|
|
|
2
2
|
export const SESSION_REFERENCE_PREFIX = "@session:";
|
|
3
3
|
|
|
4
4
|
const SESSION_REFERENCE_PATTERN =
|
|
5
|
-
/(?:^|\s)@session:([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?![
|
|
5
|
+
/(?:^|\s)@session:(?:\[([^\]]+)\]|([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?))(?![\w.\]-])/g;
|
|
6
6
|
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
|
|
7
7
|
|
|
8
8
|
export interface ReferenceSessionInfo {
|
|
@@ -31,12 +31,13 @@ export const DEFAULT_REFERENCE_LIMITS: ReferenceLimits = {
|
|
|
31
31
|
maxTotalBytes: 48_000,
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/** 提取 @session:[name](组 1)或旧格式 @session:id(组 2),返回 name 或 id。 */
|
|
34
35
|
export function extractSessionReferenceIds(text: string): string[] {
|
|
35
36
|
const ids: string[] = [];
|
|
36
37
|
const seen = new Set<string>();
|
|
37
38
|
|
|
38
39
|
for (const match of text.matchAll(SESSION_REFERENCE_PATTERN)) {
|
|
39
|
-
const id = match[1];
|
|
40
|
+
const id = (match[1] ?? match[2])?.trim();
|
|
40
41
|
if (id && !seen.has(id)) {
|
|
41
42
|
seen.add(id);
|
|
42
43
|
ids.push(id);
|
|
@@ -154,39 +154,47 @@ function formatDate(date: Date): string {
|
|
|
154
154
|
|
|
155
155
|
/** Stable across keystrokes while getReferences returns the same reference objects. */
|
|
156
156
|
const sessionSearchTextCache = new WeakMap<SessionReference, string>();
|
|
157
|
-
const sessionItemCache = new WeakMap<
|
|
157
|
+
const sessionItemCache = new WeakMap<
|
|
158
|
+
SessionReference,
|
|
159
|
+
{ cwd: string; named?: AutocompleteItem; stable?: AutocompleteItem }
|
|
160
|
+
>();
|
|
158
161
|
|
|
159
162
|
function sessionSearchText(reference: SessionReference): string {
|
|
160
163
|
const cached = sessionSearchTextCache.get(reference);
|
|
161
164
|
if (cached !== undefined) return cached;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
reference.info.firstMessage,
|
|
165
|
-
reference.info.cwd,
|
|
166
|
-
reference.info.id,
|
|
167
|
-
...reference.referenceIds,
|
|
168
|
-
]
|
|
169
|
-
.filter(Boolean)
|
|
170
|
-
.join(" ");
|
|
165
|
+
// 会话只按显式 session name 匹配,避免 firstMessage、路径或 ID 产生噪声。
|
|
166
|
+
const text = reference.info.name?.trim() ?? "";
|
|
171
167
|
sessionSearchTextCache.set(reference, text);
|
|
172
168
|
return text;
|
|
173
169
|
}
|
|
174
170
|
|
|
175
|
-
function sessionItem(
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
function sessionItem(
|
|
172
|
+
reference: SessionReference,
|
|
173
|
+
currentCwd: string,
|
|
174
|
+
useStableId: boolean,
|
|
175
|
+
): AutocompleteItem {
|
|
176
|
+
let cached = sessionItemCache.get(reference);
|
|
177
|
+
const variant = useStableId ? "stable" : "named";
|
|
178
|
+
if (cached?.cwd === currentCwd && cached[variant]) return cached[variant];
|
|
178
179
|
|
|
179
180
|
const session = reference.info;
|
|
180
181
|
const workspace = samePath(session.cwd, currentCwd)
|
|
181
182
|
? "current workspace"
|
|
182
183
|
: session.cwd || "unknown workspace";
|
|
183
184
|
const label = reference.kind === "subagent" ? "[SubAgent]" : "[Session]";
|
|
185
|
+
// 唯一名称保持短格式;同名或无名称时使用稳定 ID,避免引用歧义。
|
|
186
|
+
const sessionName = session.name?.trim();
|
|
187
|
+
const referenceId = useStableId
|
|
188
|
+
? reference.referenceIds[0]
|
|
189
|
+
: (sessionName ?? reference.referenceIds[0]);
|
|
184
190
|
const item: AutocompleteItem = {
|
|
185
|
-
value: `${SESSION_REFERENCE_PREFIX}${
|
|
191
|
+
value: `${SESSION_REFERENCE_PREFIX}[${referenceId}]`,
|
|
186
192
|
label: `${label} ${sessionTitle(session)}`,
|
|
187
193
|
description: `${workspace} · ${session.messageCount} messages · ${formatDate(session.modified)}`,
|
|
188
194
|
};
|
|
189
|
-
|
|
195
|
+
if (cached?.cwd !== currentCwd) cached = { cwd: currentCwd };
|
|
196
|
+
cached[variant] = item;
|
|
197
|
+
sessionItemCache.set(reference, cached);
|
|
190
198
|
return item;
|
|
191
199
|
}
|
|
192
200
|
|
|
@@ -222,14 +230,24 @@ function filterSessions(
|
|
|
222
230
|
if (query.startsWith("session:")) return [];
|
|
223
231
|
|
|
224
232
|
const ordered = orderSessionReferences(references, currentCwd);
|
|
233
|
+
const nameCounts = new Map<string, number>();
|
|
234
|
+
for (const reference of ordered) {
|
|
235
|
+
const name = reference.info.name?.trim();
|
|
236
|
+
if (name) nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
|
|
237
|
+
}
|
|
225
238
|
const trimmed = query.trim();
|
|
226
|
-
//
|
|
239
|
+
// 模糊匹配只接受有 session name 的会话;subagent 按 agent name 匹配。
|
|
240
|
+
const searchable = ordered.filter(
|
|
241
|
+
(reference) => reference.kind === "subagent" || Boolean(reference.info.name?.trim()),
|
|
242
|
+
);
|
|
243
|
+
// Empty query: top-N already sorted — no fuzzy scan over the full list。
|
|
227
244
|
const matches = trimmed
|
|
228
|
-
? fuzzyFilter(
|
|
245
|
+
? fuzzyFilter(searchable, trimmed, sessionSearchText)
|
|
229
246
|
: ordered.slice(0, MAX_SESSION_SUGGESTIONS);
|
|
230
|
-
return matches
|
|
231
|
-
.
|
|
232
|
-
|
|
247
|
+
return matches.slice(0, MAX_SESSION_SUGGESTIONS).map((reference) => {
|
|
248
|
+
const name = reference.info.name?.trim();
|
|
249
|
+
return sessionItem(reference, currentCwd, Boolean(name && (nameCounts.get(name) ?? 0) > 1));
|
|
250
|
+
});
|
|
233
251
|
}
|
|
234
252
|
|
|
235
253
|
function isPathLikeQuery(query: string): boolean {
|
|
@@ -470,12 +488,22 @@ export default function sessionReferenceExtension(pi: ExtensionAPI): void {
|
|
|
470
488
|
),
|
|
471
489
|
));
|
|
472
490
|
const referencesById = new Map<string, SessionReference>();
|
|
491
|
+
const referencesByName = new Map<string, SessionReference>();
|
|
492
|
+
const ambiguousNames = new Set<string>();
|
|
473
493
|
for (const reference of references) {
|
|
474
494
|
for (const id of reference.referenceIds) referencesById.set(id, reference);
|
|
495
|
+
const name = reference.info.name?.trim();
|
|
496
|
+
if (!name || ambiguousNames.has(name)) continue;
|
|
497
|
+
if (referencesByName.has(name)) {
|
|
498
|
+
referencesByName.delete(name);
|
|
499
|
+
ambiguousNames.add(name);
|
|
500
|
+
} else {
|
|
501
|
+
referencesByName.set(name, reference);
|
|
502
|
+
}
|
|
475
503
|
}
|
|
476
504
|
const seenReferences = new Set<SessionReference>();
|
|
477
505
|
const matchingReferences = referenceIds
|
|
478
|
-
.map((id) => referencesById.get(id))
|
|
506
|
+
.map((id) => referencesById.get(id) ?? referencesByName.get(id))
|
|
479
507
|
.filter((reference): reference is SessionReference => {
|
|
480
508
|
if (!reference || reference.info.id === currentSessionId || seenReferences.has(reference))
|
|
481
509
|
return false;
|
package/extensions/index.ts
CHANGED
|
@@ -22,6 +22,18 @@ export default function (pi: ExtensionAPI): void {
|
|
|
22
22
|
updateConfig(next) {
|
|
23
23
|
compactThinking?.updateConfig(next);
|
|
24
24
|
},
|
|
25
|
+
getMessageThinkingDurationMs(messageTimestamp) {
|
|
26
|
+
return compactThinking?.getMessageThinkingDurationMs?.(messageTimestamp);
|
|
27
|
+
},
|
|
28
|
+
isMessageThinkingActive(messageTimestamp) {
|
|
29
|
+
return compactThinking?.isMessageThinkingActive?.(messageTimestamp) ?? false;
|
|
30
|
+
},
|
|
31
|
+
getThinkingAnimationFrame() {
|
|
32
|
+
return compactThinking?.getThinkingAnimationFrame?.() ?? 0;
|
|
33
|
+
},
|
|
34
|
+
setCompactSummaryActive(active) {
|
|
35
|
+
compactThinking?.setCompactSummaryActive?.(active);
|
|
36
|
+
},
|
|
25
37
|
};
|
|
26
38
|
claudeCodeStyle(pi, undefined, compactThinkingBridge);
|
|
27
39
|
compactThinking = installCompactThinking(pi, getCompactThinkingConfig());
|