pi-timeline 0.1.0 → 0.1.1
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 +7 -2
- package/README_zh.md +3 -0
- package/package.json +2 -2
- package/src/index.ts +10 -6
package/README.md
CHANGED
|
@@ -27,10 +27,12 @@ transcript viewport precisely to the one you pick.
|
|
|
27
27
|
> [!NOTE] Jump target
|
|
28
28
|
> `user` the user message itself
|
|
29
29
|
> `reply` the final answer
|
|
30
|
+
>
|
|
31
|
+
> Plain-text replies use the same block start as native `Ctrl+↑/↓`. Replies with
|
|
32
|
+
> thinking skip it and keep one blank line immediately before the answer, if present.
|
|
30
33
|
|
|
31
34
|
> [!WARNING]
|
|
32
|
-
> Invalid shortcuts are rejected. Without a modifier, only named keys (such as
|
|
33
|
-
> `f2`) are accepted.
|
|
35
|
+
> Invalid shortcuts are rejected. Without a modifier, only named keys (such as`f2`) are accepted.
|
|
34
36
|
|
|
35
37
|
## Install
|
|
36
38
|
|
|
@@ -41,6 +43,9 @@ pi -e npm:pi-timeline # one-shot trial
|
|
|
41
43
|
|
|
42
44
|
## Configuration file
|
|
43
45
|
|
|
46
|
+
> [!TIP]
|
|
47
|
+
> Use `/timeline-settings` to open the visualization settings panel
|
|
48
|
+
|
|
44
49
|
At `~/.pi/agent/timeline.json`
|
|
45
50
|
|
|
46
51
|
| Key | Type | Default | Meaning |
|
package/README_zh.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-timeline",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Message timeline for pi: list a session's user messages and jump to any of them with exact, instant scrolling.",
|
|
5
5
|
"keywords": ["pi-package", "pi-extension", "timeline", "navigation"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/Hayston1001/pi-extensions.git",
|
|
10
10
|
"directory": "packages/timeline"
|
|
11
11
|
},
|
|
12
|
-
"files": ["src
|
|
12
|
+
"files": ["src", "README.md", "README_zh.md"],
|
|
13
13
|
"pi": {
|
|
14
14
|
"extensions": ["./src/index.ts"]
|
|
15
15
|
},
|
package/src/index.ts
CHANGED
|
@@ -412,7 +412,7 @@ interface MessageRecord {
|
|
|
412
412
|
landmark: boolean;
|
|
413
413
|
/** 助手块里带不带思考: 带思考就跳过思考落正文, 不带就落块首(= pi 原生) */
|
|
414
414
|
hasThinking?: boolean;
|
|
415
|
-
/**
|
|
415
|
+
/** 正文起点在块内的偏移, 含紧邻正文的一行空白(带思考的块才用得上) */
|
|
416
416
|
textOffset?: number;
|
|
417
417
|
}
|
|
418
418
|
|
|
@@ -748,8 +748,9 @@ function hasTextSource(value: any, parts: string[], depth = 0): boolean {
|
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
/**
|
|
751
|
-
*
|
|
751
|
+
* 带思考的助手块里正文的起点(块内偏移行数), 保留正文前紧邻的一行空白.
|
|
752
752
|
* 认正文先按内容(源文本 == 消息里的 text 段), 认不出再按形状(第一个不是思考块的可见块).
|
|
753
|
+
* 只在前一行确实为空时向前一行, 不硬减偏移, 避免把思考末行带回视口.
|
|
753
754
|
*/
|
|
754
755
|
function assistantBodyOffset(component: any, width: number): number | undefined {
|
|
755
756
|
const children: any[] | undefined = component?.contentContainer?.children;
|
|
@@ -757,10 +758,13 @@ function assistantBodyOffset(component: any, width: number): number | undefined
|
|
|
757
758
|
const parts = assistantTextParts(component?.lastMessage);
|
|
758
759
|
const entries: Array<{ child: any; offset: number; visible: boolean }> = [];
|
|
759
760
|
let offset = 0;
|
|
761
|
+
let previousLine: string | undefined;
|
|
760
762
|
for (const child of children) {
|
|
761
763
|
const lines: string[] = typeof child?.render === "function" ? child.render(width) : [];
|
|
762
|
-
|
|
764
|
+
const anchor = offset > 0 && previousLine !== undefined && stripAnsi(previousLine).trim() === "" ? offset - 1 : offset;
|
|
765
|
+
entries.push({ child, offset: anchor, visible: lines.some((line) => stripAnsi(line).trim().length > 0) });
|
|
763
766
|
offset += lines.length;
|
|
767
|
+
if (lines.length > 0) previousLine = lines[lines.length - 1];
|
|
764
768
|
}
|
|
765
769
|
for (const entry of entries) {
|
|
766
770
|
if (entry.visible && hasTextSource(entry.child, parts)) return entry.offset;
|
|
@@ -776,8 +780,8 @@ function assistantBodyOffset(component: any, width: number): number | undefined
|
|
|
776
780
|
*
|
|
777
781
|
* - 纯文本回复: **块的第一行**--和 pi 原生 `Ctrl+↑/↓`(`scrollToPrompt`)逐行一致(它认的
|
|
778
782
|
* 就是行首带 OSC 133 块标记的那行, 也就是块首的上边距).
|
|
779
|
-
* - 回复块里**带思考**: 跳过思考,
|
|
780
|
-
*
|
|
783
|
+
* - 回复块里**带思考**: 跳过思考, 保留正文前紧邻的一行空白(没有空行就落正文).
|
|
784
|
+
* 既不让几十行思考把正文推出屏幕, 也不让正文贴在视口最上沿.
|
|
781
785
|
*/
|
|
782
786
|
function anchoredRow(record: MessageRecord | undefined): number | undefined {
|
|
783
787
|
if (!record) return undefined;
|
|
@@ -841,7 +845,7 @@ function locateRows(items: TimelineItem[], tui: any, view: ScrollViewLike, jumpT
|
|
|
841
845
|
if (located.some((item) => item !== undefined)) {
|
|
842
846
|
const rows = located.map((item) => {
|
|
843
847
|
if (!item) return undefined;
|
|
844
|
-
//
|
|
848
|
+
// 纯文本落块首; 带思考时落正文, 保留正文前的一行空白.
|
|
845
849
|
return anchoredRow(pickAnchorRecord(item, collected.records, jumpTo));
|
|
846
850
|
});
|
|
847
851
|
const blockRows = located.map((item) => (item ? collected.records[item.index].row : undefined));
|