cli-jaw 1.6.10 → 1.6.12
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/dist/bin/commands/reset.js +1 -1
- package/dist/bin/commands/reset.js.map +1 -1
- package/dist/bin/postinstall.js +103 -14
- package/dist/bin/postinstall.js.map +1 -1
- package/dist/server.js +27 -3
- package/dist/server.js.map +1 -1
- package/dist/src/core/db.js +29 -0
- package/dist/src/core/db.js.map +1 -1
- package/dist/src/core/main-session.js +12 -3
- package/dist/src/core/main-session.js.map +1 -1
- package/dist/src/memory/heartbeat.js +1 -1
- package/dist/src/memory/heartbeat.js.map +1 -1
- package/dist/src/memory/indexing.js +1 -0
- package/dist/src/memory/indexing.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +3 -1
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/prompt/builder.js +6 -0
- package/dist/src/prompt/builder.js.map +1 -1
- package/package.json +1 -1
- package/public/css/chat.css +19 -0
- package/public/css/variables.css +15 -0
- package/public/dist/assets/architecture-YZFGNWBL-ztGcIsMX.js +1 -0
- package/public/dist/assets/architectureDiagram-Q4EWVU46-BeLDNmwN.js +1 -0
- package/public/dist/assets/blockDiagram-DXYQGD6D-ChvfvChp.js +1 -0
- package/public/dist/assets/c4Diagram-AHTNJAMY-BzjAXXeE.js +1 -0
- package/public/dist/assets/classDiagram-6PBFFD2Q-Ce2Zjeeb.js +1 -0
- package/public/dist/assets/classDiagram-v2-HSJHXN6E-DGGliiix.js +1 -0
- package/public/dist/assets/cose-bilkent-S5V4N54A-T-OwcThC.js +1 -0
- package/public/dist/assets/dagre-KV5264BT-DmdK-8pb.js +1 -0
- package/public/dist/assets/diagram-5BDNPKRD-Bf8S6ACH.js +1 -0
- package/public/dist/assets/diagram-G4DWMVQ6-C37hs32X.js +1 -0
- package/public/dist/assets/diagram-MMDJMWI5-9NNeBmGR.js +1 -0
- package/public/dist/assets/diagram-TYMM5635-l-8r-KVr.js +1 -0
- package/public/dist/assets/{employees-C8NcsoIa.js → employees-CA_DI2gy.js} +1 -1
- package/public/dist/assets/erDiagram-SMLLAGMA-BfSqi5hi.js +1 -0
- package/public/dist/assets/flowDiagram-DWJPFMVM-B6am8zYz.js +1 -0
- package/public/dist/assets/ganttDiagram-T4ZO3ILL-weu0QfVv.js +1 -0
- package/public/dist/assets/gitGraph-7Q5UKJZL-BucTBJ0C.js +1 -0
- package/public/dist/assets/gitGraphDiagram-UUTBAWPF-D4BnXgVC.js +1 -0
- package/public/dist/assets/idb-cache-C7z4qE00.js +1 -0
- package/public/dist/assets/idb-cache-DbK81tgv.js +1 -0
- package/public/dist/assets/{index-DQ81DqnC.js → index-6UFnW9uO.js} +9 -9
- package/public/dist/assets/index-ck7lqnh7.css +1 -0
- package/public/dist/assets/info-OMHHGYJF-OpOBLEsS.js +1 -0
- package/public/dist/assets/infoDiagram-42DDH7IO-9W0kVtoy.js +1 -0
- package/public/dist/assets/ishikawaDiagram-UXIWVN3A-BEgeMMA5.js +1 -0
- package/public/dist/assets/journeyDiagram-VCZTEJTY-gIlNwmx5.js +1 -0
- package/public/dist/assets/kanban-definition-6JOO6SKY-DV9gfO6_.js +1 -0
- package/public/dist/assets/mermaid.core-CYqc8Qyq.js +1 -0
- package/public/dist/assets/mindmap-definition-QFDTVHPH-DFHJRlCi.js +1 -0
- package/public/dist/assets/packet-4T2RLAQJ-DxyOEAi5.js +1 -0
- package/public/dist/assets/pie-ZZUOXDRM-CU7m5wDm.js +1 -0
- package/public/dist/assets/pieDiagram-DEJITSTG-BEGiEzHN.js +1 -0
- package/public/dist/assets/quadrantDiagram-34T5L4WZ-E5jEMjzC.js +1 -0
- package/public/dist/assets/radar-PYXPWWZC-CNpXegnm.js +1 -0
- package/public/dist/assets/render-C2tuSVTL.js +25 -0
- package/public/dist/assets/requirementDiagram-MS252O5E-B3fjwTBx.js +1 -0
- package/public/dist/assets/sankeyDiagram-XADWPNL6-BIyouHFw.js +1 -0
- package/public/dist/assets/sequenceDiagram-FGHM5R23-CROknRPY.js +1 -0
- package/public/dist/assets/{settings-kyjHk6rj.js → settings-BJcG1r6G.js} +1 -1
- package/public/dist/assets/settings-DV5X1U8f.js +1 -0
- package/public/dist/assets/{skills-B6bTgYdX.js → skills-D3cWRZOl.js} +1 -1
- package/public/dist/assets/skills-idPvxY0n.js +1 -0
- package/public/dist/assets/slash-commands-BHtBaFWh.js +1 -0
- package/public/dist/assets/{slash-commands-D-vY1ONF.js → slash-commands-PkW1NPle.js} +1 -1
- package/public/dist/assets/stateDiagram-FHFEXIEX-DqgrX77_.js +1 -0
- package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-CMxISNLC.js +1 -0
- package/public/dist/assets/timeline-definition-GMOUNBTQ-CheLYWOf.js +1 -0
- package/public/dist/assets/treeView-SZITEDCU-CWgmTXw-.js +1 -0
- package/public/dist/assets/treemap-W4RFUUIX-CMkxaF1N.js +1 -0
- package/public/dist/assets/ui-BdW-cWnY.js +131 -0
- package/public/dist/assets/ui-qcenMIau.js +1 -0
- package/public/dist/assets/{vendor-mermaid-DV2i2BfY.js → vendor-mermaid-C2RBgdM6.js} +4 -4
- package/public/dist/assets/vennDiagram-DHZGUBPP-Cm4YZzbv.js +1 -0
- package/public/dist/assets/wardley-RL74JXVD-HPEa5s3y.js +1 -0
- package/public/dist/assets/wardleyDiagram-NUSXRM2D-vZvODvIY.js +1 -0
- package/public/dist/assets/ws-Dcq99IkD.js +2 -0
- package/public/dist/assets/xychartDiagram-5P7HB3ND-DdPllF4_.js +1 -0
- package/public/dist/index.html +15 -2
- package/public/index.html +13 -0
- package/public/js/features/avatar.ts +50 -0
- package/public/js/features/chat.ts +1 -1
- package/public/js/features/idb-cache.ts +5 -0
- package/public/js/main.ts +2 -0
- package/public/js/render.ts +19 -9
- package/public/js/ui.ts +13 -7
- package/public/js/virtual-scroll.ts +63 -23
- package/public/js/ws.ts +10 -4
- package/public/dist/assets/architecture-YZFGNWBL-BBAoUgDs.js +0 -1
- package/public/dist/assets/architectureDiagram-Q4EWVU46-CnCGC-W1.js +0 -1
- package/public/dist/assets/blockDiagram-DXYQGD6D-CCKUN-eq.js +0 -1
- package/public/dist/assets/c4Diagram-AHTNJAMY-2BznTVIo.js +0 -1
- package/public/dist/assets/classDiagram-6PBFFD2Q-CNIdDcl_.js +0 -1
- package/public/dist/assets/classDiagram-v2-HSJHXN6E-D5c5cCXM.js +0 -1
- package/public/dist/assets/cose-bilkent-S5V4N54A-15-I0CI3.js +0 -1
- package/public/dist/assets/dagre-KV5264BT-B-7UZ2bP.js +0 -1
- package/public/dist/assets/diagram-5BDNPKRD-CtpSLvPV.js +0 -1
- package/public/dist/assets/diagram-G4DWMVQ6-C-FxLjz8.js +0 -1
- package/public/dist/assets/diagram-MMDJMWI5-CyWYCa1y.js +0 -1
- package/public/dist/assets/diagram-TYMM5635-Bc2Z_pKL.js +0 -1
- package/public/dist/assets/erDiagram-SMLLAGMA-BOsddzBo.js +0 -1
- package/public/dist/assets/flowDiagram-DWJPFMVM-CNS6NI2y.js +0 -1
- package/public/dist/assets/ganttDiagram-T4ZO3ILL-DzeONbMw.js +0 -1
- package/public/dist/assets/gitGraph-7Q5UKJZL-D-msAZeS.js +0 -1
- package/public/dist/assets/gitGraphDiagram-UUTBAWPF-Ck2kMeHe.js +0 -1
- package/public/dist/assets/index-DbSvUuCm.css +0 -1
- package/public/dist/assets/info-OMHHGYJF-38sLGsyT.js +0 -1
- package/public/dist/assets/infoDiagram-42DDH7IO-BpZBAgqS.js +0 -1
- package/public/dist/assets/ishikawaDiagram-UXIWVN3A-CHk3pDry.js +0 -1
- package/public/dist/assets/journeyDiagram-VCZTEJTY-BQ1Iwmix.js +0 -1
- package/public/dist/assets/kanban-definition-6JOO6SKY-B0_TgypH.js +0 -1
- package/public/dist/assets/mermaid.core-C6Yxaqu5.js +0 -1
- package/public/dist/assets/mindmap-definition-QFDTVHPH-Di38Ha2d.js +0 -1
- package/public/dist/assets/packet-4T2RLAQJ-WhSOczez.js +0 -1
- package/public/dist/assets/pie-ZZUOXDRM-BFlknSFa.js +0 -1
- package/public/dist/assets/pieDiagram-DEJITSTG-C580ezo8.js +0 -1
- package/public/dist/assets/quadrantDiagram-34T5L4WZ-CqL6mFQa.js +0 -1
- package/public/dist/assets/radar-PYXPWWZC-C6TfDjQ7.js +0 -1
- package/public/dist/assets/render-DLUswUab.js +0 -25
- package/public/dist/assets/requirementDiagram-MS252O5E-CJ2UTxBk.js +0 -1
- package/public/dist/assets/sankeyDiagram-XADWPNL6-CtV7e3kn.js +0 -1
- package/public/dist/assets/sequenceDiagram-FGHM5R23-CK_ww48d.js +0 -1
- package/public/dist/assets/settings-BQXndJHI.js +0 -1
- package/public/dist/assets/skills-BaSoozYo.js +0 -1
- package/public/dist/assets/slash-commands-DwlnxrFi.js +0 -1
- package/public/dist/assets/stateDiagram-FHFEXIEX-CY21kttq.js +0 -1
- package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-Da4cAFSL.js +0 -1
- package/public/dist/assets/timeline-definition-GMOUNBTQ-BRNd_2SN.js +0 -1
- package/public/dist/assets/treeView-SZITEDCU-C4o4RBOK.js +0 -1
- package/public/dist/assets/treemap-W4RFUUIX-BIAsQVaf.js +0 -1
- package/public/dist/assets/ui-B4mnMXvw.js +0 -131
- package/public/dist/assets/ui-BnZpTNdD.js +0 -1
- package/public/dist/assets/vennDiagram-DHZGUBPP-BE5I_z1u.js +0 -1
- package/public/dist/assets/wardley-RL74JXVD-BYUgx5S_.js +0 -1
- package/public/dist/assets/wardleyDiagram-NUSXRM2D-DZn9qJg1.js +0 -1
- package/public/dist/assets/ws-BMpuc7kR.js +0 -2
- package/public/dist/assets/xychartDiagram-5P7HB3ND-DIYzBAwh.js +0 -1
- /package/public/dist/assets/{constants-74vpnlVG.js → constants-DwGsi0Gn.js} +0 -0
- /package/public/dist/assets/{locale-DIXc-_34.js → locale-DVVWjxKN.js} +0 -0
package/public/dist/index.html
CHANGED
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap"
|
|
26
26
|
rel="stylesheet">
|
|
27
27
|
<!-- Vite handles module bundling in dev (HMR) and production (build) -->
|
|
28
|
-
<script type="module" crossorigin src="/dist/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/dist/assets/index-6UFnW9uO.js"></script>
|
|
29
29
|
<link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-Bjnw0wQ6.css">
|
|
30
|
-
<link rel="stylesheet" crossorigin href="/dist/assets/index-
|
|
30
|
+
<link rel="stylesheet" crossorigin href="/dist/assets/index-ck7lqnh7.css">
|
|
31
31
|
</head>
|
|
32
32
|
|
|
33
33
|
<body>
|
|
@@ -86,6 +86,19 @@
|
|
|
86
86
|
class="input-agent-name">
|
|
87
87
|
<button class="sidebar-hb-btn btn-action-sm w-auto" id="appNameSave">✓</button>
|
|
88
88
|
</div>
|
|
89
|
+
|
|
90
|
+
<div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
|
|
91
|
+
<div class="flex gap-2 items-center">
|
|
92
|
+
<div class="flex gap-1 items-center flex-1">
|
|
93
|
+
<label class="avatar-label">🤖</label>
|
|
94
|
+
<input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
|
|
95
|
+
</div>
|
|
96
|
+
<div class="flex gap-1 items-center flex-1">
|
|
97
|
+
<label class="avatar-label">👤</label>
|
|
98
|
+
<input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
|
|
99
|
+
</div>
|
|
100
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
|
|
101
|
+
</div>
|
|
89
102
|
<button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
|
|
90
103
|
<button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
|
|
91
104
|
<button class="sidebar-hb-btn" data-action="openTemplates"><span data-icon="plan"></span> 프롬프트 템플릿</button>
|
package/public/index.html
CHANGED
|
@@ -92,6 +92,19 @@
|
|
|
92
92
|
class="input-agent-name">
|
|
93
93
|
<button class="sidebar-hb-btn btn-action-sm w-auto" id="appNameSave">✓</button>
|
|
94
94
|
</div>
|
|
95
|
+
|
|
96
|
+
<div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
|
|
97
|
+
<div class="flex gap-2 items-center">
|
|
98
|
+
<div class="flex gap-1 items-center flex-1">
|
|
99
|
+
<label class="avatar-label">🤖</label>
|
|
100
|
+
<input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
|
|
101
|
+
</div>
|
|
102
|
+
<div class="flex gap-1 items-center flex-1">
|
|
103
|
+
<label class="avatar-label">👤</label>
|
|
104
|
+
<input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
|
|
105
|
+
</div>
|
|
106
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
|
|
107
|
+
</div>
|
|
95
108
|
<button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
|
|
96
109
|
<button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
|
|
97
110
|
<button class="sidebar-hb-btn" data-action="openTemplates"><span data-icon="plan"></span> 프롬프트 템플릿</button>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ── Agent & User Avatar ──
|
|
2
|
+
const AGENT_KEY = 'agentAvatar';
|
|
3
|
+
const USER_KEY = 'userAvatar';
|
|
4
|
+
const DEFAULT_AGENT = '🦈';
|
|
5
|
+
const DEFAULT_USER = '👤';
|
|
6
|
+
|
|
7
|
+
let agentAvatar = DEFAULT_AGENT;
|
|
8
|
+
let userAvatar = DEFAULT_USER;
|
|
9
|
+
|
|
10
|
+
export function getAgentAvatar(): string { return agentAvatar; }
|
|
11
|
+
export function getUserAvatar(): string { return userAvatar; }
|
|
12
|
+
|
|
13
|
+
export function setAgentAvatar(emoji: string): void {
|
|
14
|
+
agentAvatar = (emoji || '').trim() || DEFAULT_AGENT;
|
|
15
|
+
localStorage.setItem(AGENT_KEY, agentAvatar);
|
|
16
|
+
document.querySelectorAll('.agent-icon').forEach(el => { el.textContent = agentAvatar; });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function setUserAvatar(emoji: string): void {
|
|
20
|
+
userAvatar = (emoji || '').trim() || DEFAULT_USER;
|
|
21
|
+
localStorage.setItem(USER_KEY, userAvatar);
|
|
22
|
+
document.querySelectorAll('.user-icon').forEach(el => { el.textContent = userAvatar; });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function initAvatar(): void {
|
|
26
|
+
agentAvatar = localStorage.getItem(AGENT_KEY) || DEFAULT_AGENT;
|
|
27
|
+
userAvatar = localStorage.getItem(USER_KEY) || DEFAULT_USER;
|
|
28
|
+
|
|
29
|
+
const ai = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
|
|
30
|
+
const ui = document.getElementById('userAvatarInput') as HTMLInputElement | null;
|
|
31
|
+
if (ai) ai.value = agentAvatar;
|
|
32
|
+
if (ui) ui.value = userAvatar;
|
|
33
|
+
|
|
34
|
+
document.getElementById('avatarSave')?.addEventListener('click', () => {
|
|
35
|
+
const a = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
|
|
36
|
+
const u = document.getElementById('userAvatarInput') as HTMLInputElement | null;
|
|
37
|
+
if (a) setAgentAvatar(a.value);
|
|
38
|
+
if (u) setUserAvatar(u.value);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
for (const id of ['agentAvatarInput', 'userAvatarInput']) {
|
|
42
|
+
document.getElementById(id)?.addEventListener('keydown', (e: Event) => {
|
|
43
|
+
if ((e as KeyboardEvent).key === 'Enter') {
|
|
44
|
+
(e as KeyboardEvent).preventDefault();
|
|
45
|
+
document.getElementById('avatarSave')?.click();
|
|
46
|
+
(e.target as HTMLInputElement).blur();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -202,7 +202,7 @@ function renderFilePreview(): void {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
export async function clearChat(): Promise<void> {
|
|
205
|
-
|
|
205
|
+
// UI-only clear — do NOT call /api/clear (it deletes DB messages)
|
|
206
206
|
cancelPostRender();
|
|
207
207
|
getVirtualScroll().clear();
|
|
208
208
|
const chatEl = document.getElementById('chatMessages');
|
|
@@ -74,6 +74,11 @@ function openDB(): Promise<IDBDatabase> {
|
|
|
74
74
|
|
|
75
75
|
export async function cacheMessages(messages: CachedMessage[]): Promise<void> {
|
|
76
76
|
try {
|
|
77
|
+
// Guard: never wipe the cache with an empty array — only a deliberate
|
|
78
|
+
// clearCache() call should empty the store. This prevents data loss when
|
|
79
|
+
// the server briefly returns [] during restarts or scope mismatches.
|
|
80
|
+
if (messages.length === 0) return;
|
|
81
|
+
|
|
77
82
|
const db = await openDB();
|
|
78
83
|
const tx = db.transaction(STORE, 'readwrite');
|
|
79
84
|
const store = tx.objectStore(STORE);
|
package/public/js/main.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
import { state } from './state.js';
|
|
50
50
|
import { loadCliRegistry, getCliKeys } from './constants.js';
|
|
51
51
|
import { initAppName } from './features/appname.js';
|
|
52
|
+
import { initAvatar } from './features/avatar.js';
|
|
52
53
|
import { initSidebar, toggleLeft, toggleRight } from './features/sidebar.js';
|
|
53
54
|
import { initTheme } from './features/theme.js';
|
|
54
55
|
import { initGestures } from './features/gesture.js';
|
|
@@ -431,6 +432,7 @@ async function bootstrap(): Promise<void> {
|
|
|
431
432
|
loadEmployees();
|
|
432
433
|
initHeartbeatBadge();
|
|
433
434
|
initAppName();
|
|
435
|
+
initAvatar();
|
|
434
436
|
initSidebar();
|
|
435
437
|
initMsgCopy();
|
|
436
438
|
initGestures();
|
package/public/js/render.ts
CHANGED
|
@@ -963,20 +963,30 @@ export function renderMarkdown(text: string, isStreaming = false): string {
|
|
|
963
963
|
// ── Batched post-render scheduler ──
|
|
964
964
|
// Coalesces multiple renderMarkdown() calls into a single post-render pass.
|
|
965
965
|
let postRenderRAF: number | null = null;
|
|
966
|
+
let postRenderTimer: ReturnType<typeof setTimeout> | null = null;
|
|
966
967
|
|
|
967
968
|
function schedulePostRender(): void {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
969
|
+
// Debounce: coalesce rapid VS render triggers into a single pass
|
|
970
|
+
if (postRenderTimer) clearTimeout(postRenderTimer);
|
|
971
|
+
if (postRenderRAF) { cancelAnimationFrame(postRenderRAF); postRenderRAF = null; }
|
|
972
|
+
postRenderTimer = setTimeout(() => {
|
|
973
|
+
postRenderTimer = null;
|
|
974
|
+
postRenderRAF = requestAnimationFrame(() => {
|
|
975
|
+
postRenderRAF = null;
|
|
976
|
+
renderMermaidBlocks();
|
|
977
|
+
rehighlightAll();
|
|
978
|
+
bindDiagramZoom();
|
|
979
|
+
const msgContainer = document.getElementById('chatMessages');
|
|
980
|
+
if (msgContainer) linkifyFilePaths(msgContainer);
|
|
981
|
+
});
|
|
982
|
+
}, 100);
|
|
977
983
|
}
|
|
978
984
|
|
|
979
985
|
export function cancelPostRender(): void {
|
|
986
|
+
if (postRenderTimer) {
|
|
987
|
+
clearTimeout(postRenderTimer);
|
|
988
|
+
postRenderTimer = null;
|
|
989
|
+
}
|
|
980
990
|
if (postRenderRAF) {
|
|
981
991
|
cancelAnimationFrame(postRenderRAF);
|
|
982
992
|
postRenderRAF = null;
|
package/public/js/ui.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { state } from './state.js';
|
|
3
3
|
import { renderMarkdown, escapeHtml, stripOrchestration, linkifyFilePaths } from './render.js';
|
|
4
4
|
import { getAppName } from './features/appname.js';
|
|
5
|
+
import { getAgentAvatar, getUserAvatar } from './features/avatar.js';
|
|
5
6
|
import { t } from './features/i18n.js';
|
|
6
7
|
import { api } from './api.js';
|
|
7
8
|
import { cacheMessages, getCachedMessages, appendCachedMessage, upsertMessage, setMessageScope, getScopedMessages } from './features/idb-cache.js';
|
|
@@ -34,8 +35,7 @@ function parseToolLog(toolLog?: string | null): ToolLogEntry[] {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function getAgentIcon(_cli?: string | null): string {
|
|
37
|
-
|
|
38
|
-
return ICONS.shark;
|
|
38
|
+
return getAgentAvatar();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function toProcessSteps(tools: ToolLogEntry[]): ProcessStep[] {
|
|
@@ -295,7 +295,7 @@ export function addMessage(role: string, text: string, cli?: string | null): HTM
|
|
|
295
295
|
div.innerHTML = `<div class="agent-icon" aria-hidden="true">${getAgentIcon(cli)}</div><div class="agent-body"><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
296
296
|
} else {
|
|
297
297
|
div.className = `msg msg-${role}`;
|
|
298
|
-
div.innerHTML = `<div class="msg-label">${label}</div><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button>`;
|
|
298
|
+
div.innerHTML = `<div class="user-body"><div class="msg-label">${label}</div><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatar()}</div>`;
|
|
299
299
|
}
|
|
300
300
|
const contentEl = div.querySelector('.msg-content');
|
|
301
301
|
if (contentEl) contentEl.setAttribute('data-raw', stripOrchestration(text));
|
|
@@ -368,11 +368,15 @@ export function handleSave(): void {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
function updateStatMsgs(count: number): void {
|
|
372
|
+
const el = document.getElementById('statMsgs');
|
|
373
|
+
if (el) el.textContent = t('stat.messages', { count });
|
|
374
|
+
}
|
|
375
|
+
|
|
371
376
|
export async function loadStats(): Promise<void> {
|
|
372
377
|
const msgs = await api<MessageItem[]>('/api/messages');
|
|
373
378
|
if (!msgs) return;
|
|
374
|
-
|
|
375
|
-
if (el) el.textContent = t('stat.messages', { count: msgs.length });
|
|
379
|
+
updateStatMsgs(msgs.length);
|
|
376
380
|
}
|
|
377
381
|
|
|
378
382
|
export async function loadMessages(): Promise<void> {
|
|
@@ -403,7 +407,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
403
407
|
const skeletonContent = '<div class="skeleton-line"></div><div class="skeleton-line"></div>';
|
|
404
408
|
const html = role === 'agent'
|
|
405
409
|
? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${getAgentIcon(m.cli)}</div><div class="agent-body">${toolHtml}<div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
|
|
406
|
-
: `<div class="msg msg-${role}"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
410
|
+
: `<div class="msg msg-${role}"><div class="user-body"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatar()}</div></div>`;
|
|
407
411
|
vs.addItem(crypto.randomUUID(), html);
|
|
408
412
|
}
|
|
409
413
|
|
|
@@ -452,6 +456,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
452
456
|
cacheMessages(msgs.map(m => ({
|
|
453
457
|
role: m.role, content: m.content, cli: m.cli ?? null, tool_log: m.tool_log ?? null, timestamp: Date.now(),
|
|
454
458
|
}))).catch(() => {});
|
|
459
|
+
updateStatMsgs(msgs.length);
|
|
455
460
|
showEmptyState();
|
|
456
461
|
return;
|
|
457
462
|
}
|
|
@@ -474,7 +479,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
474
479
|
const skeletonContent = '<div class="skeleton-line"></div><div class="skeleton-line"></div>';
|
|
475
480
|
const html = role === 'agent'
|
|
476
481
|
? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${getAgentIcon(m.cli)}</div><div class="agent-body">${toolHtml}<div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
|
|
477
|
-
: `<div class="msg msg-${role}"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
482
|
+
: `<div class="msg msg-${role}"><div class="user-body"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatar()}</div></div>`;
|
|
478
483
|
vs.addItem(crypto.randomUUID(), html);
|
|
479
484
|
}
|
|
480
485
|
vs.onLazyRender = (targets: HTMLElement[]) => {
|
|
@@ -513,6 +518,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
513
518
|
});
|
|
514
519
|
}
|
|
515
520
|
addSystemMsg(`${ICONS.warning} 오프라인 모드 — 캐시된 메시지 표시 중`);
|
|
521
|
+
updateStatMsgs(cached.length);
|
|
516
522
|
}
|
|
517
523
|
showEmptyState();
|
|
518
524
|
}
|
|
@@ -102,8 +102,8 @@ export class VirtualScroll {
|
|
|
102
102
|
this._totalHeight += this.items[i].height;
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
|
-
|
|
106
|
-
this.container.
|
|
105
|
+
// Atomic swap — avoids visible blank frame during activation
|
|
106
|
+
this.container.replaceChildren(this.spacerTop, this.viewport, this.spacerBottom);
|
|
107
107
|
this.container.addEventListener('scroll', this.scrollHandler, { passive: true });
|
|
108
108
|
this.render();
|
|
109
109
|
}
|
|
@@ -152,32 +152,50 @@ export class VirtualScroll {
|
|
|
152
152
|
this.spacerTop.style.height = `${topSpace}px`;
|
|
153
153
|
this.spacerBottom.style.height = `${bottomSpace}px`;
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
// Build map of currently mounted items by vsIdx
|
|
156
|
+
const mounted = new Map<number, HTMLElement>();
|
|
157
|
+
for (const child of Array.from(this.viewport.children) as HTMLElement[]) {
|
|
158
|
+
const idx = Number(child.dataset.vsIdx);
|
|
159
|
+
if (!isNaN(idx)) mounted.set(idx, child);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Remove items no longer in range
|
|
163
|
+
for (const [idx, el] of mounted) {
|
|
164
|
+
if (idx < first || idx > last) {
|
|
165
|
+
el.remove();
|
|
166
|
+
mounted.delete(idx);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Build ordered list — reuse existing or create new
|
|
171
|
+
const ordered: HTMLElement[] = [];
|
|
156
172
|
for (let i = first; i <= last; i++) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
const existing = mounted.get(i);
|
|
174
|
+
if (existing) {
|
|
175
|
+
ordered.push(existing);
|
|
176
|
+
} else {
|
|
177
|
+
const item = this.items[i];
|
|
178
|
+
const div = document.createElement('div');
|
|
179
|
+
div.innerHTML = item.html;
|
|
180
|
+
const el = div.firstElementChild as HTMLElement;
|
|
181
|
+
if (el) {
|
|
182
|
+
el.dataset.vsIdx = String(i);
|
|
183
|
+
ordered.push(el);
|
|
184
|
+
}
|
|
164
185
|
}
|
|
165
186
|
}
|
|
166
|
-
this.viewport.innerHTML = '';
|
|
167
|
-
this.viewport.appendChild(frag);
|
|
168
187
|
|
|
169
|
-
//
|
|
170
|
-
this.viewport.
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this._totalHeight += (newH - oldH);
|
|
188
|
+
// Reorder viewport children to match (minimal DOM moves)
|
|
189
|
+
let nodeRef = this.viewport.firstChild as HTMLElement | null;
|
|
190
|
+
for (const el of ordered) {
|
|
191
|
+
if (el !== nodeRef) {
|
|
192
|
+
this.viewport.insertBefore(el, nodeRef);
|
|
193
|
+
} else {
|
|
194
|
+
nodeRef = nodeRef.nextSibling as HTMLElement | null;
|
|
177
195
|
}
|
|
178
|
-
}
|
|
196
|
+
}
|
|
179
197
|
|
|
180
|
-
// Fire lazy render callback
|
|
198
|
+
// Fire lazy render callback FIRST (replaces skeleton with real content)
|
|
181
199
|
if (this.onLazyRender) {
|
|
182
200
|
const lazyTargets = this.viewport.querySelectorAll<HTMLElement>('.lazy-pending');
|
|
183
201
|
if (lazyTargets.length > 0) {
|
|
@@ -185,10 +203,32 @@ export class VirtualScroll {
|
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
|
|
188
|
-
// Fire post-render callback for widget activation
|
|
206
|
+
// Fire post-render callback for widget activation
|
|
189
207
|
if (this.onPostRender) {
|
|
190
208
|
this.onPostRender(this.viewport);
|
|
191
209
|
}
|
|
210
|
+
|
|
211
|
+
// Batch-read heights AFTER lazy render + widget activation
|
|
212
|
+
this.remeasureVisible();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Batch-read heights from visible elements, batch-write to items array.
|
|
216
|
+
* Separated read/write passes = single forced reflow. */
|
|
217
|
+
private remeasureVisible(): void {
|
|
218
|
+
const rects: { idx: number; newH: number }[] = [];
|
|
219
|
+
this.viewport.querySelectorAll('[data-vs-idx]').forEach(el => {
|
|
220
|
+
const idx = Number((el as HTMLElement).dataset.vsIdx);
|
|
221
|
+
if (this.items[idx]) {
|
|
222
|
+
rects.push({ idx, newH: el.getBoundingClientRect().height });
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
for (const { idx, newH } of rects) {
|
|
226
|
+
const oldH = this.items[idx].height;
|
|
227
|
+
if (oldH !== newH) {
|
|
228
|
+
this.items[idx].height = newH;
|
|
229
|
+
this._totalHeight += (newH - oldH);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
192
232
|
}
|
|
193
233
|
|
|
194
234
|
scrollToBottom(): void {
|
package/public/js/ws.ts
CHANGED
|
@@ -72,6 +72,7 @@ interface WsMessage {
|
|
|
72
72
|
const agentPhaseState: Record<string, { phase: string; phaseLabel: string }> = {};
|
|
73
73
|
|
|
74
74
|
let currentOrcScope = '';
|
|
75
|
+
let lastLoadTs = 0;
|
|
75
76
|
|
|
76
77
|
/** Hydrate agent phase cache from snapshot (used after reconnect) */
|
|
77
78
|
export function hydrateAgentPhases(workers: Array<{
|
|
@@ -263,6 +264,8 @@ export function connect(): void {
|
|
|
263
264
|
getVirtualScroll().clear();
|
|
264
265
|
const el = document.getElementById('chatMessages');
|
|
265
266
|
if (el) el.innerHTML = '';
|
|
267
|
+
// Intentional clear — also wipe IndexedDB cache
|
|
268
|
+
import('./features/idb-cache.js').then(m => m.clearCache()).catch(() => {});
|
|
266
269
|
} else if (msg.type === 'session_reset') {
|
|
267
270
|
addSystemMsg(`${ICONS.refresh} Session reset — history preserved`, 'tool-activity');
|
|
268
271
|
} else if (msg.type === 'agent_added' || msg.type === 'agent_updated' || msg.type === 'agent_deleted') {
|
|
@@ -276,11 +279,14 @@ export function connect(): void {
|
|
|
276
279
|
};
|
|
277
280
|
state.ws.onopen = () => {
|
|
278
281
|
console.log('[ws] connected');
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
import('./ui.js').then(m => {
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
const skipReload = now - lastLoadTs < 10000;
|
|
284
|
+
import('./ui.js').then(async m => {
|
|
282
285
|
m.cleanupToolActivity();
|
|
283
|
-
|
|
286
|
+
if (!skipReload) {
|
|
287
|
+
await m.loadMessages();
|
|
288
|
+
lastLoadTs = Date.now();
|
|
289
|
+
}
|
|
284
290
|
m.setStatus('idle');
|
|
285
291
|
});
|
|
286
292
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{X as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{M as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{k as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{O as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{D as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as render};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{E as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as render};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{T as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{S as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{x as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{y as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{J as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createGitGraphServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{v as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|