Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/components/AnnotationCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default function AnnotationCanvas({
boxOpacity,
showLabels,
hiddenClassIds,
hideAllBoxes,
selectedId,
activeClassId,
editMode,
Expand All @@ -82,7 +83,8 @@ export default function AnnotationCanvas({

const dragS = getDragStateRef.current();

// Draw annotations (skip hidden classes)
// Draw annotations (skip when hidden or class is hidden)
if (hideAllBoxes) return;
for (const ann of annotations) {
if (hiddenClassIds.has(ann.classId)) continue;

Expand Down Expand Up @@ -261,6 +263,7 @@ export default function AnnotationCanvas({
state.showLabels !== prevState.showLabels ||
state.activeClassId !== prevState.activeClassId ||
state.hiddenClassIds !== prevState.hiddenClassIds ||
state.hideAllBoxes !== prevState.hideAllBoxes ||
state.editMode !== prevState.editMode
) {
requestRedraw();
Expand Down
3 changes: 2 additions & 1 deletion src/components/AnnotationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default function AnnotationList() {
const annotations = useAppStore((s) => s.annotations);
const selectedId = useAppStore((s) => s.selectedId);
const hiddenClassIds = useAppStore((s) => s.hiddenClassIds);
const hideAllBoxes = useAppStore((s) => s.hideAllBoxes);
const setSelected = useAppStore((s) => s.setSelected);
const deleteAnnotation = useAppStore((s) => s.deleteAnnotation);
const listRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -76,7 +77,7 @@ export default function AnnotationList() {
onClick={() => setSelected(ann.id)}
className={`px-3 py-2 cursor-pointer border-b border-gray-100 flex items-start gap-2 hover:bg-gray-50 ${
isSelected ? 'bg-blue-50 ring-1 ring-inset ring-blue-200' : ''
} ${isHidden ? 'opacity-40' : ''}`}
} ${isHidden || hideAllBoxes ? 'opacity-40' : ''}`}
>
{/* Color dot */}
<span
Expand Down
5 changes: 4 additions & 1 deletion src/components/KeyboardShortcutsHelp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ const SHORTCUT_GROUPS: { title: string; shortcuts: Shortcut[] }[] = [
},
{
title: 'Display',
shortcuts: [{ keys: ['L'], description: 'Toggle labels' }],
shortcuts: [
{ keys: ['L'], description: 'Toggle labels' },
{ keys: ['H'], description: 'Hide all boxes' },
],
},
{
title: 'File',
Expand Down
17 changes: 10 additions & 7 deletions src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function Toolbar({ onToggleHelp }: ToolbarProps) {
const setEditMode = useAppStore((s) => s.setEditMode);

const hiddenClassIds = useAppStore((s) => s.hiddenClassIds);
const hideAllBoxes = useAppStore((s) => s.hideAllBoxes);

const setActiveClass = useAppStore((s) => s.setActiveClass);
const zoomIn = useAppStore((s) => s.zoomIn);
Expand All @@ -27,6 +28,7 @@ export default function Toolbar({ onToggleHelp }: ToolbarProps) {

const isIdle = editMode === 'idle';
const allVisible = hiddenClassIds.size === 0;
const effectiveAllVisible = allVisible && !hideAllBoxes;

return (
<div
Expand Down Expand Up @@ -98,41 +100,42 @@ export default function Toolbar({ onToggleHelp }: ToolbarProps) {
<button
onClick={toggleAllClassVisibility}
className={`flex items-center gap-1.5 px-2 py-1 text-xs rounded transition-colors ${
allVisible ? 'bg-gray-100 text-gray-800' : 'text-gray-500 hover:bg-gray-50'
effectiveAllVisible ? 'bg-gray-100 text-gray-800' : 'text-gray-500 hover:bg-gray-50'
}`}
aria-label={allVisible ? 'Hide all classes' : 'Show all classes'}
title={allVisible ? 'Hide all' : 'Show all'}
>
<span
className={`w-3 h-3 rounded border flex items-center justify-center text-[8px] ${
allVisible ? 'bg-gray-600 border-gray-600 text-white' : 'border-gray-400'
effectiveAllVisible ? 'bg-gray-600 border-gray-600 text-white' : 'border-gray-400'
}`}
>
{allVisible ? '✓' : ''}
{effectiveAllVisible ? '✓' : ''}
</span>
All
</button>
{CLASSES.map((cls) => {
const isVisible = !hiddenClassIds.has(cls.id);
const effectiveVisible = isVisible && !hideAllBoxes;
return (
<button
key={cls.id}
onClick={() => toggleClassVisibility(cls.id)}
className={`flex items-center gap-1.5 px-2 py-1 text-xs rounded transition-colors ${
isVisible ? 'bg-gray-100 text-gray-800' : 'text-gray-500 hover:bg-gray-50'
effectiveVisible ? 'bg-gray-100 text-gray-800' : 'text-gray-500 hover:bg-gray-50'
}`}
aria-label={`${isVisible ? 'Hide' : 'Show'} ${cls.name} annotations`}
title={`${isVisible ? 'Hide' : 'Show'} ${cls.name}`}
>
<span
className={`w-3 h-3 rounded border flex items-center justify-center text-[8px]`}
style={{
backgroundColor: isVisible ? cls.color : undefined,
backgroundColor: effectiveVisible ? cls.color : undefined,
borderColor: cls.color,
color: isVisible ? 'white' : 'transparent',
color: effectiveVisible ? 'white' : 'transparent',
}}
>
{isVisible ? '✓' : ''}
{effectiveVisible ? '✓' : ''}
</span>
<span className="capitalize">{cls.name}</span>
</button>
Expand Down
16 changes: 15 additions & 1 deletion src/hooks/useKeyboardShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ export function useKeyboardShortcuts({
}
break;
}
case 'h':
case 'H':
useAppStore.getState().setHideAllBoxes(true);
break;
case 'l':
useAppStore.getState().toggleLabels();
break;
Expand All @@ -111,7 +115,17 @@ export function useKeyboardShortcuts({
}
};

const onKeyUp = (e: KeyboardEvent) => {
if (e.key === 'h' || e.key === 'H') {
useAppStore.getState().setHideAllBoxes(false);
}
};

window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
return () => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
}, [cancelDrawing, cancelDrag, isHelpOpen, toggleHelp]);
}
5 changes: 5 additions & 0 deletions src/store/useAppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface AppState {
boxOpacity: number;
showLabels: boolean;
hiddenClassIds: Set<number>;
hideAllBoxes: boolean;
lastSaved: number | null;

// Viewport state
Expand All @@ -43,6 +44,7 @@ interface AppState {
clearAll: () => void;
setOpacity: (value: number) => void;
toggleLabels: () => void;
setHideAllBoxes: (value: boolean) => void;
toggleClassVisibility: (classId: number) => void;
toggleAllClassVisibility: () => void;
setViewport: (zoom: number, panX: number, panY: number) => void;
Expand Down Expand Up @@ -81,6 +83,7 @@ export const useAppStore = create<AppState>((set) => ({
boxOpacity: 0.3,
showLabels: true,
hiddenClassIds: new Set(),
hideAllBoxes: false,
lastSaved: null,

zoom: 1,
Expand Down Expand Up @@ -133,6 +136,8 @@ export const useAppStore = create<AppState>((set) => ({

toggleLabels: () => set((state) => ({ showLabels: !state.showLabels })),

setHideAllBoxes: (value) => set({ hideAllBoxes: value }),

toggleClassVisibility: (classId) =>
set((state) => {
const next = new Set(state.hiddenClassIds);
Expand Down
Loading