@i14ks/ccv 0.16.2 → 0.16.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.
Files changed (26) hide show
  1. package/bundle/ccv.mjs +87 -6
  2. package/package.json +1 -1
  3. package/web/assets/{codemirror-CMssNlj1.js → codemirror-CkgU8c7a.js} +1 -1
  4. package/web/assets/{index-k2hhaMZJ.js → index--DhZrg25.js} +1 -1
  5. package/web/assets/{index-DMeJ3xy0.js → index-B0NcQgiZ.js} +1 -1
  6. package/web/assets/{index-BpOx8-W2.js → index-BC7TmL8M.js} +1 -1
  7. package/web/assets/{index-CrUrevEN.js → index-BFwSwibX.js} +1 -1
  8. package/web/assets/{index-HnPp_JMB.js → index-Bhx7H3mC.js} +1 -1
  9. package/web/assets/{index-DhS8IelO.js → index-CNQ0551W.js} +1 -1
  10. package/web/assets/{index-DPVHF0JV.js → index-CQqNgFvZ.js} +1 -1
  11. package/web/assets/{index-CMsLxeb-.js → index-CstcrxY-.js} +1 -1
  12. package/web/assets/{index-hzfPIc5t.js → index-DHVISfG-.js} +1 -1
  13. package/web/assets/{index-DbZeX0xl.js → index-DaRnRpPz.js} +1 -1
  14. package/web/assets/{index-BbaEOHHE.js → index-DfGVckBo.js} +1 -1
  15. package/web/assets/{index-BDlE7XrJ.js → index-DlOju0OY.js} +65 -65
  16. package/web/assets/{index-4OYhLb6v.js → index-DoqmNDN4.js} +1 -1
  17. package/web/assets/{index-B20zmSkd.js → index-Dv0cw76Z.js} +1 -1
  18. package/web/assets/{index-kRQFCBLi.js → index-DvhOZOas.js} +1 -1
  19. package/web/assets/{index-umqVrxlG.js → index-GT_NZ0qK.js} +1 -1
  20. package/web/assets/{index-PkTbpJ8b.js → index-OuSuAczZ.js} +1 -1
  21. package/web/assets/{index-B8wBlTJK.js → index-P6m4z84O.js} +1 -1
  22. package/web/assets/{index-DyY1FCgf.js → index-gmvISUqO.js} +1 -1
  23. package/web/assets/{index-CMpPRV85.js → index-s7Wcfj5F.js} +1 -1
  24. package/web/assets/index-tvzk50nQ.css +1 -0
  25. package/web/index.html +2 -2
  26. package/web/assets/index-CPM-bl8z.css +0 -1
package/bundle/ccv.mjs CHANGED
@@ -5344,6 +5344,16 @@ import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile8 } from
5344
5344
  import path14 from "node:path";
