@liguoshuai/pi-web-chat 1.7.2 → 1.7.3
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/docs/ARCHITECTURE.md +1 -1
- package/docs/CHANGELOG.md +10 -0
- package/docs/ISSUES.md +36 -0
- package/package.json +1 -1
- package/public/app.js +13 -0
- package/public/style.css +23 -2
- package/server.js +1 -1
package/docs/ARCHITECTURE.md
CHANGED
package/docs/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [1.7.3] - 2026-07-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **新建会话即时绑定会话文件与 URL (Instant Session Binding)**:在 `public/app.js` 中自动捕获 RPC 消息中的 `sessionFile`,新建会话发送第一条 Prompt 后立即更新浏览器 URL、当前状态与侧边栏高亮,防止页面刷新丢失当前对话。
|
|
14
|
+
- **服务端历史文本提取空指针保护 (Null Safety in extractText)**:在 `server.js` 的 `extractText` 增加对分片项为 null/undefined 的防守式过滤,避免异常历史记录导致元数据读取崩溃。
|
|
15
|
+
- **移动端侧边栏底部连接状态与版本号显示修复 (Mobile Sidebar Bottom & Safe Area)**:修复手机端侧边栏 `100vh` 溢出导致底部“已连接/版本号”不可见的问题;增加 `100dvh`、`min-height: 0`、`flex-shrink: 0` 及全面屏 `safe-area-inset-bottom` 适配。
|
|
16
|
+
|
|
17
|
+
|
|
8
18
|
---
|
|
9
19
|
|
|
10
20
|
## [1.7.0] - 2026-07-26
|
package/docs/ISSUES.md
CHANGED
|
@@ -64,3 +64,39 @@
|
|
|
64
64
|
**修复**:
|
|
65
65
|
- 以 Session Key 为粒度管理 `PiAgent` 实例。
|
|
66
66
|
- 任何一个客户端发送 `prompt` 或 `steer` 指令时,通过 `agent.wsSend(..., senderWs)` 广播同步给连入该 Session 的其他客户端。
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 7. 新建会话时 URL、侧边栏高亮与会话状态未即时绑定
|
|
71
|
+
|
|
72
|
+
**症状**:新建对话并发送消息后,底层已生成 session jsonl,但浏览器 URL 仍为 `/`,`state.currentSessionFile` 为空,刷新页面后丢失当前会话视图,侧边栏也无法立即高亮当前项。
|
|
73
|
+
|
|
74
|
+
**根因**:前端仅在显式点击侧边栏会话或调用 `switch_session` 时才绑定 `state.currentSessionFile`,未在 `prompt` 返回包含 `sessionFile` 的 RPC 数据时即时同步。
|
|
75
|
+
|
|
76
|
+
**修复**:在前端 `handlePiMessage` 入口增加对 `obj.data?.sessionFile` 的自动感知与绑定,一旦检测到新分配的 sessionFile,即时更新 `state.currentSessionFile`、通过 `history.replaceState` 同步浏览器 URL 并刷新侧边栏高亮。
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 8. 历史会话内容反序列化时潜在的 null 分片过滤崩溃
|
|
81
|
+
|
|
82
|
+
**症状**:当历史会话 jsonl 中偶发由于中断或格式异常导致 content 数组中包含 `null`/`undefined` 元素时,`extractText` 会触发 `TypeError: Cannot read properties of null (reading 'type')`。
|
|
83
|
+
|
|
84
|
+
**修复**:在 `server.js` 的 `extractText` 过滤器中加入判空保护 `c && (c.type === "text" || typeof c === "string")`。
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 9. 移动端侧边栏底部连接状态与版本号被遮挡/不可见
|
|
89
|
+
|
|
90
|
+
**症状**:在手机端浏览器打开左侧抽屉(Sidebar)时,底部的“已连接 / 版本号”信息看不到或被系统手势栏/底栏遮挡。
|
|
91
|
+
|
|
92
|
+
**根因**:
|
|
93
|
+
1. 移动端 `.sidebar` 样式设置了固定 `height: 100vh`,而手机浏览器 `100vh` 包含了底部工具栏高度,导致底部区域被挤出屏幕外。
|
|
94
|
+
2. `.session-list` 缺少 `min-height: 0` 约束,会话数量多时 flexbox 未能限制高度,将 `.sidebar-bottom` 顶出视口。
|
|
95
|
+
3. 缺少 `env(safe-area-inset-bottom)` 安全区域边距,导致贴底元素被全面屏手势条遮挡。
|
|
96
|
+
|
|
97
|
+
**修复**:
|
|
98
|
+
- 移动端 `.sidebar` 改用 `height: 100dvh; max-height: 100dvh; overflow: hidden;`。
|
|
99
|
+
- 给 `.session-list` 增加 `min-height: 0`,给 `.sidebar-bottom` 增加 `flex-shrink: 0`。
|
|
100
|
+
- 引入 `safe-area-inset-bottom` 底部安全区自适应内边距,并将 z-index 提升至 105,确保手机端完整可见。
|
|
101
|
+
|
|
102
|
+
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -1107,6 +1107,19 @@ function sendWs(obj) {
|
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
function handlePiMessage(obj) {
|
|
1110
|
+
// Automatically bind to the session file as soon as pi allocates it on disk
|
|
1111
|
+
if (obj.data?.sessionFile && obj.data.sessionFile !== state.currentSessionFile) {
|
|
1112
|
+
state.currentSessionFile = obj.data.sessionFile;
|
|
1113
|
+
try {
|
|
1114
|
+
const newUrl = window.location.pathname + "?session=" + encodeURIComponent(obj.data.sessionFile);
|
|
1115
|
+
window.history.replaceState({ session: obj.data.sessionFile }, "", newUrl);
|
|
1116
|
+
} catch {}
|
|
1117
|
+
if ($("#topSessionName").textContent === "新对话") {
|
|
1118
|
+
$("#topSessionName").textContent = baseName(obj.data.sessionFile);
|
|
1119
|
+
}
|
|
1120
|
+
refreshSessions();
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1110
1123
|
// Backfill markers emitted by the server when it replays buffered events
|
|
1111
1124
|
// that happened in the background while no browser was attached.
|
|
1112
1125
|
if (obj.type === "backfill_start") {
|
package/public/style.css
CHANGED
|
@@ -51,6 +51,7 @@ body {
|
|
|
51
51
|
height: 100%;
|
|
52
52
|
transition: margin-left 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
53
53
|
z-index: 100;
|
|
54
|
+
overflow: hidden;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/* Sidebar collapsed state on desktop */
|
|
@@ -74,6 +75,7 @@ body {
|
|
|
74
75
|
display: flex;
|
|
75
76
|
gap: 8px;
|
|
76
77
|
align-items: center;
|
|
78
|
+
flex-shrink: 0;
|
|
77
79
|
}
|
|
78
80
|
.btn-new {
|
|
79
81
|
flex: 1;
|
|
@@ -94,6 +96,7 @@ body {
|
|
|
94
96
|
|
|
95
97
|
.sidebar-search {
|
|
96
98
|
padding: 0 10px 8px;
|
|
99
|
+
flex-shrink: 0;
|
|
97
100
|
}
|
|
98
101
|
.sidebar-search input {
|
|
99
102
|
width: 100%;
|
|
@@ -109,6 +112,7 @@ body {
|
|
|
109
112
|
|
|
110
113
|
.session-list {
|
|
111
114
|
flex: 1;
|
|
115
|
+
min-height: 0;
|
|
112
116
|
overflow-y: auto;
|
|
113
117
|
padding: 4px 8px 16px;
|
|
114
118
|
}
|
|
@@ -134,10 +138,13 @@ body {
|
|
|
134
138
|
.sidebar-empty { color: var(--text-dim); font-size: 13px; padding: 20px 12px; text-align: center; }
|
|
135
139
|
|
|
136
140
|
.sidebar-bottom {
|
|
141
|
+
flex-shrink: 0;
|
|
137
142
|
border-top: 1px solid var(--border);
|
|
138
|
-
padding: 10px;
|
|
143
|
+
padding: 10px 12px;
|
|
144
|
+
padding-bottom: max(10px, env(safe-area-inset-bottom, 10px));
|
|
139
145
|
font-size: 12px;
|
|
140
146
|
color: var(--text-muted);
|
|
147
|
+
background: var(--bg-sidebar);
|
|
141
148
|
}
|
|
142
149
|
.sidebar-bottom a { color: var(--text-muted); text-decoration: none; }
|
|
143
150
|
.sidebar-bottom a:hover { color: var(--text); }
|
|
@@ -149,7 +156,7 @@ body {
|
|
|
149
156
|
}
|
|
150
157
|
.app-version {
|
|
151
158
|
font-size: 11px;
|
|
152
|
-
color: var(--text-
|
|
159
|
+
color: var(--text-muted);
|
|
153
160
|
user-select: none;
|
|
154
161
|
}
|
|
155
162
|
.conn-status {
|
|
@@ -827,12 +834,26 @@ body {
|
|
|
827
834
|
left: 0;
|
|
828
835
|
bottom: 0;
|
|
829
836
|
width: 280px; /* Wider and more usable drawer on mobile */
|
|
837
|
+
height: 100%;
|
|
830
838
|
height: 100vh;
|
|
839
|
+
height: 100dvh;
|
|
840
|
+
max-height: 100dvh;
|
|
831
841
|
margin-left: 0 !important; /* Cancel desktop margin-left sliding */
|
|
832
842
|
transform: translateX(-100%);
|
|
833
843
|
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
834
844
|
box-shadow: 4px 0 25px rgba(0, 0, 0, 0.5);
|
|
835
845
|
border-right: 1px solid var(--border);
|
|
846
|
+
display: flex;
|
|
847
|
+
flex-direction: column;
|
|
848
|
+
overflow: hidden;
|
|
849
|
+
z-index: 105;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.sidebar-bottom {
|
|
853
|
+
flex-shrink: 0;
|
|
854
|
+
padding: 12px 14px;
|
|
855
|
+
padding-bottom: max(16px, calc(10px + env(safe-area-inset-bottom, 0px)));
|
|
856
|
+
background: var(--bg-sidebar);
|
|
836
857
|
}
|
|
837
858
|
|
|
838
859
|
.app.sidebar-open .sidebar {
|
package/server.js
CHANGED
|
@@ -584,7 +584,7 @@ function extractText(content) {
|
|
|
584
584
|
if (typeof content === "string") return content;
|
|
585
585
|
if (!Array.isArray(content)) return "";
|
|
586
586
|
return content
|
|
587
|
-
.filter(c => c.type === "text" || typeof c === "string")
|
|
587
|
+
.filter(c => c && (c.type === "text" || typeof c === "string"))
|
|
588
588
|
.map(c => typeof c === "string" ? c : c.text)
|
|
589
589
|
.join("");
|
|
590
590
|
}
|