Skip to content
Merged
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
4 changes: 2 additions & 2 deletions client/dive-common/components/BottomPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import ControlsContainer from 'dive-common/components/ControlsContainer.vue';
import TrackSettingsPanel from 'dive-common/components/TrackSettingsPanel.vue';
import TrackListColumnSettings from 'dive-common/components/TrackListColumnSettings.vue';
import TrackList from 'vue-media-annotator/components/Tracks/TrackList.vue';
import FilterList from 'vue-media-annotator/components/FilterList.vue';
import TypeSettingsPanel from 'dive-common/components/TypeSettingsPanel.vue';
import FilterList from 'vue-media-annotator/components/Types/FilterList.vue';
import TypeSettingsPanel from 'dive-common/components/Types/TypeSettingsPanel.vue';
import ConfidenceFilter from 'dive-common/components/ConfidenceFilter.vue';
import ConfidenceSubsection from 'dive-common/components/ConfidenceSubsection.vue';
import AttributeSubsection from 'dive-common/components/Attributes/AttributesSubsection.vue';
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/GroupSidebar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from 'vue';
import { shallowMount } from '@vue/test-utils';
import { clientSettings } from 'dive-common/store/settings';
import FilterList from 'vue-media-annotator/components/FilterList.vue';
import FilterList from 'vue-media-annotator/components/Types/FilterList.vue';
import GroupSidebar from './GroupSidebar.vue';

const provideMocks = vi.hoisted(() => ({
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/GroupSidebar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import FilterList from 'vue-media-annotator/components/FilterList.vue';
import FilterList from 'vue-media-annotator/components/Types/FilterList.vue';
import GroupList from 'vue-media-annotator/components/GroupList.vue';

import StackedVirtualSidebarContainer from 'dive-common/components/StackedVirtualSidebarContainer.vue';
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { clientSettings } from 'dive-common/store/settings';
import ConfidenceFilter from 'dive-common/components/ConfidenceFilter.vue';
import TrackDetailsPanel from 'dive-common/components/TrackDetailsPanel.vue';
import TrackSettingsPanel from 'dive-common/components/TrackSettingsPanel.vue';
import TypeSettingsPanel from 'dive-common/components/TypeSettingsPanel.vue';
import TypeSettingsPanel from 'dive-common/components/Types/TypeSettingsPanel.vue';
import StackedVirtualSidebarContainer from 'dive-common/components/StackedVirtualSidebarContainer.vue';
import { usePrompt } from 'dive-common/vue-utilities/prompt-service';

Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/TrackDetailsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { Attribute } from 'vue-media-annotator/use/AttributeTypes';
import type Track from 'src/track';
import TrackItem from 'vue-media-annotator/components/Tracks/TrackItem.vue';
import TooltipBtn from 'vue-media-annotator/components/TooltipButton.vue';
import TypePicker from 'vue-media-annotator/components/TypePicker.vue';
import TypePicker from 'vue-media-annotator/components/Types/TypePicker.vue';
import RangeEditor from 'vue-media-annotator/components/RangeEditor.vue';

import AttributeInput from 'dive-common/components/Attributes/AttributeInput.vue';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
} from 'vue';
import { useReadOnlyMode, useTrackFilters } from 'vue-media-annotator/provides';
import { clientSettings } from 'dive-common/store/settings';
import { CategoryImport, parseCategoryFile } from '../categoryImport';
import { CategoryImport, parseCategoryFile } from '../../categoryImport';
import WormsImport from './WormsImport.vue';
import {
flattenHierarchyForest, pruneHierarchyUpward, resolveTypeHierarchy,
} from '../typeHierarchy';
} from '../../typeHierarchy';
import {
groupSynonymRemaps, multiAcceptedNameWarnings, synonymRemapSummary, SynonymRemap, TaxonomySources,
} from '../worms';
} from '../../worms';

