pi-session-peek 0.5.2 → 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 +14 -0
- package/README.md +7 -2
- package/README.zh-CN.md +7 -2
- package/package.json +1 -1
- package/src/i18n.ts +6 -2
- package/src/peek-component.ts +17 -6
- package/src/preview.ts +9 -3
- package/src/query.ts +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
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
|
+
|
|
8
|
+
## 0.6.0
|
|
9
|
+
|
|
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.
|
|
11
|
+
搜索:普通词重新支持匹配工作目录,但仅在「全部项目」范围下生效;当前目录树范围保持 0.5.2 的行为(只匹配对话和会话名),因为该范围下所有会话共享同一段路径前缀。
|
|
12
|
+
- `Ctrl+Y` replaces `Ctrl+F` for scrolling the preview down half a page. In pi's fullscreen mode `Ctrl+F` is captured by pi's own transcript search on Windows/WSL before the picker ever sees it.
|
|
13
|
+
预览向下翻半页由 `Ctrl+F` 改为 `Ctrl+Y`:pi 全屏模式下 `Ctrl+F` 会被 pi 自己的全文搜索(Windows/WSL)先行截获,选择器根本收不到。
|
|
14
|
+
- The picker now declares `capturesViewportInput`, so in pi's fullscreen mode `PgUp` / `PgDn` reach the picker instead of scrolling pi's transcript.
|
|
15
|
+
选择器现在声明了 `capturesViewportInput`,pi 全屏模式下 `PgUp` / `PgDn` 会送达选择器,不再被 pi 截去滚动主对话记录。
|
|
16
|
+
|
|
3
17
|
## 0.5.2
|
|
4
18
|
|
|
5
19
|
- Wheel over the list now scrolls the list like a scrollbar instead of moving the selection. The selection stays where it is and comes back into view on `↑` / `↓`, a click, a new filter or a delete.
|
package/README.md
CHANGED
|
@@ -64,10 +64,11 @@ In TUI mode, run `/peek` and start typing. Press `Enter` on a session to continu
|
|
|
64
64
|
| `Tab` | Current directory tree ↔ all projects |
|
|
65
65
|
| `↑` `↓` | Select session |
|
|
66
66
|
| `PgUp` `PgDn` | Scroll preview by a page |
|
|
67
|
-
| `Ctrl+U` `Ctrl+
|
|
67
|
+
| `Ctrl+U` `Ctrl+Y` | Scroll preview by half a page |
|
|
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`. |
|
|
@@ -107,7 +108,7 @@ The filter is case-insensitive and matches anywhere in the text. Whitespace sepa
|
|
|
107
108
|
|
|
108
109
|
Quotes are the escape hatch: `"-foo"`, `"a|b"`, `"name:x"` and `"/x/"` search for those characters literally. A regex that fails to compile is searched as plain text. Only positive terms are highlighted in the preview, and `name:` / `dir:` terms never touch the conversation, so `name:auth` alone shows the full conversation with no highlights.
|
|
109
110
|
|
|
110
|
-
The search index holds user and assistant messages and the session name.
|
|
111
|
+
The search index holds user and assistant messages and the session name. Plain words also match the working directory, but only in the all-projects scope: with the current-directory scope every session shares the same path prefix, so a plain word from the path would match all of them; use `dir:` / `cwd:` to match the path in that scope. The index does not hold tool call arguments or tool results, and the tool lines shown by `Ctrl+T` are never matched or highlighted.
|
|
111
112
|
|
|
112
113
|
## 🌐 Language
|
|
113
114
|
|
|
@@ -183,6 +184,10 @@ git push --follow-tags
|
|
|
183
184
|
|
|
184
185
|
`npm version` first checks that the working tree is clean and stops with `Git working directory not clean.` otherwise, so everything, including `CHANGELOG.md`, has to be committed before the bump. Untracked files are fine. It then runs the hooks declared in `package.json`: `preversion` checks that `CHANGELOG.md` has a non-empty `## Unreleased` section and runs `npm run check`, aborting the bump before anything is touched if either fails; `version` runs `scripts/release-changelog.mjs`, which renames `## Unreleased` to the new version and stages the file. npm then commits `package.json`, the lockfile and `CHANGELOG.md` as `chore: release X.Y.Z` (message set in `.npmrc`) and tags it `vX.Y.Z`.
|
|
185
186
|
|
|
187
|
+
## 🔗 Friend links
|
|
188
|
+
|
|
189
|
+
- [LinuxDo — sincere, friendly, united, professional](https://linux.do)
|
|
190
|
+
|
|
186
191
|
## 📄 License
|
|
187
192
|
|
|
188
193
|
MIT. See [`LICENSE`](./LICENSE).
|
package/README.zh-CN.md
CHANGED
|
@@ -64,10 +64,11 @@ pi 扩展以你的用户权限运行,只装信得过的包。
|
|
|
64
64
|
| `Tab` | 当前目录树 ↔ 全部项目 |
|
|
65
65
|
| `↑` `↓` | 选会话 |
|
|
66
66
|
| `PgUp` `PgDn` | 预览翻页 |
|
|
67
|
-
| `Ctrl+U` `Ctrl+
|
|
67
|
+
| `Ctrl+U` `Ctrl+Y` | 预览翻半页 |
|
|
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` 一样 |
|
|
@@ -107,7 +108,7 @@ pi 扩展以你的用户权限运行,只装信得过的包。
|
|
|
107
108
|
|
|
108
109
|
引号是万能转义:`"-foo"`、`"a|b"`、`"name:x"`、`"/x/"` 都按字面搜这些字符。正则写错了就当普通文字搜。预览里只高亮正向的词,`name:` / `dir:` 的词不碰对话正文,所以只输入 `name:auth` 时右栏显示完整对话、没有高亮。
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
索引里有用户和助手的消息和会话名。普通词在「全部项目」范围下也匹配工作目录;当前目录树范围下所有会话的路径都带同一段前缀,普通词一碰到路径就会命中全部会话,所以这个范围下不匹配路径,要搜路径用 `dir:` / `cwd:`。没有工具调用的参数和结果,`Ctrl+T` 显示的工具行也不参与匹配和高亮。
|
|
111
112
|
|
|
112
113
|
## 🌐 界面语言
|
|
113
114
|
|
|
@@ -183,6 +184,10 @@ git push --follow-tags
|
|
|
183
184
|
|
|
184
185
|
`npm version` 一上来先检查 git 工作区是否干净,不干净就报 `Git working directory not clean.` 退出,所以 `CHANGELOG.md` 在内的所有改动都要先提交(未跟踪的文件不影响)。然后才跑 `package.json` 里声明的钩子:`preversion` 先确认 `CHANGELOG.md` 里有非空的 `## Unreleased` 段落,再跑 `npm run check`,任一不通过就在改动任何文件之前中止;`version` 跑 `scripts/release-changelog.mjs`,把 `## Unreleased` 改成新版本号并 `git add`。然后 npm 把 `package.json`、lockfile 和 `CHANGELOG.md` 一起提交成 `chore: release X.Y.Z`(提交信息定义在 `.npmrc`),并打上 `vX.Y.Z` 的 tag。
|
|
185
186
|
|
|
187
|
+
## 🔗 友情链接
|
|
188
|
+
|
|
189
|
+
- [LinuxDo — 真诚、友善、团结、专业](https://linux.do)
|
|
190
|
+
|
|
186
191
|
## 📄 许可证
|
|
187
192
|
|
|
188
193
|
MIT,见 [`LICENSE`](./LICENSE)。
|
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/^
|
|
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/^
|
|
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";
|
|
@@ -29,6 +29,9 @@ const WHEEL_ITEMS = 1; // 滚轮每格滚几个列表项(每项两行)。只
|
|
|
29
29
|
const HINT_MS = 1500; // 底部"已复制"提示停留时间
|
|
30
30
|
|
|
31
31
|
export class PeekComponent implements Component, Focusable {
|
|
32
|
+
// 声明了它,pi 全屏模式才把 PageUp/PageDown 等视口按键交给组件(shouldDeferViewportInput),
|
|
33
|
+
// 否则被 pi 截去滚主对话记录。Ctrl+F 例外:pi 的全文搜索绑定在 defer 检查之前,永远到不了组件
|
|
34
|
+
readonly capturesViewportInput = true;
|
|
32
35
|
private input: Input;
|
|
33
36
|
private scope: "current" | "all";
|
|
34
37
|
private filtered: PeekSession[] = [];
|
|
@@ -38,6 +41,7 @@ export class PeekComponent implements Component, Focusable {
|
|
|
38
41
|
private previewOffset = 0;
|
|
39
42
|
private previewKey = ""; // 预览缓存的键(会话 + 关键词 + 宽度 + 工具开关),置空强制重建
|
|
40
43
|
private showTools = false; // Ctrl+T:预览里显示每次工具调用的一行摘要
|
|
44
|
+
private roleFilter: RoleFilter = "all"; // Ctrl+E:预览只看自己发的 / 只看 AI / 全部,循环切换
|
|
41
45
|
private previewLines: string[] = [];
|
|
42
46
|
private matchLines: number[] = []; // 预览里含关键词的行(升序),Ctrl+N / Ctrl+P 用
|
|
43
47
|
private rendered: RenderCache = new WeakMap(); // 每条消息渲染好的行,按宽度缓存
|
|
@@ -122,7 +126,7 @@ export class PeekComponent implements Component, Focusable {
|
|
|
122
126
|
let out = this.all;
|
|
123
127
|
if (this.scope === "current") out = out.filter((s) => this.inCurrentTree(s));
|
|
124
128
|
if (since) out = out.filter((s) => s.mtime >= since);
|
|
125
|
-
if (terms.length) out = out.filter((s) => matchesSession(s, terms));
|
|
129
|
+
if (terms.length) out = out.filter((s) => matchesSession(s, terms, this.scope === "all"));
|
|
126
130
|
this.filtered = out;
|
|
127
131
|
const idx = prev ? out.indexOf(prev) : -1;
|
|
128
132
|
this.selected = idx >= 0 ? idx : keepSelection ? Math.min(this.selected, Math.max(0, out.length - 1)) : 0;
|
|
@@ -271,7 +275,7 @@ export class PeekComponent implements Component, Focusable {
|
|
|
271
275
|
this.invalidate();
|
|
272
276
|
return;
|
|
273
277
|
}
|
|
274
|
-
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("
|
|
278
|
+
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("y"))) {
|
|
275
279
|
const step = matchesKey(data, Key.pageDown) ? this.bodyHeight() : Math.max(1, this.bodyHeight() >> 1);
|
|
276
280
|
this.previewOffset += step;
|
|
277
281
|
this.invalidate();
|
|
@@ -294,6 +298,13 @@ export class PeekComponent implements Component, Focusable {
|
|
|
294
298
|
this.invalidate();
|
|
295
299
|
return;
|
|
296
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
|
+
}
|
|
297
308
|
if (matchesKey(data, Key.ctrl("n"))) {
|
|
298
309
|
this.jumpMatch(1);
|
|
299
310
|
return;
|
|
@@ -482,13 +493,13 @@ export class PeekComponent implements Component, Focusable {
|
|
|
482
493
|
return { rt, hasBody: rt.user.length > 0 || rt.assistant.length > 0 };
|
|
483
494
|
}
|
|
484
495
|
|
|
485
|
-
// 右栏。预览键(会话 + 关键词 + 宽度 +
|
|
496
|
+
// 右栏。预览键(会话 + 关键词 + 宽度 + 工具开关 + 角色过滤)没变就复用;变了重建,滚动位置放到首个命中处或底部
|
|
486
497
|
private rebuildPreview(rw: number, rt: RoleTerms): void {
|
|
487
498
|
const s = this.filtered[this.selected];
|
|
488
|
-
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";
|
|
489
500
|
if (key === this.previewKey) return;
|
|
490
501
|
this.previewKey = key;
|
|
491
|
-
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);
|
|
492
503
|
this.previewLines = p.lines;
|
|
493
504
|
this.matchLines = p.matchLines;
|
|
494
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 回复里
|
package/src/query.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { PeekMsg, PeekSession } from "./sessions.ts";
|
|
|
4
4
|
import { stylePrefix, type PeekTheme } from "./theme.ts";
|
|
5
5
|
|
|
6
6
|
// 搜索框语法。空白分隔多个词,全部满足才算(AND)。每个词可以是:
|
|
7
|
-
// foo
|
|
7
|
+
// foo 子串,大小写不敏感,在对话正文和会话名里找;全局范围下还看工作目录(见 termHits)
|
|
8
8
|
// "foo bar" 短语,引号里的一段空白匹配正文里任意一段空白(含换行)
|
|
9
9
|
// a|b 任一命中(OR);要搜字面的 | 就加引号
|
|
10
10
|
// /re/ 正则(JS 语法,自动带 i 和 m 标志,不能含空白,用 \s 代替);写错了按普通文字搜
|
|
@@ -116,7 +116,7 @@ function hit(re: RegExp, s: string): boolean {
|
|
|
116
116
|
return execFrom(re, s, 0) !== null;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
function termHits(s: PeekSession, t: Term): boolean {
|
|
119
|
+
function termHits(s: PeekSession, t: Term, includeCwd: boolean): boolean {
|
|
120
120
|
switch (t.field) {
|
|
121
121
|
case "name":
|
|
122
122
|
return hit(t.re, s.name);
|
|
@@ -126,16 +126,17 @@ function termHits(s: PeekSession, t: Term): boolean {
|
|
|
126
126
|
case "assistant":
|
|
127
127
|
return s.msgs.some((m) => m.role === t.field && hit(t.re, m.text));
|
|
128
128
|
default:
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
return hit(t.re, s.name) || s.msgs.some((m) => hit(t.re, m.text));
|
|
129
|
+
// 普通词只在全局范围看工作目录:当前目录树范围下所有会话都带同一段前缀,路径词会命中全部会话。
|
|
130
|
+
// 当前范围下要搜路径仍可用 dir:
|
|
131
|
+
return hit(t.re, s.name) || s.msgs.some((m) => hit(t.re, m.text)) || (includeCwd && hit(t.re, s.cwd));
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// 每个词都要满足:普通词在会话名或某条消息里出现,排除词则哪里都不出现;带前缀的只看对应的地方。
|
|
136
|
+
// includeCwd:普通词是否也匹配工作目录,调用方传 scope === "all"
|
|
136
137
|
// 正文不预存小写副本,直接在原文上用不区分大小写的正则匹配
|
|
137
|
-
export function matchesSession(s: PeekSession, terms: Term[]): boolean {
|
|
138
|
-
return terms.every((t) => termHits(s, t) !== t.negate);
|
|
138
|
+
export function matchesSession(s: PeekSession, terms: Term[], includeCwd = false): boolean {
|
|
139
|
+
return terms.every((t) => termHits(s, t, includeCwd) !== t.negate);
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
// 单段文字用:命中任意一个词就算
|