commit-ai-agent 1.0.5 → 1.0.6
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/package.json +1 -1
- package/public/app.js +68 -2
- package/public/index.html +59 -2
- package/public/style.css +111 -0
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -5,6 +5,58 @@ let selectedProject = null;
|
|
|
5
5
|
let isAnalyzing = false;
|
|
6
6
|
let analyzeMode = 'commit'; // 'commit' | 'status'
|
|
7
7
|
|
|
8
|
+
// ── Aria State Machine ──
|
|
9
|
+
function setAriaState(state, opts = {}) {
|
|
10
|
+
const robotWrap = document.getElementById('aria-robot-wrap');
|
|
11
|
+
const bubbleText = document.getElementById('aria-bubble-text');
|
|
12
|
+
const typingDots = document.getElementById('aria-typing-dots');
|
|
13
|
+
const chipDot = document.getElementById('aria-chip-dot');
|
|
14
|
+
const chipText = document.getElementById('aria-chip-text');
|
|
15
|
+
if (!robotWrap || !bubbleText) return;
|
|
16
|
+
|
|
17
|
+
// Base state (strip -commit / -status suffix for robot/chip)
|
|
18
|
+
const baseState = state.startsWith('ready') ? 'ready' : state;
|
|
19
|
+
|
|
20
|
+
// Robot animation class
|
|
21
|
+
robotWrap.className = `aria-robot-wrap ${baseState}`;
|
|
22
|
+
|
|
23
|
+
// Header chip
|
|
24
|
+
const chipMap = {
|
|
25
|
+
idle: { cls: 'idle', label: 'Aria · 대기 중' },
|
|
26
|
+
ready: { cls: 'ready', label: 'Aria · 준비됨' },
|
|
27
|
+
thinking: { cls: 'thinking', label: 'Aria · 분석 중...' },
|
|
28
|
+
done: { cls: 'done', label: 'Aria · 완료' },
|
|
29
|
+
error: { cls: 'error', label: 'Aria · 오류' },
|
|
30
|
+
};
|
|
31
|
+
const cm = chipMap[baseState] || chipMap.idle;
|
|
32
|
+
if (chipDot) chipDot.className = `aria-chip-dot ${cm.cls}`;
|
|
33
|
+
if (chipText) chipText.textContent = cm.label;
|
|
34
|
+
|
|
35
|
+
// Bubble messages
|
|
36
|
+
const p = opts.project ? `<strong>${opts.project}</strong>` : '';
|
|
37
|
+
const msgMap = {
|
|
38
|
+
idle: '어떤 프로젝트의 커밋을 분석해드릴까요?',
|
|
39
|
+
'ready-commit': `${p} 최근 커밋을 확인했어요. 분석을 시작할까요? 👀`,
|
|
40
|
+
'ready-status': opts.n > 0
|
|
41
|
+
? `${p}에서 변경된 파일 <strong>${opts.n}개</strong>를 발견했어요. 리뷰해드릴까요?`
|
|
42
|
+
: `${p}에 현재 변경사항이 없어요.`,
|
|
43
|
+
thinking: '코드를 꼼꼼히 살펴보고 있어요',
|
|
44
|
+
done: '분석 완료! 리포트를 확인해보세요. 😊',
|
|
45
|
+
error: '앗, 문제가 발생했어요. 다시 시도해볼까요?',
|
|
46
|
+
};
|
|
47
|
+
const newMsg = msgMap[state] || msgMap.idle;
|
|
48
|
+
|
|
49
|
+
// Fade transition
|
|
50
|
+
bubbleText.style.opacity = '0';
|
|
51
|
+
bubbleText.style.transform = 'translateY(4px)';
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
bubbleText.innerHTML = newMsg;
|
|
54
|
+
if (typingDots) typingDots.style.display = state === 'thinking' ? 'inline-flex' : 'none';
|
|
55
|
+
bubbleText.style.opacity = '1';
|
|
56
|
+
bubbleText.style.transform = 'translateY(0)';
|
|
57
|
+
}, 180);
|
|
58
|
+
}
|
|
59
|
+
|
|
8
60
|
// ── Boot ──
|
|
9
61
|
document.addEventListener('DOMContentLoaded', () => {
|
|
10
62
|
init();
|
|
@@ -29,6 +81,8 @@ async function init() {
|
|
|
29
81
|
document.getElementById('status-diff-toggle-btn').addEventListener('click', () => {
|
|
30
82
|
togglePre('status-diff-content', 'status-diff-toggle-btn');
|
|
31
83
|
});
|
|
84
|
+
|
|
85
|
+
setAriaState('idle');
|
|
32
86
|
}
|
|
33
87
|
|
|
34
88
|
// ── Config check ──
|
|
@@ -106,8 +160,10 @@ async function fetchCommitPreview() {
|
|
|
106
160
|
if (error) throw new Error(error);
|
|
107
161
|
renderCommitCard(commit);
|
|
108
162
|
document.getElementById('commit-card').style.display = 'block';
|
|
163
|
+
setAriaState('ready-commit', { project: selectedProject });
|
|
109
164
|
} catch (e) {
|
|
110
165
|
console.warn('commit preview failed:', e.message);
|
|
166
|
+
setAriaState('ready-commit', { project: selectedProject });
|
|
111
167
|
}
|
|
112
168
|
}
|
|
113
169
|
|
|
@@ -119,11 +175,14 @@ async function fetchStatusPreview() {
|
|
|
119
175
|
if (status) {
|
|
120
176
|
renderStatusCard(status);
|
|
121
177
|
document.getElementById('status-card').style.display = 'block';
|
|
178
|
+
setAriaState('ready-status', { project: selectedProject, n: status.totalFiles });
|
|
122
179
|
} else {
|
|
123
180
|
document.getElementById('selected-hint').textContent = `${selectedProject} — 변경사항 없음`;
|
|
181
|
+
setAriaState('ready-status', { project: selectedProject, n: 0 });
|
|
124
182
|
}
|
|
125
183
|
} catch (e) {
|
|
126
184
|
console.warn('status preview failed:', e.message);
|
|
185
|
+
setAriaState('ready-status', { project: selectedProject, n: 0 });
|
|
127
186
|
}
|
|
128
187
|
}
|
|
129
188
|
|
|
@@ -176,6 +235,9 @@ function switchMode(mode) {
|
|
|
176
235
|
document.getElementById('status-card').style.display = 'none';
|
|
177
236
|
document.getElementById('result-card').style.display = 'none';
|
|
178
237
|
|
|
238
|
+
const btnText = document.getElementById('analyze-btn-text');
|
|
239
|
+
if (btnText) btnText.textContent = mode === 'commit' ? 'Aria에게 분석 요청' : 'Aria에게 리뷰 요청';
|
|
240
|
+
|
|
179
241
|
if (!selectedProject) return;
|
|
180
242
|
if (mode === 'commit') fetchCommitPreview();
|
|
181
243
|
else fetchStatusPreview();
|
|
@@ -202,7 +264,8 @@ async function startAnalysis(endpoint) {
|
|
|
202
264
|
analysisBody.innerHTML = '';
|
|
203
265
|
reportSaved.textContent = '';
|
|
204
266
|
document.getElementById('copy-btn').style.display = 'none'; // 분석 시작 시 숨김
|
|
205
|
-
setStatus('loading', '
|
|
267
|
+
setStatus('loading', 'Aria가 코드를 살펴보고 있어요...');
|
|
268
|
+
setAriaState('thinking');
|
|
206
269
|
analyzeBtn.disabled = true;
|
|
207
270
|
btnIcon.textContent = '⏳';
|
|
208
271
|
|
|
@@ -252,19 +315,22 @@ async function startAnalysis(endpoint) {
|
|
|
252
315
|
}
|
|
253
316
|
} else if (data.type === 'done') {
|
|
254
317
|
setStatus('done', '✅ 분석 완료!');
|
|
318
|
+
setAriaState('done');
|
|
255
319
|
document.getElementById('copy-btn').style.display = 'inline-flex'; // 완료 시에만 표시
|
|
256
320
|
} else if (data.type === 'error') {
|
|
257
321
|
setStatus('error', `오류: ${data.message}`);
|
|
322
|
+
setAriaState('error');
|
|
258
323
|
}
|
|
259
324
|
} catch {}
|
|
260
325
|
}
|
|
261
326
|
}
|
|
262
327
|
} catch (err) {
|
|
263
328
|
setStatus('error', `네트워크 오류: ${err.message}`);
|
|
329
|
+
setAriaState('error');
|
|
264
330
|
} finally {
|
|
265
331
|
isAnalyzing = false;
|
|
266
332
|
analyzeBtn.disabled = false;
|
|
267
|
-
btnIcon.textContent = '
|
|
333
|
+
btnIcon.textContent = '🤖';
|
|
268
334
|
document.getElementById('copy-btn')._text = fullText;
|
|
269
335
|
resultCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
270
336
|
}
|
package/public/index.html
CHANGED
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
리포트
|
|
38
38
|
</button>
|
|
39
39
|
</nav>
|
|
40
|
+
<div class="aria-chip" id="aria-chip">
|
|
41
|
+
<span class="aria-chip-dot" id="aria-chip-dot"></span>
|
|
42
|
+
<span id="aria-chip-text">Aria · 대기 중</span>
|
|
43
|
+
</div>
|
|
40
44
|
</div>
|
|
41
45
|
</header>
|
|
42
46
|
|
|
@@ -44,6 +48,59 @@
|
|
|
44
48
|
<main class="main">
|
|
45
49
|
<!-- ── Tab: Analyze ── -->
|
|
46
50
|
<section class="tab-content active" id="tab-analyze">
|
|
51
|
+
|
|
52
|
+
<!-- ── Aria Hero ── -->
|
|
53
|
+
<div class="aria-hero">
|
|
54
|
+
<div class="aria-robot-wrap idle" id="aria-robot-wrap">
|
|
55
|
+
<svg class="aria-robot" viewBox="0 0 90 115" width="90" height="115" xmlns="http://www.w3.org/2000/svg">
|
|
56
|
+
<defs>
|
|
57
|
+
<linearGradient id="rg-head" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
58
|
+
<stop offset="0%" stop-color="#6366f1"/>
|
|
59
|
+
<stop offset="100%" stop-color="#22d3ee"/>
|
|
60
|
+
</linearGradient>
|
|
61
|
+
<linearGradient id="rg-body" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
62
|
+
<stop offset="0%" stop-color="#4f46e5"/>
|
|
63
|
+
<stop offset="100%" stop-color="#0e7490"/>
|
|
64
|
+
</linearGradient>
|
|
65
|
+
</defs>
|
|
66
|
+
<!-- Antenna -->
|
|
67
|
+
<line x1="45" y1="7" x2="45" y2="20" stroke="#818cf8" stroke-width="2.5" stroke-linecap="round"/>
|
|
68
|
+
<circle class="aria-antenna-dot" cx="45" cy="5" r="5" fill="#818cf8"/>
|
|
69
|
+
<!-- Ears -->
|
|
70
|
+
<rect x="1" y="34" width="9" height="18" rx="4.5" fill="#4f46e5"/>
|
|
71
|
+
<rect x="80" y="34" width="9" height="18" rx="4.5" fill="#4f46e5"/>
|
|
72
|
+
<!-- Head -->
|
|
73
|
+
<rect x="9" y="18" width="72" height="62" rx="20" fill="url(#rg-head)"/>
|
|
74
|
+
<!-- Eyes white -->
|
|
75
|
+
<circle cx="30" cy="48" r="13" fill="white" opacity="0.92"/>
|
|
76
|
+
<circle cx="60" cy="48" r="13" fill="white" opacity="0.92"/>
|
|
77
|
+
<!-- Pupils -->
|
|
78
|
+
<circle cx="32" cy="49" r="7.5" fill="#1e1b4b"/>
|
|
79
|
+
<circle cx="62" cy="49" r="7.5" fill="#1e1b4b"/>
|
|
80
|
+
<!-- Eye shine -->
|
|
81
|
+
<circle cx="35" cy="45" r="2.5" fill="white"/>
|
|
82
|
+
<circle cx="65" cy="45" r="2.5" fill="white"/>
|
|
83
|
+
<!-- Smile -->
|
|
84
|
+
<path d="M 32 63 Q 45 73 58 63" stroke="white" stroke-width="2.8" fill="none" stroke-linecap="round"/>
|
|
85
|
+
<!-- Body -->
|
|
86
|
+
<rect x="20" y="84" width="50" height="28" rx="12" fill="url(#rg-body)"/>
|
|
87
|
+
<!-- Body buttons -->
|
|
88
|
+
<circle cx="34" cy="98" r="5" fill="rgba(255,255,255,0.35)"/>
|
|
89
|
+
<circle cx="45" cy="98" r="5" fill="rgba(255,255,255,0.35)"/>
|
|
90
|
+
<circle cx="56" cy="98" r="5" fill="rgba(255,255,255,0.35)"/>
|
|
91
|
+
</svg>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="aria-bubble-wrap">
|
|
94
|
+
<div class="aria-bubble">
|
|
95
|
+
<span class="aria-bubble-text" id="aria-bubble-text">어떤 프로젝트의 커밋을 분석해드릴까요?</span>
|
|
96
|
+
<span class="aria-typing-dots" id="aria-typing-dots" style="display:none">
|
|
97
|
+
<span></span><span></span><span></span>
|
|
98
|
+
</span>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="aria-name-badge">Aria</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
47
104
|
<!-- API Key Warning -->
|
|
48
105
|
<div class="alert alert-warn" id="api-key-warn" style="display: none">
|
|
49
106
|
⚠️ <strong>.env</strong> 파일에 <code>GEMINI_API_KEY</code>가 설정되지
|
|
@@ -80,7 +137,7 @@
|
|
|
80
137
|
>프로젝트를 선택해 주세요</span
|
|
81
138
|
>
|
|
82
139
|
<button class="btn-primary" id="analyze-btn" disabled>
|
|
83
|
-
<span class="btn-icon"
|
|
140
|
+
<span class="btn-icon">🤖</span> <span id="analyze-btn-text">Aria에게 분석 요청</span>
|
|
84
141
|
</button>
|
|
85
142
|
</div>
|
|
86
143
|
</div>
|
|
@@ -118,7 +175,7 @@
|
|
|
118
175
|
<!-- Analysis Output -->
|
|
119
176
|
<div class="card result-card" id="result-card" style="display: none">
|
|
120
177
|
<div class="result-header">
|
|
121
|
-
<h2 class="card-title"
|
|
178
|
+
<h2 class="card-title">🤖 Aria의 분석 리포트</h2>
|
|
122
179
|
<div class="result-actions">
|
|
123
180
|
<button class="btn-ghost" id="copy-btn" style="display: none">
|
|
124
181
|
📋 복사
|
package/public/style.css
CHANGED
|
@@ -283,11 +283,122 @@ body {
|
|
|
283
283
|
.report-item-date { font-size: 12px; color: var(--text3); font-family: 'JetBrains Mono', monospace; }
|
|
284
284
|
.empty-state { color: var(--text3); font-size: 14px; text-align: center; padding: 32px; }
|
|
285
285
|
|
|
286
|
+
/* ── Aria: Header Chip ── */
|
|
287
|
+
.aria-chip {
|
|
288
|
+
display: flex; align-items: center; gap: 7px;
|
|
289
|
+
background: var(--bg3); border: 1px solid var(--border);
|
|
290
|
+
border-radius: 99px; padding: 5px 13px;
|
|
291
|
+
font-size: 12px; color: var(--text2);
|
|
292
|
+
margin-right: 4px; white-space: nowrap;
|
|
293
|
+
}
|
|
294
|
+
.aria-chip-dot {
|
|
295
|
+
width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0;
|
|
296
|
+
background: #818cf8;
|
|
297
|
+
}
|
|
298
|
+
.aria-chip-dot.idle { background: #818cf8; }
|
|
299
|
+
.aria-chip-dot.ready { background: #34d399; animation: pulse 1.2s infinite; }
|
|
300
|
+
.aria-chip-dot.thinking { background: #a78bfa; animation: pulse 0.7s infinite; }
|
|
301
|
+
.aria-chip-dot.done { background: #34d399; }
|
|
302
|
+
.aria-chip-dot.error { background: #f87171; }
|
|
303
|
+
|
|
304
|
+
/* ── Aria: Hero Section ── */
|
|
305
|
+
.aria-hero {
|
|
306
|
+
display: flex; align-items: center; gap: 24px;
|
|
307
|
+
background: var(--bg2); border: 1px solid var(--border);
|
|
308
|
+
border-radius: var(--radius); padding: 20px 24px;
|
|
309
|
+
margin-bottom: 20px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* ── Aria: Robot ── */
|
|
313
|
+
.aria-robot-wrap { flex-shrink: 0; }
|
|
314
|
+
.aria-robot { display: block; }
|
|
315
|
+
|
|
316
|
+
.aria-robot-wrap.idle .aria-robot,
|
|
317
|
+
.aria-robot-wrap.ready .aria-robot { animation: aria-float 3s ease-in-out infinite alternate; }
|
|
318
|
+
.aria-robot-wrap.thinking .aria-robot { animation: aria-float 1.4s ease-in-out infinite alternate; }
|
|
319
|
+
.aria-robot-wrap.done .aria-robot { animation: aria-bounce 0.55s ease forwards; }
|
|
320
|
+
.aria-robot-wrap.error .aria-robot { animation: aria-shake 0.45s ease; }
|
|
321
|
+
|
|
322
|
+
@keyframes aria-float { 0%{transform:translateY(0)} 100%{transform:translateY(-8px)} }
|
|
323
|
+
@keyframes aria-bounce { 0%,100%{transform:scale(1)} 45%{transform:scale(1.13)} }
|
|
324
|
+
@keyframes aria-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-5px)} 75%{transform:translateX(5px)} }
|
|
325
|
+
|
|
326
|
+
/* Antenna dot by state */
|
|
327
|
+
.aria-robot-wrap.idle .aria-antenna-dot { fill: #818cf8; }
|
|
328
|
+
.aria-robot-wrap.ready .aria-antenna-dot { fill: #34d399; animation: aria-blink 1s ease-in-out infinite; }
|
|
329
|
+
.aria-robot-wrap.thinking .aria-antenna-dot { fill: #a78bfa; animation: aria-glow 0.7s ease-in-out infinite; }
|
|
330
|
+
.aria-robot-wrap.done .aria-antenna-dot { fill: #34d399; }
|
|
331
|
+
.aria-robot-wrap.error .aria-antenna-dot { fill: #f87171; }
|
|
332
|
+
|
|
333
|
+
@keyframes aria-blink { 0%,100%{opacity:1} 50%{opacity:0.35} }
|
|
334
|
+
@keyframes aria-glow {
|
|
335
|
+
0%,100%{ filter: drop-shadow(0 0 3px #a78bfa); }
|
|
336
|
+
50% { filter: drop-shadow(0 0 9px #a78bfa); }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* ── Aria: Bubble ── */
|
|
340
|
+
.aria-bubble-wrap {
|
|
341
|
+
flex: 1; display: flex; flex-direction: column;
|
|
342
|
+
align-items: flex-start; gap: 10px;
|
|
343
|
+
}
|
|
344
|
+
.aria-bubble {
|
|
345
|
+
position: relative;
|
|
346
|
+
background: var(--bg3);
|
|
347
|
+
border: 1.5px solid var(--primary);
|
|
348
|
+
border-radius: 16px; padding: 14px 18px;
|
|
349
|
+
font-size: 15px; line-height: 1.7; color: var(--text);
|
|
350
|
+
max-width: 440px;
|
|
351
|
+
}
|
|
352
|
+
/* Speech bubble tail — points left toward robot */
|
|
353
|
+
.aria-bubble::before {
|
|
354
|
+
content: ''; position: absolute;
|
|
355
|
+
left: -11px; top: 50%; transform: translateY(-50%);
|
|
356
|
+
border: 7px solid transparent; border-right-color: var(--primary);
|
|
357
|
+
}
|
|
358
|
+
.aria-bubble::after {
|
|
359
|
+
content: ''; position: absolute;
|
|
360
|
+
left: -8px; top: 50%; transform: translateY(-50%);
|
|
361
|
+
border: 5.5px solid transparent; border-right-color: var(--bg3);
|
|
362
|
+
}
|
|
363
|
+
.aria-bubble-text { display: inline; transition: opacity 0.18s ease, transform 0.18s ease; }
|
|
364
|
+
|
|
365
|
+
/* Typing dots (thinking state) */
|
|
366
|
+
.aria-typing-dots {
|
|
367
|
+
display: inline-flex; gap: 3px;
|
|
368
|
+
align-items: center; margin-left: 4px; vertical-align: middle;
|
|
369
|
+
}
|
|
370
|
+
.aria-typing-dots span {
|
|
371
|
+
width: 5px; height: 5px; border-radius: 50%;
|
|
372
|
+
background: var(--primary); display: inline-block;
|
|
373
|
+
animation: aria-dot-bounce 1.2s ease-in-out infinite;
|
|
374
|
+
}
|
|
375
|
+
.aria-typing-dots span:nth-child(2) { animation-delay: 0.2s; }
|
|
376
|
+
.aria-typing-dots span:nth-child(3) { animation-delay: 0.4s; }
|
|
377
|
+
@keyframes aria-dot-bounce {
|
|
378
|
+
0%,80%,100%{ transform: translateY(0); opacity: 0.4; }
|
|
379
|
+
40% { transform: translateY(-5px); opacity: 1; }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* Aria name badge */
|
|
383
|
+
.aria-name-badge {
|
|
384
|
+
font-size: 11px; font-weight: 700; letter-spacing: 0.5px;
|
|
385
|
+
color: var(--primary);
|
|
386
|
+
background: rgba(99,102,241,0.12); border: 1px solid rgba(99,102,241,0.25);
|
|
387
|
+
padding: 2px 11px; border-radius: 99px;
|
|
388
|
+
}
|
|
389
|
+
|
|
286
390
|
/* ── Responsive ── */
|
|
287
391
|
@media (max-width: 640px) {
|
|
288
392
|
.header-inner { padding: 0 16px; }
|
|
393
|
+
.aria-chip { display: none; } /* header chip hidden on mobile to save space */
|
|
289
394
|
.main { padding: 16px 16px 48px; }
|
|
290
395
|
.card { padding: 18px; }
|
|
291
396
|
.card-footer { flex-direction: column; align-items: stretch; gap: 12px; }
|
|
292
397
|
.commit-meta { grid-template-columns: 1fr 1fr; }
|
|
398
|
+
/* Aria hero: stack vertically */
|
|
399
|
+
.aria-hero { flex-direction: column; align-items: center; padding: 16px; gap: 14px; }
|
|
400
|
+
.aria-robot { width: 70px; height: auto; }
|
|
401
|
+
.aria-bubble { max-width: 100%; }
|
|
402
|
+
.aria-bubble::before, .aria-bubble::after { display: none; }
|
|
403
|
+
.aria-bubble-wrap { align-items: center; width: 100%; }
|
|
293
404
|
}
|