From 54285c8a4c8d966fec24536524f374282227e57b Mon Sep 17 00:00:00 2001 From: kim Date: Sun, 20 Sep 2026 14:48:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20robot-app=EC=97=90=20=EB=82=B4=EC=9E=A5?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=20=ED=8C=A8=EB=84=90=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(doctor/lawyer=20=EC=95=B1=EA=B3=BC=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=20=ED=8C=A8=EB=84=90=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 부품 리스트를 보면서 바로 옆에서 로봇 프로젝트 얘기를 할 수 있게 리사이즈 가능한 채팅 패널을 대시보드에 붙임. 세션ID를 "rb_"로 시작하게 해서 tool-scope.ts가 이 세션 안에서는 키워드 매칭 없이 robot_project 도구를 항상 열어주도록 예외 처리(doctor_app의 'dr_'/lawyer_app의 'lw_'와 동일한 패턴) — "이 부품 지워줘"처럼 짧게 말해도 바로 동작한다. 채팅 응답이 끝나면 대시보드(부품표/예산/작업)를 자동으로 다시 불러와서 채팅으로 바꾼 내용이 즉시 화면에 반영되게 함. Co-Authored-By: Claude Sonnet 5 --- src/gateway/chat/tool-scope.ts | 5 +- web-ui/html/robot-app.html | 192 +++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/src/gateway/chat/tool-scope.ts b/src/gateway/chat/tool-scope.ts index 5bffacb..051a7fc 100644 --- a/src/gateway/chat/tool-scope.ts +++ b/src/gateway/chat/tool-scope.ts @@ -305,6 +305,9 @@ export function buildSkillToolFilter(input: ToolScopeInput): (toolName: string) const isDetectiveAppSession = /^dt_/.test(String(sessionId || '')); const isInvestorAppSession = /^iv_/.test(String(sessionId || '')); const isDoctorAppSession = /^dr_/.test(String(sessionId || '')); + // robot-app.html의 전용 채팅 패널('rb_' 세션) 안에서는 그 대화 전체가 이미 로봇 프로젝트 + // 얘기라는 게 확실하므로, 키워드 없이 짧게 말해도("이 부품 지워줘") robot_project가 열리게 함. + const isRobotAppSession = /^rb_/.test(String(sessionId || '')); const filter = (name: string): boolean => { if (dormantAgentTools.has(name)) return false; if (dormantBrowserTools.has(name)) return false; @@ -372,7 +375,7 @@ export function buildSkillToolFilter(input: ToolScopeInput): (toolName: string) if ((name === 'eonet_events' || name === 'satellite_snapshot') && !hasSatelliteKeyword) return false; if (name === 'nvr_snapshot' && !hasCctvKeyword) return false; if (name === 'print3d_model' && !hasPrint3dKeyword) return false; - if (name === 'robot_project' && !hasRobotProjectKeyword) return false; + if (name === 'robot_project' && !isRobotAppSession && !hasRobotProjectKeyword) return false; if (name === 'run_command' && !hasRunCommandKeyword) return false; return true; }; diff --git a/web-ui/html/robot-app.html b/web-ui/html/robot-app.html index 9dc9840..9ec3a6a 100644 --- a/web-ui/html/robot-app.html +++ b/web-ui/html/robot-app.html @@ -24,6 +24,9 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis .rb-budget-label{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;} .rb-budget-val{font-size:15px;font-weight:700;margin-top:2px;} +.rb-layout{flex:1;display:flex;min-height:0;overflow:hidden;} +.rb-main{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden;} + .rb-tabs{display:flex;border-bottom:1px solid var(--line);background:var(--panel);flex-shrink:0;padding:0 14px;} .rb-tab-btn{background:none;border:none;border-bottom:2px solid transparent;padding:8px 14px;font-size:12px;font-weight:600;color:var(--muted);cursor:pointer;font-family:inherit;transition:.15s;margin-bottom:-1px;} .rb-tab-btn.active{color:#f59e0b;border-bottom-color:#f59e0b;} @@ -32,6 +35,47 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis .rb-pane{display:none;} .rb-pane.active{display:block;} +/* ── 채팅 패널 ── */ +.rb-resize{width:4px;flex-shrink:0;background:var(--line);cursor:col-resize;transition:.15s;} +.rb-resize:hover,.rb-resize.dragging{background:#f59e0b;} +.rb-chat{width:340px;flex-shrink:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--panel);min-height:0;} +.rb-chat-hdr{display:flex;align-items:center;padding:6px 10px;border-bottom:1px solid var(--line);flex-shrink:0;gap:6px;} +.rb-chat-label{font-size:10px;font-weight:700;color:#f59e0b;text-transform:uppercase;letter-spacing:.07em;flex-shrink:0;} +.rb-chat-clear{background:none;border:none;cursor:pointer;font-size:14px;color:var(--muted);padding:2px 4px;transition:.12s;} +.rb-chat-clear:hover{color:#ef4444;} +.rb-msgs{flex:1;overflow-y:auto;padding:10px;display:flex;flex-direction:column;gap:8px;} +.rb-msgs::-webkit-scrollbar{width:3px;} +.rb-msgs::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px;} +.rb-msg{display:flex;flex-direction:column;} +.rb-msg.user{align-items:flex-end;} +.rb-msg.assistant{align-items:flex-start;} +.rb-msg.system{align-items:center;} +.rb-bubble{padding:7px 11px;border-radius:10px;font-size:12px;line-height:1.55;max-width:93%;word-break:break-word;} +.rb-msg.user .rb-bubble{background:#3d2c05;color:#fde9b8;border-radius:10px 10px 3px 10px;} +.rb-msg.assistant .rb-bubble{background:var(--panel-2);border:1px solid var(--line);border-radius:10px 10px 10px 3px;} +.rb-msg.system .rb-bubble{background:transparent;color:var(--muted);font-size:11px;} +.rb-bubble p{margin:3px 0;} +.rb-bubble h3{font-size:13px;margin:6px 0 3px;} +.rb-bubble ul,.rb-bubble ol{padding-left:16px;margin:3px 0;} +.rb-bubble pre{background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:8px;overflow-x:auto;font-size:11px;margin:6px 0;} +.rb-bubble strong{font-weight:700;} +.rb-typing{display:flex;gap:3px;align-items:center;padding:6px 10px;} +.rb-dot{width:5px;height:5px;background:var(--muted);border-radius:50%;animation:rbDot 1.2s ease-in-out infinite;} +.rb-dot:nth-child(2){animation-delay:.2s;}.rb-dot:nth-child(3){animation-delay:.4s;} +@keyframes rbDot{0%,80%,100%{transform:scale(.5);opacity:.4;}40%{transform:scale(1);opacity:1;}} +.rb-input-bar{flex-shrink:0;padding:8px;border-top:1px solid var(--line);display:flex;gap:6px;align-items:flex-end;} +.rb-textarea{flex:1;background:var(--panel-2);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:12px;color:var(--text);font-family:inherit;resize:none;min-height:34px;max-height:120px;outline:none;transition:.15s;} +.rb-textarea:focus{border-color:#f59e0b;} +.rb-send{background:#7c4a03;border:none;border-radius:8px;padding:7px 13px;font-size:12px;color:#fff;cursor:pointer;font-family:inherit;font-weight:700;transition:.15s;flex-shrink:0;} +.rb-send:hover:not(:disabled){background:#9a5c03;} +.rb-send:disabled{opacity:.5;cursor:not-allowed;} + +@media (max-width:900px){ + .rb-layout{flex-direction:column;} + .rb-resize{display:none;} + .rb-chat{width:auto;height:38vh;border-left:none;border-top:1px solid var(--line);} +} + /* ── 부품 표 ── */ .rb-parts-table{width:100%;border-collapse:collapse;font-size:12px;} .rb-parts-table th{text-align:left;font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;padding:4px 6px;border-bottom:1px solid var(--line);} @@ -94,6 +138,8 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
총 예상비용
0원
+
+
@@ -145,6 +191,24 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
+
+ +
+ +
+
+ 🤖 로봇 프로젝트 채팅 + + +
+
+
+ + +
+
+ +