-
Notifications
You must be signed in to change notification settings - Fork 13
Rework FSC Create and Refine GUI #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
horatiualmasan
merged 29 commits into
main
from
horatiu-lig-7767-implement-new-fsc-createedit-wizard
Oct 24, 2025
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
c78a1aa
Rework FSC Create and Refine GUI
horatiualmasan ec3d50b
format
horatiualmasan 9e3dca9
update
horatiualmasan 99b99bd
fix test and format
horatiualmasan 2460728
clean-up
horatiualmasan bdeb209
format
horatiualmasan d311094
Merge branch 'main' into horatiu-lig-7767-implement-new-fsc-createedi…
horatiualmasan c552a37
add slider for dialogs
horatiualmasan d05c039
Merge branch 'horatiu-lig-7767-implement-new-fsc-createedit-wizard' o…
horatiualmasan 25576a5
update menu
horatiualmasan d81145f
make instruction section expandable
horatiualmasan eb06df8
update resizing
horatiualmasan f3b2eb4
update based on feedback
horatiualmasan 63a36a0
remove close button from nottifications
horatiualmasan 796c7c9
Merge branch 'main' into horatiu-lig-7767-implement-new-fsc-createedi…
horatiualmasan 98f7183
clean up
horatiualmasan af97365
Merge branch 'horatiu-lig-7767-implement-new-fsc-createedit-wizard' o…
horatiualmasan 2b23fba
Merge branch 'main' into horatiu-lig-7767-implement-new-fsc-createedi…
horatiualmasan 575c7cc
remove unneded flag
horatiualmasan 71cfb65
Merge branch 'horatiu-lig-7767-implement-new-fsc-createedit-wizard' o…
horatiualmasan 2a66e3c
remove unused import
horatiualmasan 06014d3
fix issue with missing samples for classifier
horatiualmasan e0a5951
update after review
horatiualmasan be38bd8
update after review
horatiualmasan c29e8e7
fix tests
horatiualmasan 8e35646
Merge branch 'main' into horatiu-lig-7767-implement-new-fsc-createedi…
horatiualmasan d141dc4
show classifier button only on sample page
horatiualmasan 3fff160
Merge branch 'horatiu-lig-7767-implement-new-fsc-createedit-wizard' o…
horatiualmasan fe2dc51
fix
horatiualmasan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
169 changes: 169 additions & 0 deletions
169
lightly_studio_view/src/lib/components/FewShotClassifier/ClassifierSamplesGrid.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| <script lang="ts"> | ||
| import { SampleImage, SelectableBox } from '$lib/components'; | ||
| import { useClassifierState } from '$lib/hooks/useClassifiers/useClassifierState'; | ||
| import { useSamplesInfinite } from '$lib/hooks/useSamplesInfinite/useSamplesInfinite'; | ||
| import { useSettings } from '$lib/hooks/useSettings'; | ||
| import { Grid } from 'svelte-virtual'; | ||
| import type { SampleView } from '$lib/api/lightly_studio_local'; | ||
|
|
||
| const { dataset_id }: { dataset_id: string } = $props(); | ||
|
|
||
| const { classifierSamples, classifierSelectedSampleIds, toggleClassifierSampleSelection } = | ||
| useClassifierState(); | ||
| const { gridViewSampleRenderingStore } = useSettings(); | ||
|
|
||
| const samplesParams = $derived({ | ||
| dataset_id, | ||
| mode: 'classifier' as const, | ||
| classifierSamples: $classifierSamples || undefined | ||
| }); | ||
|
|
||
| const { samples: infiniteSamples } = $derived(useSamplesInfinite(samplesParams)); | ||
|
|
||
| const displayedSamples: SampleView[] = $derived( | ||
| $infiniteSamples && | ||
| $infiniteSamples.data && | ||
| $classifierSamples && | ||
| ($classifierSamples.positiveSampleIds.length > 0 || | ||
| $classifierSamples.negativeSampleIds.length > 0) | ||
| ? $infiniteSamples.data.pages.flatMap((page) => page.data) | ||
| : [] | ||
| ); | ||
|
|
||
| let viewport: HTMLElement | null = $state(null); | ||
| let objectFit = $state($gridViewSampleRenderingStore); | ||
| // Set initial height | ||
| let viewportHeight = $state(400); | ||
|
|
||
| // Grid configuration - 4 images per row | ||
| const sampleWidth = 160; | ||
| const sampleHeight = 160; | ||
| const GridGap = 6; | ||
|
|
||
| // Update viewport height when viewport changes | ||
| $effect(() => { | ||
| if (viewport) { | ||
| const resizeObserver = new ResizeObserver((entries) => { | ||
| for (const entry of entries) { | ||
| viewportHeight = Math.max(entry.contentRect.height, 200); | ||
| } | ||
| }); | ||
| resizeObserver.observe(viewport); | ||
| return () => resizeObserver.disconnect(); | ||
| } | ||
| }); | ||
|
|
||
| const handleOnClick: (event: MouseEvent & { currentTarget: HTMLElement }) => void = (event) => { | ||
| const sampleId = event.currentTarget.dataset.sampleId!; | ||
| toggleSampleSelection(sampleId); | ||
| }; | ||
|
|
||
| const handleOnDoubleClick: (event: MouseEvent & { currentTarget: HTMLElement }) => void = ( | ||
| event | ||
| ) => { | ||
| event.preventDefault(); | ||
| const sampleId = event.currentTarget.dataset.sampleId!; | ||
| toggleSampleSelection(sampleId); | ||
| }; | ||
|
|
||
| const handleKeyDown: (event: KeyboardEvent & { currentTarget: HTMLElement }) => void = ( | ||
| event | ||
| ) => { | ||
| if (event.key === 'Enter' || event.key === ' ') { | ||
| event.preventDefault(); | ||
| const sampleId = event.currentTarget.dataset.sampleId!; | ||
| toggleSampleSelection(sampleId); | ||
| } | ||
| }; | ||
|
|
||
| function toggleSampleSelection(sampleId: string) { | ||
| toggleClassifierSampleSelection(sampleId); | ||
| } | ||
| </script> | ||
|
|
||
| {#if $infiniteSamples.isPending} | ||
| <!-- Loading state --> | ||
| <div class="flex h-full w-full items-center justify-center"> | ||
| <div class="text-sm text-muted-foreground">Loading samples...</div> | ||
| </div> | ||
| {:else if $infiniteSamples.isError} | ||
| <!-- Error state --> | ||
| <div class="flex h-full w-full items-center justify-center"> | ||
| <div class="text-sm text-muted-foreground">Error loading samples</div> | ||
| </div> | ||
| {:else if $infiniteSamples.isSuccess && displayedSamples.length === 0} | ||
| <!-- Empty state --> | ||
| <div class="flex h-full w-full items-center justify-center"> | ||
| <div class="text-center text-muted-foreground"> | ||
| <div class="mb-2 text-sm font-medium">No samples available</div> | ||
| <div class="text-xs">No samples found for this classifier.</div> | ||
| </div> | ||
| </div> | ||
| {:else} | ||
| <!-- Main grid content --> | ||
| <div class="viewport h-full w-full" bind:this={viewport}> | ||
| {#if displayedSamples.length > 0} | ||
| <Grid | ||
| itemCount={displayedSamples.length} | ||
| itemHeight={sampleHeight + GridGap} | ||
| itemWidth={sampleWidth + GridGap} | ||
| height={viewportHeight} | ||
| class="overflow-none overflow-y-auto dark:[color-scheme:dark]" | ||
| style="--sample-width: {sampleWidth}px; --sample-height: {sampleHeight}px;" | ||
| overScan={5} | ||
| > | ||
| {#snippet item({ index, style }: { index: number; style: string })} | ||
| {#key $infiniteSamples.dataUpdatedAt} | ||
| {#if displayedSamples[index]} | ||
| <div | ||
| class="relative cursor-pointer" | ||
| class:sample-selected={$classifierSelectedSampleIds.has( | ||
| displayedSamples[index].sample_id | ||
| )} | ||
| {style} | ||
| data-testid="classifier-sample-grid-item" | ||
| data-sample-id={displayedSamples[index].sample_id} | ||
| data-sample-name={displayedSamples[index].file_name} | ||
| data-index={index} | ||
| onclick={handleOnClick} | ||
| ondblclick={handleOnDoubleClick} | ||
| onkeydown={handleKeyDown} | ||
| aria-label={`Select sample: ${displayedSamples[index].file_name}`} | ||
| role="button" | ||
| tabindex="0" | ||
| > | ||
| <div class="absolute inset-0 z-10"> | ||
| <SelectableBox | ||
| onSelect={() => undefined} | ||
| isSelected={$classifierSelectedSampleIds.has( | ||
| displayedSamples[index].sample_id | ||
| )} | ||
| /> | ||
| </div> | ||
|
|
||
| <SampleImage sample={displayedSamples[index]} {objectFit} /> | ||
| </div> | ||
| {/if} | ||
| {/key} | ||
| {/snippet} | ||
| </Grid> | ||
| {:else} | ||
| <div class="flex h-full w-full items-center justify-center"> | ||
| <div class="text-sm text-muted-foreground">No samples to display</div> | ||
| </div> | ||
| {/if} | ||
| </div> | ||
| {/if} | ||
|
|
||
| <style> | ||
| .viewport { | ||
| overflow-y: hidden; | ||
| } | ||
|
|
||
| .sample-selected { | ||
| outline: drop-shadow(1px 1px 1px hsl(var(--primary))) | ||
| drop-shadow(1px -1px 1px hsl(var(--primary))) | ||
| drop-shadow(-1px -1px 1px hsl(var(--primary))) | ||
| drop-shadow(-1px 1px 1px hsl(var(--primary))); | ||
| } | ||
| </style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.