5345
5345
  var SessionPins = class {
5346
5346
  #pinned = /* @__PURE__ */ new Map();
5347
+ /**
5348
+ * Куда закрепление ушло с id, под которым его больше нет.
5349
+ *
5350
+ * Закрепление — место одной линии работы, а не одной сессии: оно переезжает
5351
+ * на дисковый id, на id после `/clear`, на сессию, продолженную из
5352
+ * хэндоффа. Хэндофф же остаётся у сессии, с которой оно давно ушло, и
5353
+ * продолжить из него могут позже — без следа хост не нашёл бы, у кого
5354
+ * закрепление сейчас, и новая сессия осталась бы вне панели.
5355
+ */
5356
+ #moved = /* @__PURE__ */ new Map();
5347
5357
  #storePath;
5348
5358
  constructor(dataDir) {
5349
5359
  this.#storePath = path14.join(dataDir, "session-pins.json");
@@ -5353,8 +5363,10 @@ var SessionPins = class {
5353
5363
  const raw = await readFile9(this.#storePath, "utf8");
5354
5364
  const parsed = JSON.parse(raw);
5355
5365
  this.#pinned = new Map(Object.entries(parsed.pinned ?? {}).filter(([, r]) => isRecord2(r)));
5366
+ this.#moved = new Map(Object.entries(parsed.moved ?? {}).filter(([, to]) => typeof to === "string"));
5356
5367
  } catch {
5357
5368
  this.#pinned = /* @__PURE__ */ new Map();
5369
+ this.#moved = /* @__PURE__ */ new Map();
5358
5370
  }
5359
5371
  }
5360
5372
  find(sessionId) {
@@ -5363,6 +5375,22 @@ var SessionPins = class {
5363
5375
  has(sessionId) {
5364
5376
  return this.#pinned.has(sessionId);
5365
5377
  }
5378
+ /**
5379
+ * У кого сейчас закрепление, когда-то бывшее на `sessionId`: сам этот id,
5380
+ * если оно ещё на нём, иначе — конец следа переездов. `undefined` — оно
5381
+ * никуда не вело или его с тех пор сняли.
5382
+ */
5383
+ holder(sessionId) {
5384
+ const seen = /* @__PURE__ */ new Set();
5385
+ let current = sessionId;
5386
+ while (current !== void 0 && !seen.has(current)) {
5387
+ if (this.#pinned.has(current))
5388
+ return current;
5389
+ seen.add(current);
5390
+ current = this.#moved.get(current);
5391
+ }
5392
+ return void 0;
5393
+ }
5366
5394
  /** Проекты, в которых есть хоть что-то закреплённое, — по ним собирается панель. */
5367
5395
  workspaceIds() {
5368
5396
  return [...new Set([...this.#pinned.values()].map((r) => r.workspaceId))];
@@ -5406,6 +5434,7 @@ var SessionPins = class {
5406
5434
  this.#pinned.delete(fromId);
5407
5435
  if (!this.#pinned.has(toId))
5408
5436
  this.#pinned.set(toId, record);
5437
+ this.#moved.set(fromId, toId);
5409
5438
  await this.#persist();
5410
5439
  return true;
5411
5440
  }
@@ -5418,6 +5447,10 @@ var SessionPins = class {
5418
5447
  * иначе прежняя так и останется в панели. Время берётся у первой найденной
5419
5448
  * записи — новая встаёт ровно на место прежней, а не в конец панели.
5420
5449
  * Возвращает `true`, если прежняя была закреплена.
5450
+ *
5451
+ * Закрепление могло уйти с прежней дальше (см. `holder`) — тогда в
5452
+ * `fromIds` называют и нынешнего держателя: след с каждого из них теперь
5453
+ * ведёт на `toId`.
5421
5454
  */
5422
5455
  async transfer(fromIds, toId) {
5423
5456
  let record;
@@ -5428,6 +5461,9 @@ var SessionPins = class {
5428
5461
  if (!record)
5429
5462
  return false;
5430
5463
  this.#pinned.set(toId, record);
5464
+ for (const id of fromIds)
5465
+ if (id !== toId)
5466
+ this.#moved.set(id, toId);
5431
5467
  await this.#persist();
5432
5468
  return true;
5433
5469
  }
@@ -5451,7 +5487,14 @@ var SessionPins = class {
5451
5487
  return changed;
5452
5488
  }
5453
5489
  async #persist() {
5454
- const file = { pinned: Object.fromEntries(this.#pinned) };
5490
+ for (const id of [...this.#moved.keys()]) {
5491
+ if (this.holder(id) === void 0)
5492
+ this.#moved.delete(id);
5493
+ }
5494
+ const file = {
5495
+ pinned: Object.fromEntries(this.#pinned),
5496
+ ...this.#moved.size > 0 ? { moved: Object.fromEntries(this.#moved) } : {}
5497
+ };
5455
5498
  await mkdir7(path14.dirname(this.#storePath), { recursive: true });
5456
5499
  await writeFile8(this.#storePath, JSON.stringify(file, null, 2), "utf8");
5457
5500
  }
@@ -8517,8 +8560,30 @@ var HostServer = class {
8517
8560
  session.start();
8518
8561
  this.#sendLive();
8519
8562
  this.#send(socket, { type: "session.summary", session: session.summary() });
8520
- if (frame.continuesFrom)
8521
- await this.#carryPin(workspace.id, frame.continuesFrom, session.id);
8563
+ if (!frame.continuesFrom)
8564
+ return;
8565
+ await this.#carryPin(workspace.id, frame.continuesFrom, session.id);
8566
+ if (frame.fromHandoff)
8567
+ await this.#markHandoffContinued(frame.continuesFrom);
8568
+ }
8569
+ /**
8570
+ * Отметить хэндофф продолженным — и сказать об этом всем окнам. Он остаётся
8571
+ * лежать у прежней сессии, пока его не скроют, и без отметки её баннер звал
8572
+ * бы продолжить ещё раз так же настойчиво, как в первый: уже с места, с
8573
+ * которого работа давно ушла.
8574
+ */
8575
+ async #markHandoffContinued(sessionId) {
8576
+ const handoff = await this.#handoffs.get(sessionId);
8577
+ if (!handoff)
8578
+ return;
8579
+ const continued = { ...handoff, continuedAt: Date.now() };
8580
+ try {
8581
+ await this.#handoffs.save(continued);
8582
+ } catch (error) {
8583
+ console.error("[handoff]", error instanceof Error ? error.message : String(error));
8584
+ return;
8585
+ }
8586
+ this.#broadcast({ type: "handoff.state", sessionId, handoff: continued });
8522
8587
  }
8523
8588
  /**
8524
8589
  * «Продолжить в новой сессии» из хэндоффа: закрепление прежней сессии
@@ -8530,13 +8595,29 @@ var HostServer = class {
8530
8595
  * на него запись переедет сама, как у любой свежей закреплённой (см.
8531
8596
  * `#wire`). У прежней снимаем оба id — она может быть ещё жива, и тогда
8532
8597
  * закрепление записано и под её uuid (см. `case 'session.pin'`).
8598
+ *
8599
+ * Закрепление могло уйти с прежней раньше — её уже продолжали, и хэндофф с
8600
+ * тех пор лежит у неё без дела. Тогда оно у потомка той же линии работы, и
8601
+ * новая сессия занимает место у него (см. `SessionPins.holder`): иначе
8602
+ * продолжение из старого хэндоффа оставляло бы в панели прежнюю строку, а
8603
+ * новую — вне её.
8533
8604
  */
8534
8605
  async #carryPin(workspaceId, fromId, toId) {
8535
- const fromIds = [fromId];
8606
+ const ids = /* @__PURE__ */ new Set([fromId]);
8607
+ const holder = this.#pins.holder(fromId);
8608
+ if (holder && holder !== toId)
8609
+ ids.add(holder);
8536
8610
  for (const live of this.#sessions.values()) {
8537
- if (live.id !== toId && live.claudeSessionId === fromId)
8538
- fromIds.push(live.id);
8611
+ if (live.id === toId)
8612
+ continue;
8613
+ const diskId = live.claudeSessionId;
8614
+ if (!ids.has(live.id) && !(diskId && ids.has(diskId)))
8615
+ continue;
8616
+ ids.add(live.id);
8617
+ if (diskId)
8618
+ ids.add(diskId);
8539
8619
  }
8620
+ const fromIds = [...ids];
8540
8621
  if (!await this.#pins.transfer(fromIds, toId))
8541
8622
  return;
8542
8623
  for (const id of fromIds)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i14ks/ccv",
3
- "version": "0.16.2",
3
+ "version": "0.16.3",
4
4
  "license": "UNLICENSED",
5
5
  "description": "GUI для Claude Code: сессии агента в каталогах проектов, дерево файлов и редактор рядом с разговором.",
6
6
  "homepage": "https://claudevisual.com",
@@ -1 +1 @@
1
- import{s as r,H as a}from"./index-hzfPIc5t.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-BDlE7XrJ.js";import"./index-BHot-lnj.js";const m=e.theme({"&":{height:"100%",color:"var(--text)",backgroundColor:"var(--bg)",fontSize:"13px"},".cm-scroller":{fontFamily:"var(--mono)",lineHeight:"1.6"},".cm-content":{caretColor:"var(--accent)"},".cm-cursor, .cm-dropCursor":{borderLeftColor:"var(--accent)"},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"var(--cm-selection)"},".cm-gutters":{backgroundColor:"var(--bg)",color:"var(--muted)",borderRight:"1px solid var(--border-soft)"},".cm-activeLineGutter":{backgroundColor:"var(--surface-2)",color:"var(--text-2)"},".cm-activeLine":{backgroundColor:"var(--cm-active)"},".cm-selectionMatch":{backgroundColor:"var(--cm-match)"},"&.cm-focused .cm-matchingBracket":{backgroundColor:"var(--cm-match)",outline:"1px solid var(--accent-line)"},".cm-foldPlaceholder":{backgroundColor:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--muted)"},".cm-panels":{backgroundColor:"var(--surface)",color:"var(--text)",border:"1px solid var(--border)"},".cm-panels input, .cm-panels button, .cm-panels select":{fontFamily:"var(--sans)",fontSize:"12px",background:"var(--surface-2)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:"6px",padding:"3px 6px"},".cm-searchMatch":{backgroundColor:"var(--cm-match)"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"var(--accent-soft)"},".cm-tooltip":{background:"var(--surface)",border:"1px solid var(--border)",borderRadius:"8px",boxShadow:"var(--shadow-md)",color:"var(--text)"},".cm-tooltip-autocomplete ul li[aria-selected]":{background:"var(--surface-2)",color:"var(--text)"}}),i=r(a.define([{tag:[o.keyword,o.modifier,o.controlKeyword,o.moduleKeyword],color:"var(--cm-key)"},{tag:[o.name,o.deleted,o.character,o.macroName],color:"var(--text)"},{tag:[o.propertyName],color:"var(--cm-prop)"},{tag:[o.function(o.variableName),o.labelName],color:"var(--cm-fn)"},{tag:[o.color,o.constant(o.name),o.standard(o.name),o.bool],color:"var(--cm-const)"},{tag:[o.definition(o.name),o.separator],color:"var(--text)"},{tag:[o.typeName,o.className,o.changed,o.annotation,o.self,o.namespace],color:"var(--cm-type)"},{tag:[o.number,o.integer,o.float],color:"var(--cm-num)"},{tag:[o.string,o.special(o.string),o.regexp],color:"var(--cm-str)"},{tag:[o.operator,o.operatorKeyword,o.escape],color:"var(--cm-op)"},{tag:[o.meta,o.comment],color:"var(--cm-comment)",fontStyle:"italic"},{tag:o.strong,fontWeight:"650"},{tag:o.emphasis,fontStyle:"italic"},{tag:o.strikethrough,textDecoration:"line-through"},{tag:o.link,color:"var(--cm-str)",textDecoration:"underline"},{tag:o.heading,fontWeight:"650",color:"var(--cm-key)"},{tag:o.invalid,color:"var(--danger)"}]));export{i as editorHighlight,m as editorTheme};
1
+ import{s as r,H as a}from"./index-DHVISfG-.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-DlOju0OY.js";import"./index-BHot-lnj.js";const m=e.theme({"&":{height:"100%",color:"var(--text)",backgroundColor:"var(--bg)",fontSize:"13px"},".cm-scroller":{fontFamily:"var(--mono)",lineHeight:"1.6"},".cm-content":{caretColor:"var(--accent)"},".cm-cursor, .cm-dropCursor":{borderLeftColor:"var(--accent)"},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"var(--cm-selection)"},".cm-gutters":{backgroundColor:"var(--bg)",color:"var(--muted)",borderRight:"1px solid var(--border-soft)"},".cm-activeLineGutter":{backgroundColor:"var(--surface-2)",color:"var(--text-2)"},".cm-activeLine":{backgroundColor:"var(--cm-active)"},".cm-selectionMatch":{backgroundColor:"var(--cm-match)"},"&.cm-focused .cm-matchingBracket":{backgroundColor:"var(--cm-match)",outline:"1px solid var(--accent-line)"},".cm-foldPlaceholder":{backgroundColor:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--muted)"},".cm-panels":{backgroundColor:"var(--surface)",color:"var(--text)",border:"1px solid var(--border)"},".cm-panels input, .cm-panels button, .cm-panels select":{fontFamily:"var(--sans)",fontSize:"12px",background:"var(--surface-2)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:"6px",padding:"3px 6px"},".cm-searchMatch":{backgroundColor:"var(--cm-match)"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"var(--accent-soft)"},".cm-tooltip":{background:"var(--surface)",border:"1px solid var(--border)",borderRadius:"8px",boxShadow:"var(--shadow-md)",color:"var(--text)"},".cm-tooltip-autocomplete ul li[aria-selected]":{background:"var(--surface-2)",color:"var(--text)"}}),i=r(a.define([{tag:[o.keyword,o.modifier,o.controlKeyword,o.moduleKeyword],color:"var(--cm-key)"},{tag:[o.name,o.deleted,o.character,o.macroName],color:"var(--text)"},{tag:[o.propertyName],color:"var(--cm-prop)"},{tag:[o.function(o.variableName),o.labelName],color:"var(--cm-fn)"},{tag:[o.color,o.constant(o.name),o.standard(o.name),o.bool],color:"var(--cm-const)"},{tag:[o.definition(o.name),o.separator],color:"var(--text)"},{tag:[o.typeName,o.className,o.changed,o.annotation,o.self,o.namespace],color:"var(--cm-type)"},{tag:[o.number,o.integer,o.float],color:"var(--cm-num)"},{tag:[o.string,o.special(o.string),o.regexp],color:"var(--cm-str)"},{tag:[o.operator,o.operatorKeyword,o.escape],color:"var(--cm-op)"},{tag:[o.meta,o.comment],color:"var(--cm-comment)",fontStyle:"italic"},{tag:o.strong,fontWeight:"650"},{tag:o.emphasis,fontStyle:"italic"},{tag:o.strikethrough,textDecoration:"line-through"},{tag:o.link,color:"var(--cm-str)",textDecoration:"underline"},{tag:o.heading,fontWeight:"650",color:"var(--cm-key)"},{tag:o.invalid,color:"var(--danger)"}]));export{i as editorHighlight,m as editorTheme};