@i14ks/ccv 0.15.3 → 0.16.0
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/bundle/ccv.mjs +277 -33
- package/package.json +50 -50
- package/web/assets/{codemirror-Dg4vvH8i.js → codemirror-XwUmM20V.js} +1 -1
- package/web/assets/{index-DB0qYzJa.js → index-B3KPQ0Jq.js} +1 -1
- package/web/assets/{index-CTrrHYKx.js → index-B3X6gKS4.js} +1 -1
- package/web/assets/{index-D04xl5g2.js → index-B3ZG7Mgh.js} +1 -1
- package/web/assets/{index-DMpA08Ie.js → index-B3mW0ScK.js} +1 -1
- package/web/assets/{index-Bpw3TKRV.js → index-BAMLmkAm.js} +1 -1
- package/web/assets/{index-Bg6okyOc.js → index-BBdH0WyS.js} +1 -1
- package/web/assets/{index-CM3XoNnk.js → index-BCeVhzZf.js} +1 -1
- package/web/assets/{index-0iOvZWuA.js → index-BiGxRIQL.js} +1 -1
- package/web/assets/{index-CRyJJS40.js → index-Bjmeb0mL.js} +1 -1
- package/web/assets/{index-B96oAtZO.js → index-C3lLfrzk.js} +1 -1
- package/web/assets/index-CPM-bl8z.css +1 -0
- package/web/assets/{index-ZEjcSNDk.js → index-CWJg4zA3.js} +1 -1
- package/web/assets/{index-CanbdA5V.js → index-CXp-N-a3.js} +1 -1
- package/web/assets/{index-kBFqQTK1.js → index-CdFloc-n.js} +1 -1
- package/web/assets/{index-LHO2W5bU.js → index-Ceqv0JQi.js} +1 -1
- package/web/assets/{index-SxQ8qp4S.js → index-Cu0rli8K.js} +1 -1
- package/web/assets/{index-CrC2iAsw.js → index-DaN46jfw.js} +1 -1
- package/web/assets/{index-CB6_9alP.js → index-EhM5ESQH.js} +1 -1
- package/web/assets/{index-CzbpsO2-.js → index-PcpAtAyR.js} +1 -1
- package/web/assets/index-_rsPujXt.js +134 -0
- package/web/assets/{index-CNi4N4PL.js → index-byQstcIo.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-Bo84aMH8.js +0 -134
- package/web/assets/index-COiPHc3p.css +0 -1
package/bundle/ccv.mjs
CHANGED
|
@@ -1112,6 +1112,8 @@ var USAGE_MIN_INTERVAL_MS = 2e4;
|
|
|
1112
1112
|
var HANDOFF_TOOL_DENIAL = "\u041B\u0438\u043C\u0438\u0442 \u043F\u043B\u0430\u043D\u0430 \u043F\u043E\u0447\u0442\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D, \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u044B. \u041E\u0442\u0432\u0435\u0442\u044C \u0445\u044D\u043D\u0434\u043E\u0444\u0444\u043E\u043C \u0442\u0435\u043A\u0441\u0442\u043E\u043C, \u0431\u0435\u0437 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432.";
|
|
1113
1113
|
var USAGE_TURN_INTERVAL_MS = 3e4;
|
|
1114
1114
|
var INTERRUPT_SETTLE_MS = 15e3;
|
|
1115
|
+
var TASK_STOP_TIMEOUT_MS = 5e3;
|
|
1116
|
+
var TASK_WAKE_MS = 5e3;
|
|
1115
1117
|
var CONTEXT_MIN_INTERVAL_MS = 5e3;
|
|
1116
1118
|
var ClaudeSession = class extends EventEmitter {
|
|
1117
1119
|
id;
|
|
@@ -1176,6 +1178,39 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1176
1178
|
* `#setState` делает это сам, см. там же.
|
|
1177
1179
|
*/
|
|
1178
1180
|
#backgroundWork = /* @__PURE__ */ new Set();
|
|
1181
|
+
/**
|
|
1182
|
+
* Запущенные задачи основного разговора — по `system/task_*` (см.
|
|
1183
|
+
* `SessionTask`), в порядке запуска.
|
|
1184
|
+
*
|
|
1185
|
+
* Края (`task_started` / `task_notification`) могут потеряться, поэтому
|
|
1186
|
+
* список ещё и подрезается: по `background_tasks_changed` уходят фоновые,
|
|
1187
|
+
* которых нет в уровне, по `result` основного хода — задачи переднего
|
|
1188
|
+
* плана, а перезапуск процесса CLI обнуляет его целиком.
|
|
1189
|
+
*/
|
|
1190
|
+
#tasks = /* @__PURE__ */ new Map();
|
|
1191
|
+
/**
|
|
1192
|
+
* Живые фоновые задачи по `background_tasks_changed` — уровень, а не края:
|
|
1193
|
+
* каждое событие заменяет набор целиком. В отличие от `#backgroundWork`,
|
|
1194
|
+
* новый ход его не трогает — фоновый агент переживает любой ход.
|
|
1195
|
+
*
|
|
1196
|
+
* `null` — этот процесс CLI уровня ещё не присылал (или не умеет вовсе);
|
|
1197
|
+
* тогда о фоновой работе судит `#backgroundWork`.
|
|
1198
|
+
*/
|
|
1199
|
+
#backgroundLevel = null;
|
|
1200
|
+
/**
|
|
1201
|
+
* toolUseId вызовов, сделанных сабагентами. `owned_by_subagent` CLI ставит
|
|
1202
|
+
* не всем их задачам — команда агента на переднем плане приходит без него, —
|
|
1203
|
+
* поэтому вложенность узнаём ещё и по тому, чей вызов задачу запустил.
|
|
1204
|
+
*/
|
|
1205
|
+
#subagentToolUses = /* @__PURE__ */ new Set();
|
|
1206
|
+
/** Отложенная рассылка сводки по `task_progress` — он приходит на каждый инструмент агента. */
|
|
1207
|
+
#taskSummaryTimer = null;
|
|
1208
|
+
/**
|
|
1209
|
+
* Когда сессия в последний раз работала — шла ход или крутились задачи.
|
|
1210
|
+
* Сторожу лимита: порог мог перейти между опросами, и сессия, которая
|
|
1211
|
+
* только что его перешла, к моменту проверки уже может простаивать.
|
|
1212
|
+
*/
|
|
1213
|
+
#lastActiveAt = 0;
|
|
1179
1214
|
#capabilities = null;
|
|
1180
1215
|
#pendingPermissions = /* @__PURE__ */ new Map();
|
|
1181
1216
|
#model = null;
|
|
@@ -1342,7 +1377,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1342
1377
|
title: this.#title,
|
|
1343
1378
|
state: this.#state,
|
|
1344
1379
|
lastTurn: this.#lastTurn,
|
|
1345
|
-
backgroundWork: this.#backgroundWork.size > 0,
|
|
1380
|
+
backgroundWork: this.#backgroundLevel ? this.#backgroundLevel.size > 0 : this.#backgroundWork.size > 0,
|
|
1381
|
+
// Команда на переднем плане — это просто идущий вызов инструмента, он и
|
|
1382
|
+
// так виден в ленте; задачей она становится, только уйдя в фон.
|
|
1383
|
+
tasks: [...this.#tasks.values()].filter((t) => t.kind !== "shell" || t.background),
|
|
1346
1384
|
model: this.#model,
|
|
1347
1385
|
permissionMode: this.#permissionMode,
|
|
1348
1386
|
effort: this.#effort,
|
|
@@ -1445,6 +1483,7 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1445
1483
|
options2.allowedTools = READ_ONLY_TOOLS;
|
|
1446
1484
|
options2.strictMcpConfig = true;
|
|
1447
1485
|
}
|
|
1486
|
+
this.#resetTasks();
|
|
1448
1487
|
const q = query({ prompt: this.#inbox, options: options2 });
|
|
1449
1488
|
this.#query = q;
|
|
1450
1489
|
void this.#consume(q);
|
|
@@ -1565,6 +1604,7 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1565
1604
|
} catch {
|
|
1566
1605
|
}
|
|
1567
1606
|
this.#query = null;
|
|
1607
|
+
this.#resetTasks();
|
|
1568
1608
|
}
|
|
1569
1609
|
// -------------------------------------------------------------------------
|
|
1570
1610
|
// Переподключение
|
|
@@ -1621,8 +1661,8 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1621
1661
|
if (this.#closing || this.#reconnectTimer)
|
|
1622
1662
|
return;
|
|
1623
1663
|
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1624
|
-
const
|
|
1625
|
-
if (
|
|
1664
|
+
const delay2 = RECONNECT_DELAYS_MS[this.#reconnectAttempt];
|
|
1665
|
+
if (delay2 === void 0) {
|
|
1626
1666
|
this.#pendingRetry = [];
|
|
1627
1667
|
this.#record({
|
|
1628
1668
|
kind: "error",
|
|
@@ -1634,13 +1674,13 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1634
1674
|
this.#reconnectAttempt += 1;
|
|
1635
1675
|
this.#record({
|
|
1636
1676
|
kind: "error",
|
|
1637
|
-
message: `\u0421\u0432\u044F\u0437\u044C \u0441 Claude \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430: ${reason}. \u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 ${Math.round(
|
|
1677
|
+
message: `\u0421\u0432\u044F\u0437\u044C \u0441 Claude \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430: ${reason}. \u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 ${Math.round(delay2 / 1e3)} \u0441 (\u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${this.#reconnectAttempt} \u0438\u0437 ${RECONNECT_DELAYS_MS.length}).`
|
|
1638
1678
|
});
|
|
1639
1679
|
this.#setState("reconnecting");
|
|
1640
1680
|
this.#reconnectTimer = setTimeout(() => {
|
|
1641
1681
|
this.#reconnectTimer = null;
|
|
1642
1682
|
this.#restart(reason);
|
|
1643
|
-
},
|
|
1683
|
+
}, delay2);
|
|
1644
1684
|
this.#reconnectTimer.unref?.();
|
|
1645
1685
|
}
|
|
1646
1686
|
// -------------------------------------------------------------------------
|
|
@@ -1782,23 +1822,52 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1782
1822
|
this.#interruptRequested = true;
|
|
1783
1823
|
await this.#query?.interrupt();
|
|
1784
1824
|
}
|
|
1825
|
+
/** Когда сессия в последний раз работала (см. `#lastActiveAt`); идущий ход — сейчас. */
|
|
1826
|
+
get lastActiveAt() {
|
|
1827
|
+
return BUSY_STATES.has(this.#state) || this.#tasks.size > 0 ? Date.now() : this.#lastActiveAt;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Остановить одну задачу. Из списка она уйдёт по уведомлению CLI
|
|
1831
|
+
* (`task_notification` со `stopped`), а не здесь: не остановилась — пусть
|
|
1832
|
+
* и видно будет, что работает.
|
|
1833
|
+
*/
|
|
1834
|
+
async stopTask(taskId) {
|
|
1835
|
+
await this.#query?.stopTask(taskId);
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Остановить все задачи сессии — и из списка, и из уровня фоновых (там же
|
|
1839
|
+
* и вложенные). `interrupt()` фоновых агентов не трогает: они переживают
|
|
1840
|
+
* ход. `true` — было что останавливать.
|
|
1841
|
+
*/
|
|
1842
|
+
async stopTasks() {
|
|
1843
|
+
const q = this.#query;
|
|
1844
|
+
const ids = /* @__PURE__ */ new Set([...this.#tasks.keys(), ...this.#backgroundLevel ?? []]);
|
|
1845
|
+
if (!q || ids.size === 0)
|
|
1846
|
+
return false;
|
|
1847
|
+
await Promise.all([...ids].map((id) => Promise.race([q.stopTask(id), delay(TASK_STOP_TIMEOUT_MS)]).catch(() => void 0)));
|
|
1848
|
+
return true;
|
|
1849
|
+
}
|
|
1785
1850
|
/**
|
|
1786
|
-
* Ход
|
|
1787
|
-
* отправить служебный `prompt` с
|
|
1788
|
-
* текст ответа. `null` — хода не
|
|
1789
|
-
* кончился за `timeoutMs
|
|
1851
|
+
* Ход хэндоффа: остановить задачи сессии, прервать текущий ход (вместе с
|
|
1852
|
+
* субагентами переднего плана), затем отправить служебный `prompt` с
|
|
1853
|
+
* запретом на любые инструменты и вернуть текст ответа. `null` — хода не
|
|
1854
|
+
* вышло: ошибка, пустой ответ, ход не кончился за `timeoutMs` или `prompt`
|
|
1855
|
+
* не задан (спрашивать агента бессмысленно — например, окно уже исчерпано);
|
|
1856
|
+
* тогда хэндофф собирают из журнала.
|
|
1790
1857
|
*/
|
|
1791
1858
|
async runHandoff(prompt, timeoutMs) {
|
|
1792
1859
|
if (this.#closing)
|
|
1793
1860
|
return null;
|
|
1794
1861
|
this.#handoffMode = true;
|
|
1795
1862
|
try {
|
|
1863
|
+
if (await this.stopTasks())
|
|
1864
|
+
await this.#waitForState("running", TASK_WAKE_MS);
|
|
1796
1865
|
if (this.#state === "running" || this.#state === "waiting_permission") {
|
|
1797
1866
|
const settled = this.#nextTurnEnd(INTERRUPT_SETTLE_MS);
|
|
1798
1867
|
await this.interrupt().catch(() => void 0);
|
|
1799
1868
|
await settled;
|
|
1800
1869
|
}
|
|
1801
|
-
if (this.#closing || !this.#query || this.#state !== "idle")
|
|
1870
|
+
if (!prompt || this.#closing || !this.#query || this.#state !== "idle")
|
|
1802
1871
|
return null;
|
|
1803
1872
|
const reply = this.#nextTurnEnd(timeoutMs);
|
|
1804
1873
|
this.sendPrompt(prompt, void 0, true);
|
|
@@ -1816,6 +1885,25 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1816
1885
|
this.#handoffMode = false;
|
|
1817
1886
|
}
|
|
1818
1887
|
}
|
|
1888
|
+
/** Дождаться состояния `state` не дольше `timeoutMs`; `true` — дождались. */
|
|
1889
|
+
#waitForState(state, timeoutMs) {
|
|
1890
|
+
if (this.#state === state)
|
|
1891
|
+
return Promise.resolve(true);
|
|
1892
|
+
return new Promise((resolve) => {
|
|
1893
|
+
const onSummary = (summary) => {
|
|
1894
|
+
if (summary.state !== state)
|
|
1895
|
+
return;
|
|
1896
|
+
clearTimeout(timer);
|
|
1897
|
+
this.off("summary", onSummary);
|
|
1898
|
+
resolve(true);
|
|
1899
|
+
};
|
|
1900
|
+
const timer = setTimeout(() => {
|
|
1901
|
+
this.off("summary", onSummary);
|
|
1902
|
+
resolve(false);
|
|
1903
|
+
}, timeoutMs);
|
|
1904
|
+
this.on("summary", onSummary);
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1819
1907
|
/** Ближайший конец хода (`result`) или `null`, если не дождались. */
|
|
1820
1908
|
#nextTurnEnd(timeoutMs) {
|
|
1821
1909
|
return new Promise((resolve) => {
|
|
@@ -2208,7 +2296,7 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2208
2296
|
\u0421\u0435\u0441\u0441\u0438\u044E \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u043B Claude Code, ${whence}: ${this.#executable.path}. \u0415\u0441\u043B\u0438 \u0434\u0435\u043B\u043E \u0432 \u043D\u0451\u043C, CCV_CLAUDE_PATH \u0443\u043A\u0430\u0436\u0435\u0442 \u0434\u0440\u0443\u0433\u043E\u0439, \u0430 CCV_BUNDLED_CLAUDE=1 \u0432\u0435\u0440\u043D\u0451\u0442 ccv \u043D\u0430 \u0431\u0438\u043D\u0430\u0440\u043D\u0438\u043A \u0438\u0437 Agent SDK.`;
|
|
2209
2297
|
}
|
|
2210
2298
|
#handleMessage(message) {
|
|
2211
|
-
if (this.#state === "idle" && (message.type === "assistant" || message.type === "stream_event")) {
|
|
2299
|
+
if (this.#state === "idle" && (message.type === "assistant" || message.type === "stream_event") && message.parent_tool_use_id === null) {
|
|
2212
2300
|
this.#setState("running");
|
|
2213
2301
|
}
|
|
2214
2302
|
switch (message.type) {
|
|
@@ -2242,6 +2330,14 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2242
2330
|
});
|
|
2243
2331
|
if (isBackgroundLaunch(block.input))
|
|
2244
2332
|
this.#backgroundWork.add(block.id);
|
|
2333
|
+
if (message.parent_tool_use_id !== null) {
|
|
2334
|
+
this.#subagentToolUses.add(block.id);
|
|
2335
|
+
if (this.#subagentToolUses.size > 256) {
|
|
2336
|
+
const oldest = this.#subagentToolUses.values().next().value;
|
|
2337
|
+
if (oldest !== void 0)
|
|
2338
|
+
this.#subagentToolUses.delete(oldest);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2245
2341
|
}
|
|
2246
2342
|
}
|
|
2247
2343
|
break;
|
|
@@ -2298,6 +2394,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2298
2394
|
}
|
|
2299
2395
|
}
|
|
2300
2396
|
this.#reconnectAttempt = 0;
|
|
2397
|
+
for (const [id, task] of this.#tasks) {
|
|
2398
|
+
if (!task.background && !this.#backgroundLevel?.has(id))
|
|
2399
|
+
this.#tasks.delete(id);
|
|
2400
|
+
}
|
|
2301
2401
|
const alreadyIdle = this.#state === "idle";
|
|
2302
2402
|
this.#setState("idle");
|
|
2303
2403
|
if (alreadyIdle)
|
|
@@ -2351,9 +2451,120 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2351
2451
|
void this.refreshContext(true);
|
|
2352
2452
|
return;
|
|
2353
2453
|
}
|
|
2454
|
+
if (this.#handleTask(message))
|
|
2455
|
+
return;
|
|
2354
2456
|
const { type: _type, subtype, ...rest } = message;
|
|
2355
2457
|
this.#record({ kind: "system", subtype, data: rest });
|
|
2356
2458
|
}
|
|
2459
|
+
/**
|
|
2460
|
+
* События задач CLI → `#tasks` и `#backgroundLevel`. `true` — событие
|
|
2461
|
+
* разобрано и в журнал не идёт; `task_started` и `task_notification`
|
|
2462
|
+
* (редкие края) журнал по-прежнему получает.
|
|
2463
|
+
*
|
|
2464
|
+
* Типы SDK тут не помогают: подтипов в союзе `SDKMessage` больше, чем
|
|
2465
|
+
* объявлено, и поля вроде `owned_by_subagent` в них не описаны — читаем
|
|
2466
|
+
* как есть и проверяем форму сами.
|
|
2467
|
+
*/
|
|
2468
|
+
#handleTask(message) {
|
|
2469
|
+
const subtype = message.subtype;
|
|
2470
|
+
const taskId = typeof message.task_id === "string" ? message.task_id : null;
|
|
2471
|
+
switch (subtype) {
|
|
2472
|
+
case "task_started": {
|
|
2473
|
+
this.#lastActiveAt = Date.now();
|
|
2474
|
+
const toolUseId = str(message.tool_use_id);
|
|
2475
|
+
if (!taskId || message.owned_by_subagent === true || toolUseId !== null && this.#subagentToolUses.has(toolUseId)) {
|
|
2476
|
+
return false;
|
|
2477
|
+
}
|
|
2478
|
+
this.#tasks.set(taskId, {
|
|
2479
|
+
id: taskId,
|
|
2480
|
+
toolUseId,
|
|
2481
|
+
kind: taskKind(str(message.task_type) ?? (message.subagent_type ? "local_agent" : null)),
|
|
2482
|
+
agentType: str(message.subagent_type),
|
|
2483
|
+
description: str(message.description) ?? "",
|
|
2484
|
+
activity: null,
|
|
2485
|
+
lastTool: null,
|
|
2486
|
+
background: message.is_backgrounded === true || this.#backgroundLevel?.has(taskId) === true,
|
|
2487
|
+
startedAt: Date.now(),
|
|
2488
|
+
tokens: 0,
|
|
2489
|
+
toolUses: 0
|
|
2490
|
+
});
|
|
2491
|
+
this.emit("summary", this.summary());
|
|
2492
|
+
return false;
|
|
2493
|
+
}
|
|
2494
|
+
case "task_progress": {
|
|
2495
|
+
this.#lastActiveAt = Date.now();
|
|
2496
|
+
if (Date.now() - this.#usageAt >= USAGE_TURN_INTERVAL_MS)
|
|
2497
|
+
void this.refreshUsage();
|
|
2498
|
+
const task = taskId ? this.#tasks.get(taskId) : void 0;
|
|
2499
|
+
if (!task)
|
|
2500
|
+
return true;
|
|
2501
|
+
const usage = message.usage ?? {};
|
|
2502
|
+
task.activity = str(message.summary) ?? str(message.description) ?? task.activity;
|
|
2503
|
+
task.lastTool = str(message.last_tool_name) ?? task.lastTool;
|
|
2504
|
+
if (typeof usage.total_tokens === "number")
|
|
2505
|
+
task.tokens = usage.total_tokens;
|
|
2506
|
+
if (typeof usage.tool_uses === "number")
|
|
2507
|
+
task.toolUses = usage.tool_uses;
|
|
2508
|
+
this.#emitTasksSoon();
|
|
2509
|
+
return true;
|
|
2510
|
+
}
|
|
2511
|
+
case "task_updated": {
|
|
2512
|
+
const task = taskId ? this.#tasks.get(taskId) : void 0;
|
|
2513
|
+
if (!task)
|
|
2514
|
+
return true;
|
|
2515
|
+
const patch = message.patch ?? {};
|
|
2516
|
+
const status = patch.status;
|
|
2517
|
+
if (status === "completed" || status === "failed" || status === "killed") {
|
|
2518
|
+
this.#tasks.delete(task.id);
|
|
2519
|
+
} else {
|
|
2520
|
+
if (typeof patch.is_backgrounded === "boolean")
|
|
2521
|
+
task.background = patch.is_backgrounded;
|
|
2522
|
+
if (typeof patch.description === "string")
|
|
2523
|
+
task.description = patch.description;
|
|
2524
|
+
}
|
|
2525
|
+
this.emit("summary", this.summary());
|
|
2526
|
+
return true;
|
|
2527
|
+
}
|
|
2528
|
+
case "task_notification":
|
|
2529
|
+
this.#lastActiveAt = Date.now();
|
|
2530
|
+
if (taskId && this.#tasks.delete(taskId))
|
|
2531
|
+
this.emit("summary", this.summary());
|
|
2532
|
+
return false;
|
|
2533
|
+
case "background_tasks_changed": {
|
|
2534
|
+
this.#lastActiveAt = Date.now();
|
|
2535
|
+
const live = Array.isArray(message.tasks) ? message.tasks.flatMap((t) => typeof t.task_id === "string" ? [t.task_id] : []) : [];
|
|
2536
|
+
this.#backgroundLevel = new Set(live);
|
|
2537
|
+
for (const [id, task] of this.#tasks) {
|
|
2538
|
+
if (this.#backgroundLevel.has(id))
|
|
2539
|
+
task.background = true;
|
|
2540
|
+
else if (task.background)
|
|
2541
|
+
this.#tasks.delete(id);
|
|
2542
|
+
}
|
|
2543
|
+
this.emit("summary", this.summary());
|
|
2544
|
+
return true;
|
|
2545
|
+
}
|
|
2546
|
+
default:
|
|
2547
|
+
return false;
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
/** Сводка по прогрессу задач — не чаще раза в секунду. */
|
|
2551
|
+
#emitTasksSoon() {
|
|
2552
|
+
if (this.#taskSummaryTimer)
|
|
2553
|
+
return;
|
|
2554
|
+
this.#taskSummaryTimer = setTimeout(() => {
|
|
2555
|
+
this.#taskSummaryTimer = null;
|
|
2556
|
+
this.emit("summary", this.summary());
|
|
2557
|
+
}, 1e3);
|
|
2558
|
+
}
|
|
2559
|
+
#resetTasks() {
|
|
2560
|
+
this.#tasks.clear();
|
|
2561
|
+
this.#backgroundLevel = null;
|
|
2562
|
+
this.#backgroundWork.clear();
|
|
2563
|
+
this.#subagentToolUses.clear();
|
|
2564
|
+
if (this.#taskSummaryTimer)
|
|
2565
|
+
clearTimeout(this.#taskSummaryTimer);
|
|
2566
|
+
this.#taskSummaryTimer = null;
|
|
2567
|
+
}
|
|
2357
2568
|
/**
|
|
2358
2569
|
* Слэш-команды и модели — то, из чего UI строит меню «/» и выбор модели.
|
|
2359
2570
|
*
|
|
@@ -2464,6 +2675,8 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2464
2675
|
#setState(state) {
|
|
2465
2676
|
if (this.#state === state)
|
|
2466
2677
|
return;
|
|
2678
|
+
if (BUSY_STATES.has(this.#state) || BUSY_STATES.has(state))
|
|
2679
|
+
this.#lastActiveAt = Date.now();
|
|
2467
2680
|
this.#state = state;
|
|
2468
2681
|
if (state === "running")
|
|
2469
2682
|
this.#backgroundWork.clear();
|
|
@@ -2471,6 +2684,26 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2471
2684
|
this.emit("summary", this.summary());
|
|
2472
2685
|
}
|
|
2473
2686
|
};
|
|
2687
|
+
var BUSY_STATES = /* @__PURE__ */ new Set(["running", "waiting_permission"]);
|
|
2688
|
+
function delay(ms) {
|
|
2689
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2690
|
+
}
|
|
2691
|
+
function str(value) {
|
|
2692
|
+
return typeof value === "string" && value ? value : null;
|
|
2693
|
+
}
|
|
2694
|
+
function taskKind(type) {
|
|
2695
|
+
switch (type) {
|
|
2696
|
+
case "local_agent":
|
|
2697
|
+
case "remote_agent":
|
|
2698
|
+
return "agent";
|
|
2699
|
+
case "local_bash":
|
|
2700
|
+
return "shell";
|
|
2701
|
+
case "local_workflow":
|
|
2702
|
+
return "workflow";
|
|
2703
|
+
default:
|
|
2704
|
+
return "other";
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2474
2707
|
function describe(error) {
|
|
2475
2708
|
return error instanceof Error ? error.message : String(error);
|
|
2476
2709
|
}
|
|
@@ -2850,6 +3083,7 @@ var AntigravitySession = class extends EventEmitter2 {
|
|
|
2850
3083
|
state: this.#state,
|
|
2851
3084
|
lastTurn: this.#lastTurn,
|
|
2852
3085
|
backgroundWork: false,
|
|
3086
|
+
tasks: [],
|
|
2853
3087
|
model: this.#record.model,
|
|
2854
3088
|
permissionMode: this.#record.permissionMode,
|
|
2855
3089
|
effort: this.#record.effort,
|
|
@@ -4397,20 +4631,20 @@ var GitHubAccount = class {
|
|
|
4397
4631
|
auto_init: false
|
|
4398
4632
|
}
|
|
4399
4633
|
});
|
|
4400
|
-
const cloneUrl =
|
|
4634
|
+
const cloneUrl = str2(created.clone_url);
|
|
4401
4635
|
if (!cloneUrl)
|
|
4402
4636
|
throw new Error("GitHub \u0441\u043E\u0437\u0434\u0430\u043B \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439, \u043D\u043E \u043D\u0435 \u0432\u0435\u0440\u043D\u0443\u043B \u0430\u0434\u0440\u0435\u0441 \u0434\u043B\u044F git");
|
|
4403
4637
|
return {
|
|
4404
4638
|
cloneUrl,
|
|
4405
|
-
htmlUrl:
|
|
4406
|
-
fullName:
|
|
4639
|
+
htmlUrl: str2(created.html_url) || cloneUrl.replace(/\.git$/, ""),
|
|
4640
|
+
fullName: str2(created.full_name) || `${options2.owner}/${options2.name}`
|
|
4407
4641
|
};
|
|
4408
4642
|
}
|
|
4409
4643
|
// ---------------------------------------------------------------- внутреннее
|
|
4410
4644
|
/** Логин владельца токена; заодно это и есть проверка токена на живость. */
|
|
4411
4645
|
async #login(token) {
|
|
4412
4646
|
const user = await this.#api(token, "/user");
|
|
4413
|
-
const login =
|
|
4647
|
+
const login = str2(user.login);
|
|
4414
4648
|
if (!login)
|
|
4415
4649
|
throw new Error("GitHub \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043B \u0443\u0447\u0451\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u2014 \u0442\u043E\u043A\u0435\u043D \u043D\u0435 \u043F\u043E\u0434\u043E\u0448\u0451\u043B");
|
|
4416
4650
|
return login;
|
|
@@ -4471,9 +4705,9 @@ function apiError(status, body) {
|
|
|
4471
4705
|
let details = "";
|
|
4472
4706
|
try {
|
|
4473
4707
|
const parsed = JSON.parse(body);
|
|
4474
|
-
message =
|
|
4708
|
+
message = str2(parsed.message);
|
|
4475
4709
|
if (Array.isArray(parsed.errors)) {
|
|
4476
|
-
details = parsed.errors.map((item) =>
|
|
4710
|
+
details = parsed.errors.map((item) => str2(item.message)).filter(Boolean).join("; ");
|
|
4477
4711
|
}
|
|
4478
4712
|
} catch {
|
|
4479
4713
|
message = body.slice(0, 200).trim();
|
|
@@ -4492,7 +4726,7 @@ function apiError(status, body) {
|
|
|
4492
4726
|
return `GitHub \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${status}${text ? `: ${text}` : ""}`;
|
|
4493
4727
|
}
|
|
4494
4728
|
}
|
|
4495
|
-
function
|
|
4729
|
+
function str2(value) {
|
|
4496
4730
|
return typeof value === "string" ? value : "";
|
|
4497
4731
|
}
|
|
4498
4732
|
function describe4(error) {
|
|
@@ -5746,6 +5980,7 @@ var LimitStopState = class {
|
|
|
5746
5980
|
// ../host/dist/limit-guard.js
|
|
5747
5981
|
var HANDOFF_TIMEOUT_MS = 3 * 6e4;
|
|
5748
5982
|
var LIMIT_SWEEP_MS = 6e4;
|
|
5983
|
+
var LIMIT_RECENT_MS = 2 * LIMIT_SWEEP_MS;
|
|
5749
5984
|
var WINDOW_SPAN_MS = {
|
|
5750
5985
|
fiveHour: 5 * 60 * 6e4,
|
|
5751
5986
|
sevenDay: 7 * 24 * 60 * 6e4
|
|
@@ -6169,13 +6404,13 @@ function parseStatus2(raw) {
|
|
|
6169
6404
|
}
|
|
6170
6405
|
return {
|
|
6171
6406
|
loggedIn: parsed.loggedIn === true,
|
|
6172
|
-
method:
|
|
6173
|
-
email:
|
|
6174
|
-
organization:
|
|
6175
|
-
plan:
|
|
6407
|
+
method: str3(parsed.authMethod) || "none",
|
|
6408
|
+
email: str3(parsed.email),
|
|
6409
|
+
organization: str3(parsed.orgName),
|
|
6410
|
+
plan: str3(parsed.subscriptionType)
|
|
6176
6411
|
};
|
|
6177
6412
|
}
|
|
6178
|
-
function
|
|
6413
|
+
function str3(value) {
|
|
6179
6414
|
return typeof value === "string" ? value : "";
|
|
6180
6415
|
}
|
|
6181
6416
|
function describe5(error) {
|
|
@@ -6199,8 +6434,8 @@ var RETRY_MAX_MS = 6e4;
|
|
|
6199
6434
|
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
6200
6435
|
var CONNECT_TIMEOUT_MS = 3e4;
|
|
6201
6436
|
var SILENCE_TIMEOUT_MS = 95e3;
|
|
6202
|
-
function jitter(
|
|
6203
|
-
return Math.round(
|
|
6437
|
+
function jitter(delay2) {
|
|
6438
|
+
return Math.round(delay2 * (0.8 + Math.random() * 0.4));
|
|
6204
6439
|
}
|
|
6205
6440
|
var RelayLink = class extends EventEmitter4 {
|
|
6206
6441
|
#credentials = null;
|
|
@@ -6493,12 +6728,12 @@ var RelayLink = class extends EventEmitter4 {
|
|
|
6493
6728
|
#scheduleRetry(rejected) {
|
|
6494
6729
|
if (this.#closed || !this.#credentials || this.#retryTimer)
|
|
6495
6730
|
return;
|
|
6496
|
-
const
|
|
6731
|
+
const delay2 = rejected ? RETRY_MAX_MS : this.#retry;
|
|
6497
6732
|
this.#retry = Math.min(this.#retry * 2, RETRY_MAX_MS);
|
|
6498
6733
|
this.#retryTimer = setTimeout(() => {
|
|
6499
6734
|
this.#retryTimer = null;
|
|
6500
6735
|
this.#connect();
|
|
6501
|
-
}, jitter(
|
|
6736
|
+
}, jitter(delay2));
|
|
6502
6737
|
this.#retryTimer.unref();
|
|
6503
6738
|
}
|
|
6504
6739
|
#onMessage(socket, raw) {
|
|
@@ -6898,7 +7133,7 @@ var FINDER_TITLE = "\xB7 \u043F\u043E\u0438\u0441\u043A \u043F\u0440\u043E\u0435
|
|
|
6898
7133
|
var HISTORY_PAGE = 250;
|
|
6899
7134
|
var HISTORY_PAGE_MAX = 500;
|
|
6900
7135
|
var AUTO_STOP_SWEEP_MS = 30 * 1e3;
|
|
6901
|
-
var
|
|
7136
|
+
var BUSY_STATES2 = /* @__PURE__ */ new Set([
|
|
6902
7137
|
"running",
|
|
6903
7138
|
"waiting_permission",
|
|
6904
7139
|
"reconnecting"
|
|
@@ -7779,6 +8014,12 @@ var HostServer = class {
|
|
|
7779
8014
|
await this.#handoffs.remove(frame.sessionId);
|
|
7780
8015
|
this.#broadcast({ type: "handoff.state", sessionId: frame.sessionId, handoff: null });
|
|
7781
8016
|
return;
|
|
8017
|
+
case "task.stop": {
|
|
8018
|
+
const session = this.#sessions.get(frame.sessionId);
|
|
8019
|
+
if (session instanceof ClaudeSession)
|
|
8020
|
+
await session.stopTask(frame.taskId);
|
|
8021
|
+
return;
|
|
8022
|
+
}
|
|
7782
8023
|
case "session.handoff":
|
|
7783
8024
|
this.#send(socket, {
|
|
7784
8025
|
type: "handoff.written",
|
|
@@ -8032,10 +8273,10 @@ var HostServer = class {
|
|
|
8032
8273
|
continue;
|
|
8033
8274
|
if (drafted.has(session.id))
|
|
8034
8275
|
continue;
|
|
8035
|
-
if (
|
|
8276
|
+
if (BUSY_STATES2.has(summary.state) || summary.backgroundWork)
|
|
8036
8277
|
continue;
|
|
8037
8278
|
const side = this.#sides.get(session.id);
|
|
8038
|
-
if (side &&
|
|
8279
|
+
if (side && BUSY_STATES2.has(side.state))
|
|
8039
8280
|
continue;
|
|
8040
8281
|
const lastActivity = Math.max(summary.updatedAt, side?.summary().updatedAt ?? 0, this.#draftTouchedAt.get(session.id) ?? 0);
|
|
8041
8282
|
if (now - lastActivity < after)
|
|
@@ -8057,7 +8298,7 @@ var HostServer = class {
|
|
|
8057
8298
|
}
|
|
8058
8299
|
#isBusy(session) {
|
|
8059
8300
|
const summary = session.summary();
|
|
8060
|
-
return
|
|
8301
|
+
return BUSY_STATES2.has(summary.state) || summary.backgroundWork || summary.tasks.length > 0;
|
|
8061
8302
|
}
|
|
8062
8303
|
/**
|
|
8063
8304
|
* Перечитать лимиты, пока хоть одна сессия занята. Сама сессия делает это
|
|
@@ -8096,8 +8337,9 @@ var HostServer = class {
|
|
|
8096
8337
|
const sessions = this.#claudeUserSessions();
|
|
8097
8338
|
if (sessions.length === 0)
|
|
8098
8339
|
return;
|
|
8099
|
-
const
|
|
8100
|
-
const
|
|
8340
|
+
const now = Date.now();
|
|
8341
|
+
const busy = sessions.filter((s) => this.#isBusy(s) || now - s.lastActiveAt < LIMIT_RECENT_MS);
|
|
8342
|
+
const prompt = hit.window.utilization >= 100 ? null : handoffPrompt(hit);
|
|
8101
8343
|
const results = await Promise.all(busy.map(async (session) => {
|
|
8102
8344
|
const text = await session.runHandoff(prompt, HANDOFF_TIMEOUT_MS);
|
|
8103
8345
|
const summary = session.summary();
|
|
@@ -8699,6 +8941,7 @@ var HostServer = class {
|
|
|
8699
8941
|
lastTurn: null,
|
|
8700
8942
|
// Сессия с диска не поднята — фоновой задаче взяться неоткуда.
|
|
8701
8943
|
backgroundWork: false,
|
|
8944
|
+
tasks: [],
|
|
8702
8945
|
model: null,
|
|
8703
8946
|
permissionMode: "default",
|
|
8704
8947
|
// Сессия с диска не поднята: чем она была настроена, знает только её
|
|
@@ -8810,6 +9053,7 @@ function antigravitySummary(record) {
|
|
|
8810
9053
|
state: "closed",
|
|
8811
9054
|
lastTurn: null,
|
|
8812
9055
|
backgroundWork: false,
|
|
9056
|
+
tasks: [],
|
|
8813
9057
|
model: record.model,
|
|
8814
9058
|
permissionMode: record.permissionMode,
|
|
8815
9059
|
effort: record.effort,
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@i14ks/ccv",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"license": "UNLICENSED",
|
|
5
|
-
"description": "GUI для Claude Code: сессии агента в каталогах проектов, дерево файлов и редактор рядом с разговором.",
|
|
6
|
-
"homepage": "https://claudevisual.com",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/I14ks/ClaudeCodeVisual.git",
|
|
10
|
-
"directory": "packages/cli"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"claude",
|
|
14
|
-
"claude-code",
|
|
15
|
-
"gui",
|
|
16
|
-
"agent"
|
|
17
|
-
],
|
|
18
|
-
"type": "module",
|
|
19
|
-
"bin": {
|
|
20
|
-
"ccv": "./bundle/ccv.mjs"
|
|
21
|
-
},
|
|
22
|
-
"files": [
|
|
23
|
-
"bundle",
|
|
24
|
-
"web",
|
|
25
|
-
"THIRD-PARTY-NOTICES.md"
|
|
26
|
-
],
|
|
27
|
-
"engines": {
|
|
28
|
-
"node": ">=22.5"
|
|
29
|
-
},
|
|
30
|
-
"publishConfig": {
|
|
31
|
-
"access": "public"
|
|
32
|
-
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"build": "tsc --build && node build.mjs",
|
|
35
|
-
"prepack": "node prepack.mjs",
|
|
36
|
-
"clean": "tsc --build --clean"
|
|
37
|
-
},
|
|
38
|
-
"dependencies": {
|
|
39
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.0",
|
|
40
|
-
"@lydell/node-pty": "1.2.0-beta.14",
|
|
41
|
-
"ws": "^8.18.0"
|
|
42
|
-
},
|
|
43
|
-
"devDependencies": {
|
|
44
|
-
"@ccv/host": "*",
|
|
45
|
-
"@ccv/protocol": "*",
|
|
46
|
-
"@types/node": "^22.10.0",
|
|
47
|
-
"esbuild": "^0.28.1",
|
|
48
|
-
"typescript": "^5.7.2"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@i14ks/ccv",
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
|
+
"description": "GUI для Claude Code: сессии агента в каталогах проектов, дерево файлов и редактор рядом с разговором.",
|
|
6
|
+
"homepage": "https://claudevisual.com",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/I14ks/ClaudeCodeVisual.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude",
|
|
14
|
+
"claude-code",
|
|
15
|
+
"gui",
|
|
16
|
+
"agent"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"ccv": "./bundle/ccv.mjs"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bundle",
|
|
24
|
+
"web",
|
|
25
|
+
"THIRD-PARTY-NOTICES.md"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.5"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc --build && node build.mjs",
|
|
35
|
+
"prepack": "node prepack.mjs",
|
|
36
|
+
"clean": "tsc --build --clean"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.0",
|
|
40
|
+
"@lydell/node-pty": "1.2.0-beta.14",
|
|
41
|
+
"ws": "^8.18.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@ccv/host": "*",
|
|
45
|
+
"@ccv/protocol": "*",
|
|
46
|
+
"@types/node": "^22.10.0",
|
|
47
|
+
"esbuild": "^0.28.1",
|
|
48
|
+
"typescript": "^5.7.2"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as r,H as a}from"./index-
|
|
1
|
+
import{s as r,H as a}from"./index-byQstcIo.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-_rsPujXt.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};
|