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 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
@@ -39,6 +39,9 @@ pi -e npm:pi-timeline # 一次性试用
39
39
 
40
40
  ## 配置文件
41
41
 
42
+ > [!TIP]
43
+ > 使用 `/timeline-settings` 命令打开可视化设置面板
44
+
42
45
  位于 `~/.pi/agent/timeline.json`
43
46
 
44
47
  | 键 | 类型 | 默认 | 说明 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-timeline",
3
- "version": "0.1.0",
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/index.ts", "src/i18n.ts", "README.md", "README_zh.md"],
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
- entries.push({ child, offset, visible: lines.some((line) => stripAnsi(line).trim().length > 0) });
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
- * 正文被推出屏幕(踩过: #5 那种).
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
- // 落脚行 = 块的第一行(和 pi 原生 Ctrl+↑/↓ 认的那行一致)
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));