@pageops/sdk 0.1.39 → 0.1.40

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
@@ -17,6 +17,10 @@ inside chat answers, including live updates and historical Run selection. The
17
17
  sidebar source-package controls remain available; backend step data is retained
18
18
  for cancellation and history.
19
19
 
20
+ Version `0.1.40` adds multi-selection and confirmed batch deletion to the
21
+ conversation sidebar. Each deletion uses the existing owner-scoped API; failed
22
+ items remain selected so they can be retried.
23
+
20
24
  `src/index.js` is the readable ESM runtime baseline, `src/styles.css` preserves
21
25
  the 0.1.34 styles, and `src/extensions.css` contains the new non-minified style
22
26
  rules. `npm run build` synchronizes ESM, UMD, and CSS under `dist/` without
@@ -180,3 +180,71 @@
180
180
  cursor: not-allowed;
181
181
  opacity: .4;
182
182
  }
183
+
184
+ .po-thread-actions {
185
+ display: flex;
186
+ align-items: center;
187
+ flex-wrap: wrap;
188
+ gap: 5px;
189
+ min-height: 28px;
190
+ }
191
+
192
+ .po-thread-actions button {
193
+ min-height: 28px;
194
+ border: 0;
195
+ border-radius: 6px;
196
+ background: transparent;
197
+ color: #475569;
198
+ padding: 3px 6px;
199
+ font-size: 12px;
200
+ font-weight: 650;
201
+ }
202
+
203
+ .po-thread-actions button:hover:not(:disabled) {
204
+ background: #dbeafe;
205
+ }
206
+
207
+ .po-thread-actions [data-action="delete-selected-threads"] {
208
+ margin-left: auto;
209
+ color: #b91c1c;
210
+ }
211
+
212
+ .po-thread-actions [data-action="delete-selected-threads"]:hover:not(:disabled) {
213
+ background: #fee2e2;
214
+ }
215
+
216
+ .po-thread-actions button:disabled {
217
+ cursor: not-allowed;
218
+ opacity: .45;
219
+ }
220
+
221
+ .po-thread-select-all {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ gap: 5px;
225
+ color: #475569;
226
+ font-size: 12px;
227
+ font-weight: 650;
228
+ cursor: pointer;
229
+ }
230
+
231
+ .po-thread-select-all input,
232
+ .po-thread-checkbox {
233
+ width: 16px;
234
+ height: 16px;
235
+ accent-color: var(--po-primary);
236
+ cursor: pointer;
237
+ }
238
+
239
+ .po-thread-row.selecting {
240
+ grid-template-columns: 30px minmax(0, 1fr);
241
+ }
242
+
243
+ .po-thread-checkbox {
244
+ margin-left: 9px;
245
+ }
246
+
247
+ .po-thread-row.selecting .po-thread {
248
+ min-width: 0;
249
+ padding-left: 4px;
250
+ }
@@ -223,6 +223,9 @@ class V {
223
223
  i(this, "answerError", "");
224
224
  i(this, "conversationError", "");
225
225
  i(this, "deletingThreadId", "");
226
+ i(this, "selectingConversations", !1);
227
+ i(this, "selectedConversationIds", /* @__PURE__ */ new Set());
228
+ i(this, "bulkDeleting", !1);
226
229
  i(this, "streamFailure", "");
227
230
  i(this, "streamDone", null);
228
231
  i(this, "activeQuestionStartedAt", "");
@@ -239,7 +242,7 @@ class V {
239
242
  }
240
243
  unmount() {
241
244
  var e;
242
- this.cleanupFloatingLauncher(), this.stopSourceRefresh(), this.cancelActiveAnswer(!1), this.resetAnswerState(), (e = this.root) == null || e.remove(), this.root = null, this.panel = null, this.context = null, this.contextKey = "", this.selectedPageKey = "", this.currentThread = null, this.conversations = [], this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoading = !1, this.runLoadError = "", this.clearCopyFeedback(), this.sourceSnapshots = [], this.selectedSourceSnapshotId = null, this.sourceUploadStatus = "", this.sourceUploadProgress = null, this.sourceUploadError = "", this.sourceLoadError = "", this.sourceUploading = !1, this.deletingSourceId = "", this.developerAccess = !1, this.activeQuestionStartedAt = "", this.answerAudience = "developer";
245
+ this.cleanupFloatingLauncher(), this.stopSourceRefresh(), this.cancelActiveAnswer(!1), this.resetAnswerState(), (e = this.root) == null || e.remove(), this.root = null, this.panel = null, this.context = null, this.contextKey = "", this.selectedPageKey = "", this.currentThread = null, this.conversations = [], this.selectingConversations = !1, this.selectedConversationIds.clear(), this.bulkDeleting = !1, this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoading = !1, this.runLoadError = "", this.clearCopyFeedback(), this.sourceSnapshots = [], this.selectedSourceSnapshotId = null, this.sourceUploadStatus = "", this.sourceUploadProgress = null, this.sourceUploadError = "", this.sourceLoadError = "", this.sourceUploading = !1, this.deletingSourceId = "", this.developerAccess = !1, this.activeQuestionStartedAt = "", this.answerAudience = "developer";
243
246
  }
244
247
  open() {
245
248
  this.root && (this.cleanupFloatingLauncher(), this.root.innerHTML = "", this.panel = document.createElement("div"), this.panel.className = "po-panel", this.root.appendChild(this.panel), this.loadDeveloperAccess(), this.loadSources(), this.loadConversations(), this.renderPanel());
@@ -252,7 +255,7 @@ class V {
252
255
  const t = this.options.appId, s = String(this.options.ownerId || "").trim();
253
256
  this.options = { ...this.options, ...e }, this.client = new K(this.options);
254
257
  const r = String(this.options.ownerId || "").trim(), n = this.options.appId !== t;
255
- (n || r !== s) && (n && (this.answerAudience = this.loadAnswerAudience(this.options.appId), this.selectedSourceSnapshotId = null), this.currentThread = null, this.selectedPageKey = "", this.conversations = [], this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = ""), this.context = null, this.contextKey = "", r !== s && this.loadDeveloperAccess(), this.panel && this.loadConversations(), this.renderPanel();
258
+ (n || r !== s) && (n && (this.answerAudience = this.loadAnswerAudience(this.options.appId), this.selectedSourceSnapshotId = null), this.currentThread = null, this.selectedPageKey = "", this.conversations = [], this.selectingConversations = !1, this.selectedConversationIds.clear(), this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = ""), this.context = null, this.contextKey = "", r !== s && this.loadDeveloperAccess(), this.panel && this.loadConversations(), this.renderPanel();
256
259
  }
257
260
  renderClosed() {
258
261
  var e;
@@ -397,7 +400,7 @@ class V {
397
400
  }
398
401
  }
399
402
  async loadConversations() {
400
- this.client && (this.conversations = await this.client.listConversations(), this.renderPanel());
403
+ this.client && (this.conversations = await this.client.listConversations(), this.selectedConversationIds = new Set([...this.selectedConversationIds].filter((e) => this.conversations.some((t) => t.id === e))), this.renderPanel());
401
404
  }
402
405
  async newConversation(e = "") {
403
406
  var s;
@@ -413,7 +416,7 @@ class V {
413
416
  }
414
417
  async deleteConversation(e) {
415
418
  var t;
416
- if (!(!this.client || this.asking || this.deletingThreadId) && window.confirm(`确定删除会话“${e.title}”吗?此操作无法撤销。`)) {
419
+ if (!(!this.client || this.asking || this.deletingThreadId || this.bulkDeleting) && window.confirm(`确定删除会话“${e.title}”吗?此操作无法撤销。`)) {
417
420
  this.deletingThreadId = e.id, this.conversationError = "", this.renderPanel();
418
421
  try {
419
422
  if (await this.client.deleteConversation(e.id), this.conversations = this.conversations.filter((s) => s.id !== e.id), ((t = this.currentThread) == null ? void 0 : t.id) === e.id) {
@@ -428,6 +431,42 @@ class V {
428
431
  }
429
432
  }
430
433
  }
434
+ async deleteSelectedConversations() {
435
+ if (!this.client || this.asking || this.deletingThreadId || this.bulkDeleting) return;
436
+ const e = this.conversations.filter((t) => this.selectedConversationIds.has(t.id));
437
+ if (!e.length || !window.confirm(`确定删除选中的 ${e.length} 个会话吗?此操作无法撤销。`)) return;
438
+ const t = this.client, s = this.options.appId, r = this.client.ownerId(), n = [], c = [];
439
+ this.bulkDeleting = !0, this.conversationError = "", this.renderPanel();
440
+ try {
441
+ for (const p of e) {
442
+ try {
443
+ await t.deleteConversation(p.id), n.push(p.id);
444
+ } catch (m) {
445
+ c.push({ id: p.id, reason: m instanceof Error ? m.message : "请求失败" });
446
+ }
447
+ }
448
+ if (this.options.appId !== s || this.client.ownerId() !== r) return;
449
+ const p = new Set(n);
450
+ this.conversations = this.conversations.filter((m) => !p.has(m.id));
451
+ this.selectedConversationIds = new Set(c.map((m) => m.id));
452
+ let loadError = "";
453
+ if (this.currentThread && p.has(this.currentThread.id)) {
454
+ this.currentThread = null, this.selectedPageKey = "", this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = "", this.streaming = "", this.answerError = "", this.answerStopped = !1;
455
+ const m = this.conversations[0];
456
+ if (m) {
457
+ try {
458
+ await this.selectThread(m);
459
+ } catch (g) {
460
+ this.currentThread = null, loadError = g instanceof Error ? g.message : "加载会话失败";
461
+ }
462
+ }
463
+ }
464
+ this.selectingConversations = c.length > 0;
465
+ this.conversationError = [c.length ? `已删除 ${n.length} 个会话,${c.length} 个删除失败:${c[0].reason}` : "", loadError].filter(Boolean).join(";");
466
+ } finally {
467
+ this.bulkDeleting = !1, this.renderPanel();
468
+ }
469
+ }
431
470
  async uploadSource(e, t) {
432
471
  var s;
433
472
  if (this.client) {
@@ -633,23 +672,29 @@ class V {
633
672
  renderPanel(e = this.asking) {
634
673
  var g;
635
674
  if (!this.panel) return;
636
- const s = (g = this.panel.querySelector(".po-messages")) == null ? void 0 : g.scrollTop, r = this.questionUnavailableReason(), n = r || (this.asking ? "回答生成中,请稍候" : this.sourceSnapshots.length ? "请输入页面分析问题,Enter 发送,Shift + Enter 换行" : "可直接闲聊;业务问题需先上传代码包"), c = !!(r || this.asking), p = r ? '<button type="submit" disabled>暂不可提问</button>' : this.asking ? '<button class="po-stop" type="button" data-action="stop-answer" aria-label="停止输出" title="停止输出"><span aria-hidden="true"></span></button>' : '<button type="submit">发送</button>', m = C.map((o) => `
675
+ const listScroll = this.panel.querySelector(".po-thread-list")?.scrollTop;
676
+ const s = (g = this.panel.querySelector(".po-messages")) == null ? void 0 : g.scrollTop, r = this.questionUnavailableReason(), n = r || (this.asking ? "回答生成中,请稍候" : this.sourceSnapshots.length ? "请输入页面分析问题,Enter 发送,Shift + Enter 换行" : "可直接闲聊;业务问题需先上传代码包"), c = !!(r || this.asking || this.bulkDeleting), p = r ? '<button type="submit" disabled>暂不可提问</button>' : this.asking ? '<button class="po-stop" type="button" data-action="stop-answer" aria-label="停止输出" title="停止输出"><span aria-hidden="true"></span></button>' : `<button type="submit" ${this.bulkDeleting ? "disabled" : ""}>发送</button>`, m = C.map((o) => `
637
677
  <button type="button" data-audience="${o.value}" aria-pressed="${o.value === this.answerAudience}" ${this.asking ? "disabled" : ""}>${o.label}</button>
638
678
  `).join("");
639
679
  if (this.panel.innerHTML = `
640
680
  <aside class="po-sidebar">
641
681
  <div class="po-brand"><div class="po-logo"></div><div><strong>PageOps 问答</strong><span>页面工程专家</span></div></div>
642
- <button class="po-new" data-action="new" ${this.asking ? "disabled" : ""}>+ 新建问答</button>
682
+ <button class="po-new" data-action="new" ${this.asking || this.bulkDeleting ? "disabled" : ""}>+ 新建问答</button>
643
683
  <input class="po-search" placeholder="搜索问答" />
644
684
  ${this.conversationError ? `<p class="po-conversation-error" role="alert">${l(this.conversationError)}</p>` : ""}
685
+ ${this.conversations.length ? `<div class="po-thread-actions">${this.selectingConversations ? `
686
+ <label class="po-thread-select-all"><input type="checkbox" data-action="select-all-threads" ${this.selectedConversationIds.size === this.conversations.length ? "checked" : ""} ${this.bulkDeleting ? "disabled" : ""} />全选</label>
687
+ <button type="button" data-action="delete-selected-threads" ${!this.selectedConversationIds.size || this.bulkDeleting ? "disabled" : ""}>${this.bulkDeleting ? "删除中..." : `删除 (${this.selectedConversationIds.size})`}</button>
688
+ <button type="button" data-action="cancel-thread-selection" ${this.bulkDeleting ? "disabled" : ""}>取消</button>` : `<button type="button" data-action="select-threads" ${this.asking || this.deletingThreadId ? "disabled" : ""}>批量删除</button>`}</div>` : ""}
645
689
  <div class="po-thread-list">${this.conversations.map((o) => {
646
690
  var y;
647
691
  return `
648
- <div class="po-thread-row ${o.id === ((y = this.currentThread) == null ? void 0 : y.id) ? "active" : ""}">
649
- <button class="po-thread" data-thread="${l(o.id)}" ${this.asking ? "disabled" : ""}>
692
+ <div class="po-thread-row ${o.id === ((y = this.currentThread) == null ? void 0 : y.id) ? "active" : ""} ${this.selectingConversations ? "selecting" : ""}">
693
+ ${this.selectingConversations ? `<input class="po-thread-checkbox" type="checkbox" data-select-thread="${l(o.id)}" aria-label="选择会话:${l(o.title)}" ${this.selectedConversationIds.has(o.id) ? "checked" : ""} ${this.bulkDeleting ? "disabled" : ""} />` : ""}
694
+ <button class="po-thread" data-thread="${l(o.id)}" ${this.asking || this.bulkDeleting ? "disabled" : ""}>
650
695
  <strong>${l(o.title)}</strong><span>${O(o.created_at)}</span>
651
696
  </button>
652
- <button class="po-thread-delete" type="button" data-delete-thread="${l(o.id)}" aria-label="删除会话:${l(o.title)}" title="删除会话" ${this.asking || this.deletingThreadId === o.id ? "disabled" : ""}><span class="po-trash-icon" aria-hidden="true"></span></button>
697
+ ${this.selectingConversations ? "" : `<button class="po-thread-delete" type="button" data-delete-thread="${l(o.id)}" aria-label="删除会话:${l(o.title)}" title="删除会话" ${this.asking || this.deletingThreadId === o.id ? "disabled" : ""}><span class="po-trash-icon" aria-hidden="true"></span></button>`}
653
698
  </div>`;
654
699
  }).join("")}</div>
655
700
  </aside>
@@ -662,7 +707,7 @@ class V {
662
707
  <form class="po-composer"><textarea ${c ? "disabled" : ""} placeholder="${l(n)}"></textarea>${p}</form>
663
708
  </main>
664
709
  <aside class="po-trace"><header><h2>执行过程</h2><button data-action="close">×</button></header><section>${this.renderSourceUploader()}</section></aside>
665
- `, this.bindPanelEvents(), e) {
710
+ `, this.bindPanelEvents(), listScroll !== void 0 && (this.panel.querySelector(".po-thread-list").scrollTop = listScroll), e) {
666
711
  const o = this.panel.querySelector(".po-messages");
667
712
  o && (o.scrollTop = o.scrollHeight);
668
713
  } else if (s !== void 0) {
@@ -773,7 +818,23 @@ class V {
773
818
  }), (y = this.panel) == null || y.querySelectorAll("[data-thread]").forEach((h) => {
774
819
  h.addEventListener("click", () => {
775
820
  const d = this.conversations.find((u) => u.id === h.dataset.thread);
776
- d && this.selectThread(d);
821
+ if (!d) return;
822
+ if (this.selectingConversations) {
823
+ this.selectedConversationIds.has(d.id) ? this.selectedConversationIds.delete(d.id) : this.selectedConversationIds.add(d.id);
824
+ this.renderPanel(!1);
825
+ } else this.selectThread(d);
826
+ });
827
+ }), this.panel.querySelector('[data-action="select-threads"]')?.addEventListener("click", () => {
828
+ this.selectingConversations = !0, this.selectedConversationIds.clear(), this.conversationError = "", this.renderPanel(!1);
829
+ }), this.panel.querySelector('[data-action="cancel-thread-selection"]')?.addEventListener("click", () => {
830
+ this.selectingConversations = !1, this.selectedConversationIds.clear(), this.conversationError = "", this.renderPanel(!1);
831
+ }), this.panel.querySelector('[data-action="select-all-threads"]')?.addEventListener("change", (h) => {
832
+ this.selectedConversationIds = h.target.checked ? new Set(this.conversations.map((d) => d.id)) : /* @__PURE__ */ new Set();
833
+ this.renderPanel(!1);
834
+ }), this.panel.querySelector('[data-action="delete-selected-threads"]')?.addEventListener("click", () => void this.deleteSelectedConversations()), this.panel.querySelectorAll("[data-select-thread]").forEach((h) => {
835
+ h.addEventListener("change", () => {
836
+ h.checked ? this.selectedConversationIds.add(h.dataset.selectThread) : this.selectedConversationIds.delete(h.dataset.selectThread);
837
+ this.renderPanel(!1);
777
838
  });
778
839
  }), (w = this.panel) == null || w.querySelectorAll("[data-delete-thread]").forEach((h) => {
779
840
  h.addEventListener("click", (d) => {
@@ -223,6 +223,9 @@ class V {
223
223
  i(this, "answerError", "");
224
224
  i(this, "conversationError", "");
225
225
  i(this, "deletingThreadId", "");
226
+ i(this, "selectingConversations", !1);
227
+ i(this, "selectedConversationIds", /* @__PURE__ */ new Set());
228
+ i(this, "bulkDeleting", !1);
226
229
  i(this, "streamFailure", "");
227
230
  i(this, "streamDone", null);
228
231
  i(this, "activeQuestionStartedAt", "");
@@ -239,7 +242,7 @@ class V {
239
242
  }
240
243
  unmount() {
241
244
  var e;
242
- this.cleanupFloatingLauncher(), this.stopSourceRefresh(), this.cancelActiveAnswer(!1), this.resetAnswerState(), (e = this.root) == null || e.remove(), this.root = null, this.panel = null, this.context = null, this.contextKey = "", this.selectedPageKey = "", this.currentThread = null, this.conversations = [], this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoading = !1, this.runLoadError = "", this.clearCopyFeedback(), this.sourceSnapshots = [], this.selectedSourceSnapshotId = null, this.sourceUploadStatus = "", this.sourceUploadProgress = null, this.sourceUploadError = "", this.sourceLoadError = "", this.sourceUploading = !1, this.deletingSourceId = "", this.developerAccess = !1, this.activeQuestionStartedAt = "", this.answerAudience = "developer";
245
+ this.cleanupFloatingLauncher(), this.stopSourceRefresh(), this.cancelActiveAnswer(!1), this.resetAnswerState(), (e = this.root) == null || e.remove(), this.root = null, this.panel = null, this.context = null, this.contextKey = "", this.selectedPageKey = "", this.currentThread = null, this.conversations = [], this.selectingConversations = !1, this.selectedConversationIds.clear(), this.bulkDeleting = !1, this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoading = !1, this.runLoadError = "", this.clearCopyFeedback(), this.sourceSnapshots = [], this.selectedSourceSnapshotId = null, this.sourceUploadStatus = "", this.sourceUploadProgress = null, this.sourceUploadError = "", this.sourceLoadError = "", this.sourceUploading = !1, this.deletingSourceId = "", this.developerAccess = !1, this.activeQuestionStartedAt = "", this.answerAudience = "developer";
243
246
  }
244
247
  open() {
245
248
  this.root && (this.cleanupFloatingLauncher(), this.root.innerHTML = "", this.panel = document.createElement("div"), this.panel.className = "po-panel", this.root.appendChild(this.panel), this.loadDeveloperAccess(), this.loadSources(), this.loadConversations(), this.renderPanel());
@@ -252,7 +255,7 @@ class V {
252
255
  const t = this.options.appId, s = String(this.options.ownerId || "").trim();
253
256
  this.options = { ...this.options, ...e }, this.client = new K(this.options);
254
257
  const r = String(this.options.ownerId || "").trim(), n = this.options.appId !== t;
255
- (n || r !== s) && (n && (this.answerAudience = this.loadAnswerAudience(this.options.appId), this.selectedSourceSnapshotId = null), this.currentThread = null, this.selectedPageKey = "", this.conversations = [], this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = ""), this.context = null, this.contextKey = "", r !== s && this.loadDeveloperAccess(), this.panel && this.loadConversations(), this.renderPanel();
258
+ (n || r !== s) && (n && (this.answerAudience = this.loadAnswerAudience(this.options.appId), this.selectedSourceSnapshotId = null), this.currentThread = null, this.selectedPageKey = "", this.conversations = [], this.selectingConversations = !1, this.selectedConversationIds.clear(), this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = ""), this.context = null, this.contextKey = "", r !== s && this.loadDeveloperAccess(), this.panel && this.loadConversations(), this.renderPanel();
256
259
  }
257
260
  renderClosed() {
258
261
  var e;
@@ -397,7 +400,7 @@ class V {
397
400
  }
398
401
  }
399
402
  async loadConversations() {
400
- this.client && (this.conversations = await this.client.listConversations(), this.renderPanel());
403
+ this.client && (this.conversations = await this.client.listConversations(), this.selectedConversationIds = new Set([...this.selectedConversationIds].filter((e) => this.conversations.some((t) => t.id === e))), this.renderPanel());
401
404
  }
402
405
  async newConversation(e = "") {
403
406
  var s;
@@ -413,7 +416,7 @@ class V {
413
416
  }
414
417
  async deleteConversation(e) {
415
418
  var t;
416
- if (!(!this.client || this.asking || this.deletingThreadId) && window.confirm(`确定删除会话“${e.title}”吗?此操作无法撤销。`)) {
419
+ if (!(!this.client || this.asking || this.deletingThreadId || this.bulkDeleting) && window.confirm(`确定删除会话“${e.title}”吗?此操作无法撤销。`)) {
417
420
  this.deletingThreadId = e.id, this.conversationError = "", this.renderPanel();
418
421
  try {
419
422
  if (await this.client.deleteConversation(e.id), this.conversations = this.conversations.filter((s) => s.id !== e.id), ((t = this.currentThread) == null ? void 0 : t.id) === e.id) {
@@ -428,6 +431,42 @@ class V {
428
431
  }
429
432
  }
430
433
  }
434
+ async deleteSelectedConversations() {
435
+ if (!this.client || this.asking || this.deletingThreadId || this.bulkDeleting) return;
436
+ const e = this.conversations.filter((t) => this.selectedConversationIds.has(t.id));
437
+ if (!e.length || !window.confirm(`确定删除选中的 ${e.length} 个会话吗?此操作无法撤销。`)) return;
438
+ const t = this.client, s = this.options.appId, r = this.client.ownerId(), n = [], c = [];
439
+ this.bulkDeleting = !0, this.conversationError = "", this.renderPanel();
440
+ try {
441
+ for (const p of e) {
442
+ try {
443
+ await t.deleteConversation(p.id), n.push(p.id);
444
+ } catch (m) {
445
+ c.push({ id: p.id, reason: m instanceof Error ? m.message : "请求失败" });
446
+ }
447
+ }
448
+ if (this.options.appId !== s || this.client.ownerId() !== r) return;
449
+ const p = new Set(n);
450
+ this.conversations = this.conversations.filter((m) => !p.has(m.id));
451
+ this.selectedConversationIds = new Set(c.map((m) => m.id));
452
+ let loadError = "";
453
+ if (this.currentThread && p.has(this.currentThread.id)) {
454
+ this.currentThread = null, this.selectedPageKey = "", this.messages = [], this.steps = [], this.thoughts = [], this.selectedAgentRunId = "", this.runLoadError = "", this.streaming = "", this.answerError = "", this.answerStopped = !1;
455
+ const m = this.conversations[0];
456
+ if (m) {
457
+ try {
458
+ await this.selectThread(m);
459
+ } catch (g) {
460
+ this.currentThread = null, loadError = g instanceof Error ? g.message : "加载会话失败";
461
+ }
462
+ }
463
+ }
464
+ this.selectingConversations = c.length > 0;
465
+ this.conversationError = [c.length ? `已删除 ${n.length} 个会话,${c.length} 个删除失败:${c[0].reason}` : "", loadError].filter(Boolean).join(";");
466
+ } finally {
467
+ this.bulkDeleting = !1, this.renderPanel();
468
+ }
469
+ }
431
470
  async uploadSource(e, t) {
432
471
  var s;
433
472
  if (this.client) {
@@ -633,23 +672,29 @@ class V {
633
672
  renderPanel(e = this.asking) {
634
673
  var g;
635
674
  if (!this.panel) return;
636
- const s = (g = this.panel.querySelector(".po-messages")) == null ? void 0 : g.scrollTop, r = this.questionUnavailableReason(), n = r || (this.asking ? "回答生成中,请稍候" : this.sourceSnapshots.length ? "请输入页面分析问题,Enter 发送,Shift + Enter 换行" : "可直接闲聊;业务问题需先上传代码包"), c = !!(r || this.asking), p = r ? '<button type="submit" disabled>暂不可提问</button>' : this.asking ? '<button class="po-stop" type="button" data-action="stop-answer" aria-label="停止输出" title="停止输出"><span aria-hidden="true"></span></button>' : '<button type="submit">发送</button>', m = C.map((o) => `
675
+ const listScroll = this.panel.querySelector(".po-thread-list")?.scrollTop;
676
+ const s = (g = this.panel.querySelector(".po-messages")) == null ? void 0 : g.scrollTop, r = this.questionUnavailableReason(), n = r || (this.asking ? "回答生成中,请稍候" : this.sourceSnapshots.length ? "请输入页面分析问题,Enter 发送,Shift + Enter 换行" : "可直接闲聊;业务问题需先上传代码包"), c = !!(r || this.asking || this.bulkDeleting), p = r ? '<button type="submit" disabled>暂不可提问</button>' : this.asking ? '<button class="po-stop" type="button" data-action="stop-answer" aria-label="停止输出" title="停止输出"><span aria-hidden="true"></span></button>' : `<button type="submit" ${this.bulkDeleting ? "disabled" : ""}>发送</button>`, m = C.map((o) => `
637
677
  <button type="button" data-audience="${o.value}" aria-pressed="${o.value === this.answerAudience}" ${this.asking ? "disabled" : ""}>${o.label}</button>
638
678
  `).join("");
639
679
  if (this.panel.innerHTML = `
640
680
  <aside class="po-sidebar">
641
681
  <div class="po-brand"><div class="po-logo"></div><div><strong>PageOps 问答</strong><span>页面工程专家</span></div></div>
642
- <button class="po-new" data-action="new" ${this.asking ? "disabled" : ""}>+ 新建问答</button>
682
+ <button class="po-new" data-action="new" ${this.asking || this.bulkDeleting ? "disabled" : ""}>+ 新建问答</button>
643
683
  <input class="po-search" placeholder="搜索问答" />
644
684
  ${this.conversationError ? `<p class="po-conversation-error" role="alert">${l(this.conversationError)}</p>` : ""}
685
+ ${this.conversations.length ? `<div class="po-thread-actions">${this.selectingConversations ? `
686
+ <label class="po-thread-select-all"><input type="checkbox" data-action="select-all-threads" ${this.selectedConversationIds.size === this.conversations.length ? "checked" : ""} ${this.bulkDeleting ? "disabled" : ""} />全选</label>
687
+ <button type="button" data-action="delete-selected-threads" ${!this.selectedConversationIds.size || this.bulkDeleting ? "disabled" : ""}>${this.bulkDeleting ? "删除中..." : `删除 (${this.selectedConversationIds.size})`}</button>
688
+ <button type="button" data-action="cancel-thread-selection" ${this.bulkDeleting ? "disabled" : ""}>取消</button>` : `<button type="button" data-action="select-threads" ${this.asking || this.deletingThreadId ? "disabled" : ""}>批量删除</button>`}</div>` : ""}
645
689
  <div class="po-thread-list">${this.conversations.map((o) => {
646
690
  var y;
647
691
  return `
648
- <div class="po-thread-row ${o.id === ((y = this.currentThread) == null ? void 0 : y.id) ? "active" : ""}">
649
- <button class="po-thread" data-thread="${l(o.id)}" ${this.asking ? "disabled" : ""}>
692
+ <div class="po-thread-row ${o.id === ((y = this.currentThread) == null ? void 0 : y.id) ? "active" : ""} ${this.selectingConversations ? "selecting" : ""}">
693
+ ${this.selectingConversations ? `<input class="po-thread-checkbox" type="checkbox" data-select-thread="${l(o.id)}" aria-label="选择会话:${l(o.title)}" ${this.selectedConversationIds.has(o.id) ? "checked" : ""} ${this.bulkDeleting ? "disabled" : ""} />` : ""}
694
+ <button class="po-thread" data-thread="${l(o.id)}" ${this.asking || this.bulkDeleting ? "disabled" : ""}>
650
695
  <strong>${l(o.title)}</strong><span>${O(o.created_at)}</span>
651
696
  </button>
652
- <button class="po-thread-delete" type="button" data-delete-thread="${l(o.id)}" aria-label="删除会话:${l(o.title)}" title="删除会话" ${this.asking || this.deletingThreadId === o.id ? "disabled" : ""}><span class="po-trash-icon" aria-hidden="true"></span></button>
697
+ ${this.selectingConversations ? "" : `<button class="po-thread-delete" type="button" data-delete-thread="${l(o.id)}" aria-label="删除会话:${l(o.title)}" title="删除会话" ${this.asking || this.deletingThreadId === o.id ? "disabled" : ""}><span class="po-trash-icon" aria-hidden="true"></span></button>`}
653
698
  </div>`;
654
699
  }).join("")}</div>
655
700
  </aside>
@@ -662,7 +707,7 @@ class V {
662
707
  <form class="po-composer"><textarea ${c ? "disabled" : ""} placeholder="${l(n)}"></textarea>${p}</form>
663
708
  </main>
664
709
  <aside class="po-trace"><header><h2>执行过程</h2><button data-action="close">×</button></header><section>${this.renderSourceUploader()}</section></aside>
665
- `, this.bindPanelEvents(), e) {
710
+ `, this.bindPanelEvents(), listScroll !== void 0 && (this.panel.querySelector(".po-thread-list").scrollTop = listScroll), e) {
666
711
  const o = this.panel.querySelector(".po-messages");
667
712
  o && (o.scrollTop = o.scrollHeight);
668
713
  } else if (s !== void 0) {
@@ -773,7 +818,23 @@ class V {
773
818
  }), (y = this.panel) == null || y.querySelectorAll("[data-thread]").forEach((h) => {
774
819
  h.addEventListener("click", () => {
775
820
  const d = this.conversations.find((u) => u.id === h.dataset.thread);
776
- d && this.selectThread(d);
821
+ if (!d) return;
822
+ if (this.selectingConversations) {
823
+ this.selectedConversationIds.has(d.id) ? this.selectedConversationIds.delete(d.id) : this.selectedConversationIds.add(d.id);
824
+ this.renderPanel(!1);
825
+ } else this.selectThread(d);
826
+ });
827
+ }), this.panel.querySelector('[data-action="select-threads"]')?.addEventListener("click", () => {
828
+ this.selectingConversations = !0, this.selectedConversationIds.clear(), this.conversationError = "", this.renderPanel(!1);
829
+ }), this.panel.querySelector('[data-action="cancel-thread-selection"]')?.addEventListener("click", () => {
830
+ this.selectingConversations = !1, this.selectedConversationIds.clear(), this.conversationError = "", this.renderPanel(!1);
831
+ }), this.panel.querySelector('[data-action="select-all-threads"]')?.addEventListener("change", (h) => {
832
+ this.selectedConversationIds = h.target.checked ? new Set(this.conversations.map((d) => d.id)) : /* @__PURE__ */ new Set();
833
+ this.renderPanel(!1);
834
+ }), this.panel.querySelector('[data-action="delete-selected-threads"]')?.addEventListener("click", () => void this.deleteSelectedConversations()), this.panel.querySelectorAll("[data-select-thread]").forEach((h) => {
835
+ h.addEventListener("change", () => {
836
+ h.checked ? this.selectedConversationIds.add(h.dataset.selectThread) : this.selectedConversationIds.delete(h.dataset.selectThread);
837
+ this.renderPanel(!1);
777
838
  });
778
839
  }), (w = this.panel) == null || w.querySelectorAll("[data-delete-thread]").forEach((h) => {
779
840
  h.addEventListener("click", (d) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageops/sdk",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "PageOps embeddable frontend SDK",
5
5
  "type": "module",
6
6
  "main": "dist/pageops-sdk.umd.cjs",