mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
List drawn shapes under Draw, with icon shapes and colour swatches
Each shape drawn by hand gets a row under Draw, as Import's files do: its colour swatch, its name, and show/hide, fly-to and remove. A shape is named in its row, and its swatch opens the five colours there, so labelling and colouring happen after a shape is drawn and can be seen, instead of in a field filled in beforehand. Area, Line and Pin become icon buttons, named for screen readers and tooltips, and the colour for the next shape is a row of swatches instead of a dropdown. The annotation engine gains per-mark remove, setHidden, restyle and frame, and onChange so the list follows the board. A hidden mark stays on the board at alpha 0, a renamed line keeps its length, and fly-to frames a line by its whole path. Voice marks are not listed. The Draw, form-field and segmented-control gates drive the new controls; swatches are checked as chips, not segments.
This commit is contained in:
1 parent
8543b7be43
commit
95ed686da5
23 files changed
+1061
-214
No files matched your search
+36
-27
@@ -3,7 +3,8 @@
|
||||
*
|
||||
* UTILITIES ▸ Draw turns the globe into a whiteboard you draw on by hand: pick a
|
||||
* shape (area, line or pin), click the vertices on the real world, double-click
|
||||
* or press Enter to finish, type a label. Each finished shape goes through the
|
||||
* or press Enter to finish, then name and colour it in its row. Each finished
|
||||
* shape goes through the
|
||||
* SAME `annotationEngine.annotate()` the voice agent uses, with its geometry
|
||||
* supplied and `manual: true`, so it renders with the whiteboard look,
|
||||
* persists, de-dups, shows up in `.list()`, and clears with the board.
|
||||
@@ -50,19 +51,18 @@ import {
|
||||
removeLastVertex,
|
||||
} from './drawMode.js';
|
||||
|
||||
import {
|
||||
DRAW_COLORS,
|
||||
DRAW_COLOR_CSS,
|
||||
createDrawMarkList,
|
||||
} from './drawMarkList.js';
|
||||
|
||||
import { msg, registerUnsavedWork, t, translateKnown } from '../i18n/index.js';
|
||||
/** The id this tool claims the pointer under. */
|
||||
const DRAW_POINTER_OWNER = 'draw';
|
||||
const PREVIEW_DATA_SOURCE_NAME = 'gev-draw-preview';
|
||||
|
||||
const COLORS = ['primary', 'amber', 'cyan', 'green', 'red'];
|
||||
const PREVIEW = {
|
||||
primary: '#8be9ff',
|
||||
amber: '#ffb547',
|
||||
cyan: '#39d0ff',
|
||||
green: '#5dff9f',
|
||||
red: '#ff6b6b',
|
||||
};
|
||||
const PREVIEW = DRAW_COLOR_CSS;
|
||||
|
||||
/**
|
||||
* Wire the Draw control. Returns a handle with a `destroy()` the application
|
||||
@@ -74,8 +74,8 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
const toggle = document.getElementById('draw-toggle');
|
||||
const modeRow = document.getElementById('draw-mode-row');
|
||||
const labelRow = document.getElementById('draw-label-row');
|
||||
const labelInput = document.getElementById('draw-label-input');
|
||||
const colorSelect = document.getElementById('draw-color-select');
|
||||
const colorRow = document.getElementById('draw-colors');
|
||||
const markListEl = document.getElementById('draw-mark-list');
|
||||
const clearButton = document.getElementById('draw-clear');
|
||||
const hint = document.getElementById('draw-hint');
|
||||
if (!viewer || !annotations || !toggle) return null;
|
||||
@@ -238,14 +238,14 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
if (reason !== 'too-few') setHint(drawHint(session));
|
||||
return null;
|
||||
}
|
||||
const spec = finishSpec(session, { label: labelInput?.value || '', color });
|
||||
// Named afterwards, in the shape's row.
|
||||
const spec = finishSpec(session, { label: '', color });
|
||||
if (!spec) return null;
|
||||
// Start a fresh session BEFORE awaiting, so a second Enter or the second
|
||||
// half of a double-click cannot submit the same shape twice.
|
||||
session = createDrawSession(shape);
|
||||
cursor = null;
|
||||
syncPreview();
|
||||
if (labelInput) labelInput.value = '';
|
||||
const attempt = generation;
|
||||
try {
|
||||
const result = await annotations.annotate([spec], {
|
||||
@@ -285,7 +285,7 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
// ---- keys: only while drawing, never while typing in another field ----
|
||||
const typingElsewhere = (event) => {
|
||||
const t = event.target;
|
||||
if (!t || t === labelInput) return false;
|
||||
if (!t) return false;
|
||||
return (
|
||||
t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable
|
||||
);
|
||||
@@ -293,12 +293,12 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
/**
|
||||
* Enter is the finish key, but on a focused control Enter means "press this
|
||||
* control". Keyboard-focusing Clear and pressing Enter must clear the board,
|
||||
* not finish the shape — so the finish key is only taken from the canvas,
|
||||
* the page body, or the label field this tool owns.
|
||||
* not finish the shape — so the finish key is only taken from the canvas or
|
||||
* the page body.
|
||||
*/
|
||||
const enterBelongsToDrawing = (event) => {
|
||||
const t = event.target;
|
||||
if (!t || t === labelInput) return true;
|
||||
if (!t) return true;
|
||||
if (
|
||||
typeof t.closest === 'function' &&
|
||||
t.closest('button, a, select, [role="button"], [role="radio"]')
|
||||
@@ -328,7 +328,7 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
else setActive(false);
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Backspace' && event.target !== labelInput) {
|
||||
if (event.key === 'Backspace') {
|
||||
if (removeLastVertex(session)) {
|
||||
event.preventDefault();
|
||||
syncPreview();
|
||||
@@ -448,18 +448,21 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
}
|
||||
},
|
||||
});
|
||||
listen(colorSelect, 'change', () => {
|
||||
color = COLORS.includes(colorSelect.value) ? colorSelect.value : 'primary';
|
||||
syncPreview();
|
||||
const colorControl = createSegmentedControl({
|
||||
buttons: [...(colorRow?.querySelectorAll('.draw-color[data-color]') || [])],
|
||||
valueOf: (button) => button.dataset.color,
|
||||
listen,
|
||||
onChange: (value) => {
|
||||
color = DRAW_COLORS.includes(value) ? value : 'primary';
|
||||
colorControl.update(color);
|
||||
syncPreview();
|
||||
},
|
||||
});
|
||||
listen(clearButton, 'click', clearAll);
|
||||
listen(labelInput, 'keydown', (event) => {
|
||||
if (event.key === 'Enter' && session?.vertices?.length) {
|
||||
event.preventDefault();
|
||||
void finish();
|
||||
}
|
||||
});
|
||||
setHint(drawHint(null));
|
||||
const markList = markListEl
|
||||
? createDrawMarkList({ listEl: markListEl, annotations, listen })
|
||||
: null;
|
||||
|
||||
const api = {
|
||||
get active() {
|
||||
@@ -488,6 +491,11 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
finish,
|
||||
cancel,
|
||||
clearAll,
|
||||
/** The hand-drawn marks the list shows. */
|
||||
marks: () => markList?.marks() || [],
|
||||
setColor(next) {
|
||||
colorRow?.querySelector(`.draw-color[data-color="${next}"]`)?.click();
|
||||
},
|
||||
shapes: DRAW_SHAPES,
|
||||
/** What this tool currently holds — for teardown and leak assertions. */
|
||||
diagnostics() {
|
||||
@@ -527,6 +535,7 @@ export function initDrawTool({ viewer, annotations }) {
|
||||
if (active) setActive(false);
|
||||
destroyed = true;
|
||||
unregisterUnsavedWork();
|
||||
markList?.destroy();
|
||||
releaseSceneHandler();
|
||||
releasePointer(lease);
|
||||
lease = null;
|
||||
|
||||
Reference in new issue
Block a user