@cloud411716/fancy-webnovel 1.0.13 → 1.0.15

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.
@@ -16,6 +16,42 @@ import * as qimao from './platforms/qimao.js';
16
16
 
17
17
  // inject is declared in the root index.js; only apply() is imported by index.js
18
18
 
19
+ /**
20
+ * StatusLine — appends a single session event and updates it in-place on
21
+ * subsequent calls using session's surfaceOp replace mechanism.
22
+ *
23
+ * The event always stays at the same log position; only its content changes.
24
+ * This gives the appearance of a terminal "status bar" that doesn't scroll
25
+ * away or get buried under other messages.
26
+ */
27
+ class StatusLine {
28
+ #session;
29
+ #seq; // event sequence of the status line (set after first append)
30
+ #text = '';
31
+
32
+ constructor(session) { this.#session = session; }
33
+
34
+ /** Update the status line content. First call creates the event; subsequent
35
+ * calls replace it in-place. */
36
+ update(text) {
37
+ const event = this.#seq
38
+ ? this.#session.append(
39
+ 'fancy/status',
40
+ { content: [{ type: 'text', text }], source: { kind: 'user' } },
41
+ { surfaceOp: { op: 'replace', start: this.#seq, end: this.#seq } }
42
+ )
43
+ : this.#session.append(
44
+ 'fancy/status',
45
+ { content: [{ type: 'text', text }], source: { kind: 'user' } },
46
+ { surfaceOp: 'append' }
47
+ );
48
+ this.#seq ??= event.seq;
49
+ this.#text = text;
50
+ }
51
+
52
+ clear() { this.update(''); }
53
+ }
54
+
19
55
  const PLATFORM_TABLE = [
20
56
  ['1', 'qidian', '起点'],
21
57
  ['2', 'fanqie', '番茄'],
@@ -196,18 +232,19 @@ export function apply(ctx) {
196
232
  // ---------- in-process scraping (no subprocess) ----------
197
233
 
198
234
  // Register scan-progress listeners before scraping starts, clean up after.
199
- // Each fired event sends an ASCII spinner followup so the user sees live
200
- // progress instead of a blank screen while the scraper runs.
235
+ // StatusLine.update() uses session surfaceOp replace so the status stays
236
+ // at the same log position and doesn't accumulate or scroll away.
201
237
  const disposers = [];
238
+ const statusLine = new StatusLine(session);
202
239
  let currentRankLabel = '';
203
240
 
204
241
  disposers.push(ctx.events.on('fancy/scan:start', (_ctx, platform, _channel, _type, label) => {
205
242
  currentRankLabel = label;
206
- llmFollowup(invocation, `[${SPINNER[_spinnerIdx++ % 4]}] 正在采集:${platformLabel(platform)} ${label} …`);
243
+ statusLine.update(`[${SPINNER[_spinnerIdx++ % 4]}] 正在采集:${platformLabel(platform)} ${label} …`);
207
244
  }));
208
245
 
209
246
  disposers.push(ctx.events.on('fancy/scan:rank-done', (_ctx, _platform, _channel, _type, ok, written) => {
210
- llmFollowup(invocation, `[${SPINNER[_spinnerIdx++ % 4]}] ${currentRankLabel} ${ok ? `✓ 写入 ${written} 条` : '✗ 失败'}`);
247
+ statusLine.update(`[${SPINNER[_spinnerIdx++ % 4]}] ${currentRankLabel} ${ok ? `✓ 写入 ${written} 条` : '✗ 失败'}`);
211
248
  }));
212
249
 
213
250
  const { totalBooks, totalFiles, failedRanks, lastReceipt } = await runScans(
@@ -217,8 +254,9 @@ export function apply(ctx) {
217
254
  signal,
218
255
  );
219
256
 
220
- // Clean up progress listeners; runScans is done.
257
+ // Clean up progress listeners and clear the status bar.
221
258
  for (const d of disposers) d();
259
+ statusLine.clear();
222
260
 
223
261
  // ---------- output results ----------
224
262
  if (lastReceipt && totalFiles > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "exports": {