pi-session-peek 0.6.0 → 0.7.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 +5 -0
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/package.json +1 -1
- package/src/i18n.ts +6 -2
- package/src/peek-component.ts +12 -4
- package/src/preview.ts +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
- `Ctrl+E` cycles the preview between all messages, only your messages and only the AI's replies. The header of the preview shows the active filter and the message count follows it; hits on the hidden side are not counted or jumped to. The session list is unaffected.
|
|
6
|
+
`Ctrl+E` 在预览里循环切换 全部对话 → 只看我发的 → 只看 AI 回答。预览首行显示当前过滤状态,消息数跟着变;被隐藏一侧的命中不计数、不跳转。左栏列表不受影响。
|
|
7
|
+
|
|
3
8
|
## 0.6.0
|
|
4
9
|
|
|
5
10
|
- Search: plain terms match the working directory again, but only in the all-projects scope. The current-directory scope keeps the 0.5.2 behaviour (conversation and session name only), since every session there shares the same path prefix.
|
package/README.md
CHANGED
|
@@ -68,6 +68,7 @@ In TUI mode, run `/peek` and start typing. Press `Enter` on a session to continu
|
|
|
68
68
|
| `Shift+↑` `Shift+↓` | Scroll preview by three lines |
|
|
69
69
|
| `Ctrl+N` `Ctrl+P` | Next / previous hit line outside the current view; the top-right corner shows `hit k/n` |
|
|
70
70
|
| `Ctrl+T` | Show / hide tool calls in the preview. Each call is one line with the tool name and its main argument (command, path, URL, query). Results are never shown. |
|
|
71
|
+
| `Ctrl+E` | Cycle the preview through all messages → only yours → only the AI's. The message count follows the filter. Purely a view filter: the session list is unaffected, and hits on the hidden side are not counted or jumped to. |
|
|
71
72
|
| `Enter` | Resume the session |
|
|
72
73
|
| `Ctrl+O` | Fork the session and open the fork |
|
|
73
74
|
| `Ctrl+R` | Rename. Writes the same `session_info` entry as `/name`. |
|
package/README.zh-CN.md
CHANGED
|
@@ -68,6 +68,7 @@ pi 扩展以你的用户权限运行,只装信得过的包。
|
|
|
68
68
|
| `Shift+↑` `Shift+↓` | 预览滚三行 |
|
|
69
69
|
| `Ctrl+N` `Ctrl+P` | 跳到当前屏幕外的下一处 / 上一处命中行,右上角显示「命中 k/n」 |
|
|
70
70
|
| `Ctrl+T` | 预览里显示 / 隐藏工具调用。每次调用一行:工具名和主参数(命令、路径、URL、查询词)。不显示结果。 |
|
|
71
|
+
| `Ctrl+E` | 预览在 全部对话 → 只看我发的 → 只看 AI 回答 之间循环,消息数跟着过滤变。只是显示过滤:左栏列表不受影响,被隐藏一侧的命中不计数、不跳转。 |
|
|
71
72
|
| `Enter` | 进入会话 |
|
|
72
73
|
| `Ctrl+O` | 分叉出新会话并进入 |
|
|
73
74
|
| `Ctrl+R` | 重命名,写入的记录和 `/name` 一样 |
|
package/package.json
CHANGED
package/src/i18n.ts
CHANGED
|
@@ -28,11 +28,13 @@ const zh = {
|
|
|
28
28
|
truncated: "(会话过长,仅显示最后 {n} 条)",
|
|
29
29
|
hitInTruncated: "关键词命中在未显示的更早消息中",
|
|
30
30
|
hitOnlyMeta: "关键词仅命中会话名,对话正文无匹配",
|
|
31
|
+
viewUser: " • 只看我发的[^E]",
|
|
32
|
+
viewAi: " • 只看 AI[^E]",
|
|
31
33
|
you: "👤 你",
|
|
32
34
|
ai: "🤖 AI",
|
|
33
35
|
renamePrefix: "✏ 重命名: ",
|
|
34
36
|
confirmDelete: "⚠ 删除会话 [{desc}]?按 y / Enter 确认,任意其他键取消",
|
|
35
|
-
footer: "↑↓ 选择 PgUp/Dn·^U/^Y·⇧↑↓ 滚动 ^N/^P 命中 ^T 工具 Tab 范围 ^D 删除 ^R 改名 ^O 分叉 Enter 进入 Esc/^C 关闭 🖱 点选·双击进入·拖选·滚轮",
|
|
37
|
+
footer: "↑↓ 选择 PgUp/Dn·^U/^Y·⇧↑↓ 滚动 ^N/^P 命中 ^T 工具 ^E 角色 Tab 范围 ^D 删除 ^R 改名 ^O 分叉 Enter 进入 Esc/^C 关闭 🖱 点选·双击进入·拖选·滚轮",
|
|
36
38
|
selHint: "▭ 已选 {n} 行 ^C 复制 点击别处取消",
|
|
37
39
|
copied: "✓ 已复制 {n} 行",
|
|
38
40
|
copyFailed: "✗ 复制失败",
|
|
@@ -71,11 +73,13 @@ const en: Record<keyof typeof zh, string> = {
|
|
|
71
73
|
truncated: "(long session, showing the last {n} messages)",
|
|
72
74
|
hitInTruncated: "Keyword hits are in earlier messages not shown here",
|
|
73
75
|
hitOnlyMeta: "Keyword matched only the session name, not the conversation",
|
|
76
|
+
viewUser: " • mine only[^E]",
|
|
77
|
+
viewAi: " • AI only[^E]",
|
|
74
78
|
you: "👤 You",
|
|
75
79
|
ai: "🤖 AI",
|
|
76
80
|
renamePrefix: "✏ Rename: ",
|
|
77
81
|
confirmDelete: "⚠ Delete session [{desc}]? y / Enter to confirm, any other key to cancel",
|
|
78
|
-
footer: "↑↓ select PgUp/Dn·^U/^Y·⇧↑↓ scroll ^N/^P hits ^T tools Tab scope ^D del ^R rename ^O fork Enter open Esc close 🖱 click·dbl-click·drag·wheel",
|
|
82
|
+
footer: "↑↓ select PgUp/Dn·^U/^Y·⇧↑↓ scroll ^N/^P hits ^T tools ^E role Tab scope ^D del ^R rename ^O fork Enter open Esc close 🖱 click·dbl-click·drag·wheel",
|
|
79
83
|
selHint: "▭ {n} lines selected ^C copy click elsewhere to clear",
|
|
80
84
|
copied: "✓ Copied {n} lines",
|
|
81
85
|
copyFailed: "✗ Copy failed",
|
package/src/peek-component.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { msg } from "./i18n.ts";
|
|
|
14
14
|
import { DragSelect } from "./drag-select.ts";
|
|
15
15
|
import { BODY_TOP, bodyRows, paneWidths } from "./layout.ts";
|
|
16
16
|
import { buildList, followSelection, listRows, visibleItems } from "./list.ts";
|
|
17
|
-
import { buildPreview, initialOffset, nextMatchTarget, type RenderCache } from "./preview.ts";
|
|
17
|
+
import { buildPreview, initialOffset, nextMatchTarget, type RenderCache, type RoleFilter } from "./preview.ts";
|
|
18
18
|
import { matchesSession, parseQuery, roleTerms, type ParsedQuery, type RoleTerms } from "./query.ts";
|
|
19
19
|
import { bounds, highlightColumns, rowColumns, selectionText, type Selection } from "./selection.ts";
|
|
20
20
|
import type { PeekSession } from "./sessions.ts";
|
|
@@ -41,6 +41,7 @@ export class PeekComponent implements Component, Focusable {
|
|
|
41
41
|
private previewOffset = 0;
|
|
42
42
|
private previewKey = ""; // 预览缓存的键(会话 + 关键词 + 宽度 + 工具开关),置空强制重建
|
|
43
43
|
private showTools = false; // Ctrl+T:预览里显示每次工具调用的一行摘要
|
|
44
|
+
private roleFilter: RoleFilter = "all"; // Ctrl+E:预览只看自己发的 / 只看 AI / 全部,循环切换
|
|
44
45
|
private previewLines: string[] = [];
|
|
45
46
|
private matchLines: number[] = []; // 预览里含关键词的行(升序),Ctrl+N / Ctrl+P 用
|
|
46
47
|
private rendered: RenderCache = new WeakMap(); // 每条消息渲染好的行,按宽度缓存
|
|
@@ -297,6 +298,13 @@ export class PeekComponent implements Component, Focusable {
|
|
|
297
298
|
this.invalidate();
|
|
298
299
|
return;
|
|
299
300
|
}
|
|
301
|
+
if (matchesKey(data, Key.ctrl("e"))) {
|
|
302
|
+
this.roleFilter = this.roleFilter === "all" ? "user" : this.roleFilter === "user" ? "assistant" : "all";
|
|
303
|
+
this.previewKey = "";
|
|
304
|
+
this.drag.clear();
|
|
305
|
+
this.invalidate();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
300
308
|
if (matchesKey(data, Key.ctrl("n"))) {
|
|
301
309
|
this.jumpMatch(1);
|
|
302
310
|
return;
|
|
@@ -485,13 +493,13 @@ export class PeekComponent implements Component, Focusable {
|
|
|
485
493
|
return { rt, hasBody: rt.user.length > 0 || rt.assistant.length > 0 };
|
|
486
494
|
}
|
|
487
495
|
|
|
488
|
-
// 右栏。预览键(会话 + 关键词 + 宽度 +
|
|
496
|
+
// 右栏。预览键(会话 + 关键词 + 宽度 + 工具开关 + 角色过滤)没变就复用;变了重建,滚动位置放到首个命中处或底部
|
|
489
497
|
private rebuildPreview(rw: number, rt: RoleTerms): void {
|
|
490
498
|
const s = this.filtered[this.selected];
|
|
491
|
-
const key = s ? `${s.path}|${this.getQuery()}|${rw}|${this.showTools ? "t" : ""}` : "none";
|
|
499
|
+
const key = s ? `${s.path}|${this.getQuery()}|${rw}|${this.showTools ? "t" : ""}|${this.roleFilter}` : "none";
|
|
492
500
|
if (key === this.previewKey) return;
|
|
493
501
|
this.previewKey = key;
|
|
494
|
-
const p = buildPreview(s, rt, rw, this.showTools, this.theme, this.mdTheme, this.rendered);
|
|
502
|
+
const p = buildPreview(s, rt, rw, this.showTools, this.theme, this.mdTheme, this.rendered, this.roleFilter);
|
|
495
503
|
this.previewLines = p.lines;
|
|
496
504
|
this.matchLines = p.matchLines;
|
|
497
505
|
this.previewOffset = initialOffset(p, this.bodyHeight());
|
package/src/preview.ts
CHANGED
|
@@ -38,6 +38,9 @@ export interface Preview {
|
|
|
38
38
|
matchLines: number[]; // 含关键词的行(升序),Ctrl+N / Ctrl+P 用
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// 预览只画哪个角色的消息:全部 / 只看我发的 / 只看 AI。只影响预览,不影响左栏过滤(搜索语法的 user: / ai: 管那个)
|
|
42
|
+
export type RoleFilter = "all" | "user" | "assistant";
|
|
43
|
+
|
|
41
44
|
// 右栏内容。没有会话时只有一行提示
|
|
42
45
|
export function buildPreview(
|
|
43
46
|
s: PeekSession | undefined,
|
|
@@ -47,16 +50,18 @@ export function buildPreview(
|
|
|
47
50
|
t: PeekTheme,
|
|
48
51
|
mdTheme: MarkdownTheme,
|
|
49
52
|
cache: RenderCache,
|
|
53
|
+
role: RoleFilter = "all",
|
|
50
54
|
): Preview {
|
|
51
55
|
if (!s) return { lines: [t.fg("dim", msg("noMatch"))], matchLines: [] };
|
|
52
56
|
|
|
53
57
|
const lines: string[] = [];
|
|
54
58
|
const matchLines: number[] = [];
|
|
55
|
-
//
|
|
56
|
-
const textCount = s.msgs.reduce((n, m) => n + (m.text ? 1 : 0), 0);
|
|
59
|
+
// 消息数只算有文字的,纯工具轮次不计;角色过滤时只算画出来的那一侧
|
|
60
|
+
const textCount = s.msgs.reduce((n, m) => n + (m.text && (role === "all" || m.role === role) ? 1 : 0), 0);
|
|
57
61
|
lines.push(
|
|
58
62
|
truncateToWidth(
|
|
59
|
-
t.fg("dim", `${s.cwd} • ${msg("msgCount", { n: textCount })}${s.name ? " • " + s.name : ""}`)
|
|
63
|
+
t.fg("dim", `${s.cwd} • ${msg("msgCount", { n: textCount })}${s.name ? " • " + s.name : ""}`) +
|
|
64
|
+
(role !== "all" ? t.fg("warning", msg(role === "user" ? "viewUser" : "viewAi")) : ""),
|
|
60
65
|
rw,
|
|
61
66
|
),
|
|
62
67
|
);
|
|
@@ -88,6 +93,7 @@ export function buildPreview(
|
|
|
88
93
|
|
|
89
94
|
let prevRole: PeekMsg["role"] | undefined; // 上一条画出来的消息的角色
|
|
90
95
|
for (const m of msgs) {
|
|
96
|
+
if (role !== "all" && m.role !== role) continue; // Ctrl+E 角色过滤:另一侧的消息整条不画
|
|
91
97
|
const tools = showTools ? (m.tools ?? []) : [];
|
|
92
98
|
if (!m.text && !tools.length) continue; // 工具隐藏时,只有工具调用的 AI 轮次整条跳过
|
|
93
99
|
const terms = rt[m.role]; // user: 的词只在用户消息里高亮,ai: 的只在 AI 回复里
|