function mergeSynonymRemaps(
current: SynonymRemap[] | undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { GlobalStyleSettings } from 'dive-common/apispec';
import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
import StyleManager from 'vue-media-annotator/StyleManager';
import type { CustomStyle } from 'vue-media-annotator/StyleManager';
import TypeEditor from 'vue-media-annotator/components/TypeEditor.vue';
import TypeEditor from 'vue-media-annotator/components/Types/TypeEditor.vue';

type StyleKind = 'type' | 'group';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { defineComponent, h, nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import { WormsRecord } from '../worms';
import { WormsRecord } from '../../worms';
import WormsImport from './WormsImport.vue';

const api = vi.hoisted(() => ({ search: vi.fn(), children: vi.fn(), prepare: vi.fn() }));
vi.mock('../worms', async (importOriginal) => ({
...await importOriginal<typeof import('../worms')>(),
vi.mock('../../worms', async (importOriginal) => ({
...await importOriginal<typeof import('../../worms')>(),
WormsClient: class {
search = api.search;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from 'vue';
import {
WormsClient, WormsRecord, WORMS_PAGE_SIZE, wormsLink,
} from '../worms';
} from '../../worms';

export default defineComponent({
name: 'WormsImport',
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/UserSettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineComponent } from 'vue';
import { clientSettings } from 'dive-common/store/settings';
import isDesktopRuntime from 'dive-common/isDesktopRuntime';
import type { GlobalStyleSettings } from 'dive-common/apispec';
import SavedStylesEditor from './SavedStylesEditor.vue';
import SavedStylesEditor from './Types/SavedStylesEditor.vue';

export default defineComponent({
name: 'UserSettingsDialog',
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/components/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import SegmentationPointClick from 'dive-common/recipes/segmentationpointclick';
import EditorMenu from 'dive-common/components/EditorMenu.vue';
import ConfidenceFilter from 'dive-common/components/ConfidenceFilter.vue';
import UserGuideButton from 'dive-common/components/UserGuideButton.vue';
import TypeSettingsPanel from 'dive-common/components/TypeSettingsPanel.vue';
import TypeSettingsPanel from 'dive-common/components/Types/TypeSettingsPanel.vue';
import TrackSettingsPanel from 'dive-common/components/TrackSettingsPanel.vue';
import TrackListColumnSettings from 'dive-common/components/TrackListColumnSettings.vue';
import TrackDetailsPanel from 'dive-common/components/TrackDetailsPanel.vue';
Expand Down
2 changes: 1 addition & 1 deletion client/dive-common/store/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { reactive, Component } from 'vue';
/* Components */
import TypeThreshold from 'dive-common/components/TypeThreshold.vue';
import TypeThreshold from 'dive-common/components/Types/TypeThreshold.vue';
import ImageEnhancements from 'vue-media-annotator/components/ImageEnhancements.vue';
import GroupSidebar from 'dive-common/components/GroupSidebar.vue';
import AttributesSideBar from 'dive-common/components/Attributes/AttributesSideBar.vue';
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/GroupItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useVuetify from 'vue-media-annotator/use/useVuetify';
import { useGroupFilterControls, useHandler, useReadOnlyMode } from '../provides';
import Group from '../Group';

import TypePicker from './TypePicker.vue';
import TypePicker from './Types/TypePicker.vue';

export default defineComponent({
name: 'GroupItem',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import context from 'dive-common/store/context';
import type { TrackProjection } from 'vue-media-annotator/TrackProjection';
import TooltipBtn from '../../TooltipButton.vue';
import TypePicker from '../../TypePicker.vue';
import TypePicker from '../../Types/TypePicker.vue';
import {
useCameraStore,
useHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import {
defineComponent, h, nextTick, ref, reactive,
} from 'vue';
import { shallowMount } from '@vue/test-utils';
import { compileHierarchy } from 'dive-common/typeHierarchy';
import { clientSettings } from 'dive-common/store/settings';
import TrackFilterControls from '../TrackFilterControls';
import Track, { Feature } from '../track';
import BaseFilterControls from '../BaseFilterControls';
import Group from '../Group';
import CameraStore from '../CameraStore';
import { typeListViewStore } from './typeListViewState';
import TrackFilterControls from '../../TrackFilterControls';
import Track, { Feature } from '../../track';
import BaseFilterControls from '../../BaseFilterControls';
import Group from '../../Group';
import CameraStore from '../../CameraStore';
import FilterList from './FilterList.vue';
import TypeEditor from './TypeEditor.vue';

Expand Down Expand Up @@ -56,7 +58,7 @@ function mountFilterList(props: Record<string, unknown>) {
return { wrapper, vm: child, setProps };
}

vi.mock('../provides', () => ({
vi.mock('../../provides', () => ({
useCameraStore: () => {
const camMap = ref(new Map([['singleCam', {
trackStore: {
Expand Down Expand Up @@ -182,6 +184,7 @@ function makeCountHierarchyFixture({
}

describe('FilterList hierarchy members', () => {
beforeEach(() => typeListViewStore.clear());
beforeEach(() => {
provideMocks.seekFrame.mockReset();
provideMocks.intervalSearch.mockReset().mockReturnValue([]);
Expand All @@ -193,6 +196,45 @@ describe('FilterList hierarchy members', () => {
clientSettings.typeSettings.showFrameCount = true;
});

it('restores expanded parents and collapsed branches before rendering a returning sequence', async () => {
const hierarchy = {
leaf: 'branch', sibling: 'branch', branch: 'parent', parent: 'root',
};
const firstFixture = makeHierarchyFixture(hierarchy);
const first = mountFilterList({ ...firstFixture, showEmptyTypes: true, height: 240 });
first.vm.toggleSharedLineage();
first.vm.toggleExpanded('branch');
first.wrapper.destroy();

const secondFixture = makeHierarchyFixture(hierarchy);
const second = mountFilterList({ ...secondFixture, showEmptyTypes: true, height: 240 });
// Assert immediately, before nextTick: no compact frame should be rendered.
expect(second.vm.compactSharedLineage).toBe(false);
expect(second.wrapper.text()).toContain('Compact Parents');
expect(second.wrapper.text()).not.toContain('Expand Parents');
expect(second.vm.virtualTypes.map(({ type }) => type)).toEqual(['root', 'parent', 'branch']);
secondFixture.filterControls.typeHierarchy.value = { ...hierarchy };
secondFixture.filterControls.hierarchyIndex.value = compileHierarchy(hierarchy);
await nextTick();
expect(second.vm.compactSharedLineage).toBe(false);
expect(second.vm.virtualTypes.map(({ type }) => type)).toEqual(['root', 'parent', 'branch']);
second.wrapper.destroy();
});

it('restores each dataset independently and does not overwrite its state during hydration', async () => {
const fixture = makeHierarchyFixture({ leaf: 'branch', branch: 'root' });
const { wrapper, vm } = mountFilterList({ ...fixture, showEmptyTypes: true, height: 240 });
vm.toggleSharedLineage();
if (!provideMocks.datasetIdRef) throw new Error('No dataset ID');
provideMocks.datasetIdRef.value = 'dataset-b';
expect(vm.compactSharedLineage).toBe(true);
provideMocks.datasetIdRef.value = 'dataset-a';
expect(vm.compactSharedLineage).toBe(false);
await nextTick();
expect(vm.compactSharedLineage).toBe(false);
wrapper.destroy();
});

it('discards an open Type Editor draft when the dataset changes', async () => {
const { filterControls, styleManager } = makeHierarchyFixture();
const { vm, wrapper } = mountFilterList({
Expand Down Expand Up @@ -506,7 +548,7 @@ describe('FilterList hierarchy members', () => {
expect(vm.virtualTypes.map(({ type }) => type)).toEqual(['root', 'branch', 'leaf']);
});

it('resets collapsed branches when a dataset hierarchy is loaded', async () => {
it('preserves collapsed branches when the same dataset hierarchy reloads', async () => {
clientSettings.typeSettings.trackSortDir = 'a-z';
clientSettings.typeSettings.filterTypesByFrame = false;
const { filterControls, styleManager } = makeHierarchyFixture();
Expand All @@ -527,7 +569,7 @@ describe('FilterList hierarchy members', () => {
};
await nextTick();

expect(vm.virtualTypes.map(({ type }) => type)).toEqual(['root', 'branch', 'leaf']);
expect(vm.virtualTypes.map(({ type }) => type)).toEqual(['root']);
});

it('keeps the header query-scoped while a context parent owns its full subtree', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,29 @@ import {

import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
import { clientSettings } from 'dive-common/store/settings';
import { parentDatasetId } from 'dive-common/compositeDatasetId';
import { compileHierarchy } from 'dive-common/typeHierarchy';
import { typeListViewStore } from './typeListViewState';
import {
useCameraStore, useDatasetId, useHandler, useReadOnlyMode, useTime,
usePendingSaveCount,
} from '../provides';
import TooltipBtn from './TooltipButton.vue';
} from '../../provides';
import TooltipBtn from '../TooltipButton.vue';
import TypeEditor from './TypeEditor.vue';
import TypeItem from './TypeItem.vue';
import BaseFilterControls, { AnnotationWithContext } from '../BaseFilterControls';
import TrackFilterControls from '../TrackFilterControls';
import Track from '../track';
import Group from '../Group';
import StyleManager from '../StyleManager';
import BaseFilterControls, { AnnotationWithContext } from '../../BaseFilterControls';
import TrackFilterControls from '../../TrackFilterControls';
import Track from '../../track';
import Group from '../../Group';
import StyleManager from '../../StyleManager';
import {
createRegionSuppressionTester, getSuppressedTrackIds, hasSuppressionAttribute,
suppressionTypeResolver,
} from '../use/suppression';
} from '../../use/suppression';
import {
buildTypeListModel, countResolvedTypes, TypeListModel, TypeListRow,
updateHierarchyCheckedTypes,
} from '../typeListHierarchy';
} from './typeListHierarchy';

/* Row height shared by the type rows, the shared-lineage breadcrumb, and the
scroller's height accounting. Mirrored by `$row-height` in the style block. */
Expand Down Expand Up @@ -143,20 +145,29 @@ export default defineComponent({
const typeStylingRef = props.styleManager.typeStyling;
const filteredTracksRef = trackFilters.filteredAnnotations;
const confidenceFiltersRef = trackFilters.confidenceFilters;
const collapsedTypes: Ref<Set<string>> = ref(new Set<string>());
const compactSharedLineage = ref(true);
const viewKey = computed(() => (datasetId.value
? JSON.stringify([parentDatasetId(datasetId.value), props.group ? 'groups' : 'tracks']) : ''));
const initialView = typeListViewStore.read(viewKey.value);
const collapsedTypes: Ref<Set<string>> = ref(new Set(initialView.collapsed));
const compactSharedLineage = ref(initialView.compact);
function saveView() {
typeListViewStore.write(viewKey.value, {
compact: compactSharedLineage.value, collapsed: [...collapsedTypes.value],
});
}
// Restore before a render, including when the dataset ID arrives before its
// hierarchy. Metadata hydration must not reset the user's presentation.
watch(viewKey, (key) => {
const saved = typeListViewStore.read(key);
collapsedTypes.value = new Set(saved.collapsed);
compactSharedLineage.value = saved.compact;
}, { flush: 'sync' });
const hierarchyIndexRef = computed(() => (
!props.group && trackFilters instanceof TrackFilterControls
? trackFilters.hierarchyIndex.value
: undefined
));
const hierarchyActive = computed(() => hierarchyIndexRef.value !== undefined);
if (trackFilters instanceof TrackFilterControls) {
watch(trackFilters.typeHierarchy, () => {
collapsedTypes.value = new Set<string>();
compactSharedLineage.value = true;
});
}
watch(datasetId, () => {
data.showPicker = false;
data.selectedType = '';
Expand Down Expand Up @@ -457,10 +468,12 @@ export default defineComponent({
next.add(type);
}
collapsedTypes.value = next;
saveView();
}

function toggleSharedLineage() {
compactSharedLineage.value = !compactSharedLineage.value;
saveView();
}

const showSharedLineageControl = computed(() => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
} from 'vue';
import { shallowMount } from '@vue/test-utils';
import { TypeHierarchyError } from 'dive-common/typeHierarchy';
import BaseFilterControls from '../BaseFilterControls';
import TrackFilterControls from '../TrackFilterControls';
import BaseFilterControls from '../../BaseFilterControls';
import TrackFilterControls from '../../TrackFilterControls';
import ParentTypePicker from './ParentTypePicker.vue';
import TypeEditor from './TypeEditor.vue';

Expand All @@ -15,7 +15,7 @@ vi.mock('dive-common/vue-utilities/prompt-service', () => ({
usePrompt: () => ({ prompt: promptMock }),
}));

vi.mock('../provides', () => ({
vi.mock('../../provides', () => ({
useReadOnlyMode: () => ref(provideMocks.readOnly),
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
import { TypeHierarchyError } from 'dive-common/typeHierarchy';

import TrackFilterControls from '../TrackFilterControls';
import BaseFilterControls from '../BaseFilterControls';
import type Group from '../Group';
import type StyleManager from '../StyleManager';
import type Track from '../track';
import { useReadOnlyMode } from '../provides';
import TrackFilterControls from '../../TrackFilterControls';
import BaseFilterControls from '../../BaseFilterControls';
import type Group from '../../Group';
import type StyleManager from '../../StyleManager';
import type Track from '../../track';
import { useReadOnlyMode } from '../../provides';
import ParentTypePicker from './ParentTypePicker.vue';

export default defineComponent({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
import TooltipBtn from './TooltipButton.vue';
import TooltipBtn from '../TooltipButton.vue';

const INDENT_STEP_PX = 12;
const TREE_PREFIX_PX = 20;
Expand Down
Loading
Loading