diff --git a/src/AnnotationControl/annotationPanel.css b/src/AnnotationControl/annotationPanel.css
index f8bebc8..1c95c7b 100644
--- a/src/AnnotationControl/annotationPanel.css
+++ b/src/AnnotationControl/annotationPanel.css
@@ -218,6 +218,21 @@ img.button-icon[src$="/annotation.svg"]{
.annotation-header .annotation-label {
margin-right: 8px;
}
+
+/* tiny pencil hint showing that the title is editable via double-click */
+.annotation-header .annotation-label .annotation-edit-hint {
+ margin-left: 8px;
+ font-size: 0.85em;
+ color: rgba(223,230,233,0.72);
+ opacity: 0.9;
+ cursor: default;
+ user-select: none;
+ vertical-align: middle;
+ margin-top: -1px;
+}
+.annotation-header .annotation-label:hover .annotation-edit-hint {
+ color: rgba(255,255,255,0.95);
+}
.annotation-header .controls {
margin-left: auto;
display: flex;
diff --git a/src/AnnotationControl/annotationPanel.js b/src/AnnotationControl/annotationPanel.js
index 5c4a9f9..7ead99b 100644
--- a/src/AnnotationControl/annotationPanel.js
+++ b/src/AnnotationControl/annotationPanel.js
@@ -244,6 +244,14 @@ export function initAnnotationsPanel(viewer) {
if (uuid) startInlineEditForUUID(uuid)
})
+ // Small edit hint icon to indicate double-click to edit
+ const editHint = document.createElement('span')
+ editHint.className = 'annotation-edit-hint'
+ editHint.title = 'Double-click to edit title'
+ editHint.textContent = '🖉'
+ // keep the hint non-interactive (don't steal pointer events)
+ label.appendChild(editHint)
+
// triangular toggle (collapsed/open)
const toggle = document.createElement('span')
toggle.className = 'toggle-triangle'
@@ -363,6 +371,31 @@ export function initAnnotationsPanel(viewer) {
header.appendChild(controls)
row.appendChild(header)
+ // Allow clicking anywhere on the header to toggle the details, but
+ // ignore clicks that originate from interactive controls (jump/delete)
+ // so those buttons keep their original behavior.
+ try {
+ header.addEventListener('click', (ev) => {
+ try {
+ // If the click was inside the controls area (jump/delete), do nothing
+ if (ev.target && ev.target.closest && ev.target.closest('.controls')) {
+ return
+ }
+
+ // If the click was on an interactive element (button, input, textarea, a), ignore
+ const interactive = ['BUTTON', 'INPUT', 'TEXTAREA', 'A', 'SELECT', 'LABEL']
+ if (ev.target && ev.target.tagName && interactive.indexOf(ev.target.tagName) >= 0) {
+ return
+ }
+
+ // Otherwise toggle the row
+ row.classList.toggle('open')
+ } catch (e) {
+ // fail silently
+ }
+ })
+ } catch (e) {}
+
// Wire toggle to show/hide details
try {
toggle.addEventListener('click', (ev) => {
@@ -419,11 +452,14 @@ export function initAnnotationsPanel(viewer) {
const PLACEHOLDER_POS = [589748.27, 231444.54, 753.675]
if (pointPos && approxEqual(pointPos, PLACEHOLDER_POS)) pointPos = null
+ // Format the camera and point positions for display
if (cam || pointPos) {
const info = document.createElement('div')
info.className = 'annotation-info'
const fmt = (v) => (v ? v.map((c) => Number(c).toFixed(3)).join(', ') : '—')
- info.innerHTML = `Camera coordinates: ${fmt(cam)}
Saved Point coordinates: ${fmt(pointPos)}`
+ const fmtPoint = fmt(pointPos) ? String(fmt(pointPos)).replace(/,\s*/g, ',
') : '—'
+ const fmtCam = fmt(cam) ? String(fmt(cam)).replace(/,\s*/g, ',
') : '—'
+ info.innerHTML = `Saved Point coordinates:
${fmtPoint}
Camera coordinates:
${fmtCam}`
body.appendChild(info)
// Enable or disable jump button depending on whether we have a saved point position
@@ -451,7 +487,23 @@ export function initAnnotationsPanel(viewer) {
if (!uuid) return
const labelEl = targetContainer.querySelector(`.annotation-label[data-uuid="${uuid}"]`)
if (!labelEl) return
- const oldText = labelEl.textContent || ''
+ // Compute the visible title text without the edit-hint glyph.
+ // The label contains a text node (the title) and a child .annotation-edit-hint span.
+ let oldText = ''
+ try {
+ for (const node of Array.from(labelEl.childNodes)) {
+ if (node.nodeType === Node.TEXT_NODE) {
+ oldText += node.nodeValue || ''
+ }
+ }
+ oldText = (oldText || '').trim()
+ } catch (e) {
+ oldText = labelEl.textContent || ''
+ }
+ // Capture existing hint so we can re-attach it after editing
+ const existingHint = labelEl.querySelector && labelEl.querySelector('.annotation-edit-hint')
+ const existingHintText = existingHint ? existingHint.textContent : null
+ const existingHintTitle = existingHint ? existingHint.title : null
const input = document.createElement('input')
input.type = 'text'
input.value = oldText
@@ -471,6 +523,16 @@ export function initAnnotationsPanel(viewer) {
ev.stopPropagation()
startInlineEditForUUID(uuid)
})
+ // Re-attach the edit hint so the hint persists after editing
+ try {
+ const hint = document.createElement('span')
+ hint.className = 'annotation-edit-hint'
+ if (existingHintTitle) hint.title = existingHintTitle
+ hint.textContent = existingHintText != null ? existingHintText : '✎'
+ newLabel.appendChild(hint)
+ } catch (e) {
+ // ignore hint restoration errors
+ }
try {
if (input.isConnected) {