feat: improve agent responses and workspace UI

Add live agent progress, elapsed timing, verified code navigation and rich Markdown rendering. Preserve NanumSquare styling with missing glyph fallback, fix platform availability filtering and unify workspace scrollbar and visualization controls.
This commit is contained in:
2026-10-19 17:06:18 +09:00
parent af21fa0510
commit 8b501266ff
33 changed files with 2231 additions and 98 deletions
+50 -34
View File
@@ -24,11 +24,12 @@
#miniMapToggle {
position:absolute;
right:16px;
bottom:240px;
bottom:calc(16px + 220px + 2px);
z-index:11;
min-height:28px;
padding:0 10px;
border:1px solid rgba(255,255,255,.22);
border-radius:6px 6px 0 0;
background:rgba(17,24,39,.42);
color:rgba(255,255,255,.88);
font:12px sans-serif;
@@ -109,6 +110,7 @@
display:none;
}
#keyGuide {
display:none;
position:absolute;
left:16px;
bottom:16px;
@@ -127,7 +129,7 @@
#rosTopicStatusWrap {
position:absolute;
left:16px;
bottom:54px;
bottom:16px;
z-index:10;
width:178px;
}
@@ -252,6 +254,7 @@
#cameraPresetSelect {
min-height:32px;
border:1px solid rgba(255,255,255,.22);
border-radius:6px;
background:rgba(17,24,39,.42);
color:rgba(255,255,255,.88);
font:12px sans-serif;
@@ -261,29 +264,43 @@
box-shadow:0 10px 26px rgba(0,0,0,.16);
transition:background .15s ease, border-color .15s ease, color .15s ease;
}
#downloadMap,
#pathToggle,
#sensorToggle,
#poseToggle,
#goalToggle,
#obstacleToggle {
padding:0 11px;
}
#downloadMap {
display:inline-flex;
align-items:center;
gap:6px;
}
.viewer-icon-button {
display:grid;
place-items:center;
width:34px;
height:32px;
flex:0 0 34px;
padding:0;
}
.camera-preset-wrap {
position:relative;
width:34px;
height:32px;
flex:0 0 34px;
}
.camera-preset-wrap > svg {
position:absolute;
inset:0;
margin:auto;
color:rgba(255,255,255,.88);
pointer-events:none;
}
#cameraPresetSelect {
min-width:86px;
padding:0 24px 0 9px;
outline:none;
width:100%;
height:100%;
padding:0;
color:transparent;
appearance:none;
background-image:linear-gradient(45deg, transparent 50%, rgba(255,255,255,.7) 50%), linear-gradient(135deg, rgba(255,255,255,.7) 50%, transparent 50%);
background-position:calc(100% - 13px) 14px, calc(100% - 8px) 14px;
background-size:5px 5px, 5px 5px;
background-repeat:no-repeat;
}
#cameraPresetSelect option {
color:rgba(255,255,255,.88);
background:#202020;
}
.viewer-icon-button:focus-visible,
#cameraPresetSelect:focus-visible {
outline:2px solid #93c5fd;
outline-offset:2px;
}
#downloadMap:hover,
#pathToggle:hover,
#sensorToggle:hover,
@@ -296,6 +313,7 @@
border-color:rgba(255,255,255,.34);
color:#fff;
}
#cameraPresetSelect:hover { color:transparent; }
#downloadMap:disabled {
opacity:.45;
cursor:not-allowed;
@@ -374,35 +392,33 @@
.nav2-slam #goalToggle, .nav2-slam #poseToggle { display:none; }
.nav2-navigation #downloadWrap { display:none; }
.nav2-navigation #topLeftControls { display:block; }
.nav2-navigation #keyGuide { display:none; }
.nav2-navigation #rosTopicStatusWrap { bottom:16px; }
.real-runtime #topLeftControls { display:none; }
.real-runtime #keyGuide { display:none; }
.real-teleop:not(.editor-mode) #keyGuide { display:block; }
.real-teleop #rosTopicStatusWrap { bottom:50px; }
</style>
</head>
<body>
<div id="topLeftControls">
<button id="obstacleToggle" title="벽 배치" data-i18n-title="벽 배치" aria-label="벽 배치" data-i18n-aria-label="벽 배치" aria-pressed="false">Wall</button>
<button id="obstacleToggle" class="viewer-icon-button" title="벽 배치" data-i18n-title="벽 배치" aria-label="벽 배치" data-i18n-aria-label="벽 배치" aria-pressed="false"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-brick-wall" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M12 9v6M16 15v6M16 3v6M3 15h18M3 9h18M8 15v6M8 3v6"/></svg></button>
</div>
<div id="topRightControls">
<div id="downloadWrap">
<button id="downloadMap" title="맵 저장" data-i18n-title="맵 저장" aria-label="맵 저장" data-i18n-aria-label="맵 저장" disabled><span>SAVE</span></button>
<button id="downloadMap" class="viewer-icon-button" title="맵 저장" data-i18n-title="맵 저장" aria-label="맵 저장" data-i18n-aria-label="맵 저장" disabled><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-save" aria-hidden="true"><path d="M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"/><path d="M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7M7 3v4a1 1 0 0 0 1 1h7"/></svg></button>
</div>
<button id="pathToggle" title="로봇 경로 표시" data-i18n-title="로봇 경로 표시" aria-label="로봇 경로 표시" data-i18n-aria-label="로봇 경로 표시">Path</button>
<button id="sensorToggle" title="센서 표시" data-i18n-title="센서 표시" aria-label="센서 표시" data-i18n-aria-label="센서 표시" aria-pressed="false">Sensor</button>
<button id="cameraToggle" type="button" title="카메라 영상 표시" data-i18n-title="카메라 영상 표시" aria-label="카메라 영상 표시" data-i18n-aria-label="카메라 영상 표시" aria-pressed="true" aria-controls="cameraPreviewWrap">
<button id="pathToggle" class="viewer-icon-button" title="로봇 경로 표시" data-i18n-title="로봇 경로 표시" aria-label="로봇 경로 표시" data-i18n-aria-label="로봇 경로 표시"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-route" aria-hidden="true"><circle cx="6" cy="19" r="3"/><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15"/><circle cx="18" cy="5" r="3"/></svg></button>
<button id="sensorToggle" class="viewer-icon-button" title="센서 표시" data-i18n-title="센서 표시" aria-label="센서 표시" data-i18n-aria-label="센서 표시" aria-pressed="false"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-radar" aria-hidden="true"><path d="M19.07 4.93A10 10 0 0 0 6.99 3.34M4 6h.01M2.29 9.62A10 10 0 1 0 21.31 8.35M16.24 7.76A6 6 0 1 0 8.23 16.67M12 18h.01M17.99 11.66A6 6 0 0 1 15.77 16.67"/><circle cx="12" cy="12" r="2"/><path d="m13.41 10.59 5.66-5.66"/></svg></button>
<button id="cameraToggle" class="viewer-icon-button" type="button" title="카메라 영상 표시" data-i18n-title="카메라 영상 표시" aria-label="카메라 영상 표시" data-i18n-aria-label="카메라 영상 표시" aria-pressed="true" aria-controls="cameraPreviewWrap">
<!-- Lucide Camera icon. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"/><circle cx="12" cy="13" r="3"/></svg>
</button>
<button id="poseToggle" title="초기 위치 지정" data-i18n-title="초기 위치 지정" aria-label="초기 위치 지정" data-i18n-aria-label="초기 위치 지정">Pose</button>
<button id="goalToggle" title="목표 위치 지정" data-i18n-title="목표 위치 지정" aria-label="목표 위치 지정" data-i18n-aria-label="목표 위치 지정">Goal</button>
<button id="poseToggle" class="viewer-icon-button" title="초기 위치 지정" data-i18n-title="초기 위치 지정" aria-label="초기 위치 지정" data-i18n-aria-label="초기 위치 지정"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-locate-fixed" aria-hidden="true"><path d="M2 12h3M19 12h3M12 2v3M12 19v3"/><circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="3"/></svg></button>
<button id="goalToggle" class="viewer-icon-button" title="목표 위치 지정" data-i18n-title="목표 위치 지정" aria-label="목표 위치 지정" data-i18n-aria-label="목표 위치 지정"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-flag" aria-hidden="true"><path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528"/></svg></button>
<div class="camera-preset-wrap">
<select id="cameraPresetSelect" title="카메라 시점" data-i18n-title="카메라 시점" aria-label="카메라 시점" data-i18n-aria-label="카메라 시점">
<option value="default">Default</option>
<option value="top">Top</option>
<option value="follow">Follow</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panels-top-left" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
</div>
</div>
<canvas id="miniMap" width="260" height="220"></canvas>
<button id="miniMapToggle" title="미니맵 접기/펼치기" data-i18n-title="미니맵 접기/펼치기" aria-label="미니맵 접기" data-i18n-aria-label="미니맵 접기">▾</button>