Skip to content

Commit

Permalink
Fix a few UI issues with concepts & UI bugs (#1179)
Browse files Browse the repository at this point in the history
- Add a modal for adding to a concept that is not active.
- Change the delete a row from backspace => ctrl+backspace.

https://huggingface.co/spaces/lilacai/nikhil_staging


https://github.com/lilacai/lilac/assets/1100749/1fd9b02a-e486-4f41-bd96-5d69431d4a18
  • Loading branch information
nsthorat authored Feb 14, 2024
1 parent 8fb42a8 commit 9360374
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@

<style lang="postcss">
:global(.drop-pill .bx--list-box__menu) {
max-height: 26rem !important;
width: unset;
}
:global(.drop-pill.drop-pill-left .bx--list-box__menu) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@
<style>
.text-container {
min-width: 4rem;
max-width: 16rem;
max-width: fit-content;
}
</style>
34 changes: 17 additions & 17 deletions web/blueprint/src/lib/components/concepts/ConceptView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -142,23 +142,6 @@
{/if}
</div>
</Expandable>

{#if $embeddings.data}
<Expandable expanded>
<div slot="above" class="text-md font-semibold">Metrics</div>
<div slot="below" class="model-metrics flex flex-wrap gap-x-4 gap-y-4">
{#each $embeddings.data as embedding}
{@const model = $conceptModels.data?.find(m => m.embedding_name == embedding.name)}
<ConceptMetrics
{concept}
embedding={embedding.name}
{model}
isFetching={$conceptModels.isFetching}
/>
{/each}
</div>
</Expandable>
{/if}
<Expandable>
<div slot="above" class="text-md font-semibold">Collect labels</div>
<div slot="below" class="w-full">
Expand All @@ -176,6 +159,23 @@
{/if}
</div>
</Expandable>
{#if $embeddings.data}
<Expandable expanded>
<div slot="above" class="text-md font-semibold">Metrics</div>
<div slot="below" class="model-metrics flex flex-wrap gap-x-4 gap-y-4">
{#each $embeddings.data as embedding}
{@const model = $conceptModels.data?.find(m => m.embedding_name == embedding.name)}
<ConceptMetrics
{concept}
embedding={embedding.name}
{model}
isFetching={$conceptModels.isFetching}
/>
{/each}
</div>
</Expandable>
{/if}

<div class="flex gap-x-4">
<div class="flex w-0 flex-grow flex-col gap-y-4">
<span class="flex items-center gap-x-2 text-lg"
Expand Down
232 changes: 197 additions & 35 deletions web/blueprint/src/lib/components/datasetView/ItemMediaTextContent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Component that renders string spans as an absolute positioned
* layer, meant to be rendered on top of the source text.
*/
import {conceptIdentifier, conceptLink} from '$lib/utils';
import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api';
import {onDestroy, onMount} from 'svelte';
Expand All @@ -13,9 +14,9 @@
MONACO_OPTIONS,
getMonaco
} from '$lib/monaco';
import {editConceptMutation} from '$lib/queries/conceptQueries';
import {editConceptMutation, queryConcepts} from '$lib/queries/conceptQueries';
import type {DatasetViewStore} from '$lib/stores/datasetViewStore';
import {conceptLink} from '$lib/utils';
import {getNotificationsContext} from '$lib/stores/notificationsStore';
import {getSearches} from '$lib/view_utils';
import {
L,
Expand All @@ -33,8 +34,21 @@
type SemanticSimilaritySignal,
type SubstringSignal
} from '$lilac';
import {SkeletonText} from 'carbon-components-svelte';
import {
ComposedModal,
ModalBody,
ModalFooter,
ModalHeader,
SkeletonText,
TextArea,
Toggle
} from 'carbon-components-svelte';
import type {
DropdownItem,
DropdownItemId
} from 'carbon-components-svelte/types/Dropdown/Dropdown.svelte';
import {derived} from 'svelte/store';
import DropdownPill from '../common/DropdownPill.svelte';
import {getMonacoRenderSpans, type MonacoRenderSpan, type SpanValueInfo} from './spanHighlight';
export let text: string | null | undefined;
// The full row item.
Expand Down Expand Up @@ -92,6 +106,7 @@
}
}
const notificationStore = getNotificationsContext();
const conceptEdit = editConceptMutation();
const addConceptLabel = (
conceptNamespace: string,
Expand All @@ -101,7 +116,17 @@
) => {
if (!conceptName || !conceptNamespace)
throw Error('Label could not be added, no active concept.');
$conceptEdit.mutate([conceptNamespace, conceptName, {insert: [{text, label}]}]);
$conceptEdit.mutate([conceptNamespace, conceptName, {insert: [{text, label}]}], {
onSuccess: () => {
notificationStore.addNotification({
kind: 'success',
title: `Added ${
label ? 'positive' : 'negative'
} example to concept ${conceptNamespace}/${conceptName}`,
message: text
});
}
});
};
let editorContainer: HTMLElement;
Expand Down Expand Up @@ -331,42 +356,109 @@
}
}
// Add the concept actions to the right-click menu.
// Add to concept.
let addToConceptText: string | undefined = undefined;
function addToConceptTextChanged(e: Event) {
addToConceptText = (e.target as HTMLInputElement).value;
}
let addToConceptSelectedConcept: string | undefined = undefined;
let addToConceptPositive = true;
function selectConceptFromModal(
e: CustomEvent<{
selectedId: DropdownItemId;
selectedItem: DropdownItem;
}>
) {
addToConceptSelectedConcept = e.detail.selectedId;
}
function addToConceptFromModal() {
if (addToConceptSelectedConcept == null || addToConceptText == null) return;
const [conceptNamespace, conceptName] = addToConceptSelectedConcept.split('/');
addConceptLabel(conceptNamespace, conceptName, addToConceptText, addToConceptPositive);
addToConceptText = undefined;
}
const conceptQuery = queryConcepts();
$: concepts = $conceptQuery.data;
let conceptsInMenu: Set<string>;
let addToConceptItems: DropdownItem[] = [];
$: {
if (editor != null && searches != null) {
for (const search of searches) {
if (concepts != null) {
conceptsInMenu = new Set<string>();
for (const concept of concepts) {
if (concept.namespace == 'lilac') continue;
conceptsInMenu.add(conceptIdentifier(concept.namespace, concept.name));
}
for (const search of searches || []) {
if (search.type == 'concept') {
const idAdd = `add-positive-to-concept-${search.concept_name}`;
if (editor.getAction(idAdd) != null) continue;
editor.addAction({
id: idAdd,
label: `👍 add as positive to concept "${search.concept_name}"`,
contextMenuGroupId: 'navigation_concepts',
precondition: conceptActionKeyId(search.concept_namespace, search.concept_name),
run: () => {
const selection = getEditorSelection();
if (selection == null) return;
const label = true;
addConceptLabel(search.concept_namespace, search.concept_name, selection, label);
}
});
editor.addAction({
id: 'add-negative-to-concept',
label: `👎 add as negative to concept "${search.concept_name}"`,
contextMenuGroupId: 'navigation_concepts',
precondition: conceptActionKeyId(search.concept_namespace, search.concept_name),
run: () => {
const selection = getEditorSelection();
if (selection == null) return;
const label = false;
addConceptLabel(search.concept_namespace, search.concept_name, selection, label);
}
});
conceptsInMenu.add(conceptIdentifier(search.concept_namespace, search.concept_name));
}
}
}
addToConceptItems = Array.from(conceptsInMenu).map(concept => ({
id: concept,
text: concept
}));
}
// Add the concept actions to the right-click menu.
$: {
if (editor != null && concepts != null) {
for (const concept of conceptsInMenu) {
const [conceptNamespace, conceptName] = concept.split('/');
const idPositive = `add-positive-to-concept-${conceptNamespace}/${conceptName}`;
if (editor.getAction(idPositive) != null) continue;
editor.addAction({
id: idPositive,
label: `👍 add as positive to concept "${conceptName}"`,
contextMenuGroupId: 'navigation_concepts',
precondition: conceptActionKeyId(conceptNamespace, conceptName),
run: () => {
const selection = getEditorSelection();
if (selection == null) return;
const label = true;
addConceptLabel(conceptNamespace, conceptName, selection, label);
}
});
const idNegative = `add-negative-to-concept-${conceptNamespace}/${conceptName}`;
if (editor.getAction(idNegative) != null) continue;
editor.addAction({
id: idNegative,
label: `👎 add as negative to concept "${conceptName}"`,
contextMenuGroupId: 'navigation_concepts',
precondition: conceptActionKeyId(conceptNamespace, conceptName),
run: () => {
const selection = getEditorSelection();
if (selection == null) return;
const label = false;
addConceptLabel(conceptNamespace, conceptName, selection, label);
}
});
}
}
}
$: {
if (editor != null) {
const idAddToConcept = 'add-to-concept';
if (editor.getAction(idAddToConcept) == null) {
editor.addAction({
id: 'add-to-concept',
label: `➕ Add to concept`,
contextMenuOrder: 1000,
contextMenuGroupId: 'navigation_concepts',
run: () => {
const selection = getEditorSelection();
if (selection == null) return;
addToConceptSelectedConcept = addToConceptItems[0].id;
addToConceptText = selection;
}
});
}
}
}
// Add the search actions to the right-click menu.
Expand Down Expand Up @@ -682,6 +774,73 @@
{/if}
</div>

<div class="add-concept-modal">
<ComposedModal
size="sm"
open={addToConceptText != null}
selectorPrimaryFocus=".bx--btn--primary"
on:submit={addToConceptFromModal}
on:close={() => (addToConceptText = undefined)}
>
<ModalHeader title="Add to concept" />
<ModalBody hasForm>
<div class="flex w-full flex-col gap-y-8 pt-4">
<section class="flex flex-col gap-y-4">
<div class="text-md text-gray-700">Concept</div>
<div class="flex flex-row items-center gap-x-4">
<div>
<DropdownPill
title="Choose a concept"
items={addToConceptItems}
on:select={selectConceptFromModal}
selectedId={addToConceptSelectedConcept}
let:item
>
{@const groupByItem = addToConceptItems?.find(x => x === item)}
{#if groupByItem}
<div class="flex items-center justify-between gap-x-1">
<span title={groupByItem.text} class="truncate text-sm">{groupByItem.text}</span
>
</div>
{/if}
</DropdownPill>
</div>
<div>
<Toggle
labelA={'Negative'}
labelB={'Positive'}
bind:toggled={addToConceptPositive}
hideLabel
/>
</div>
</div>
</section>
</div>
<div class="mt-8">
<section class="flex flex-col gap-y-4">
<div class="text-md text-gray-700">Text</div>

<TextArea
value={addToConceptText || undefined}
on:input={addToConceptTextChanged}
rows={6}
placeholder="Enter text for the concept"
class="mb-2 w-full "
/>
</section>
</div>
</ModalBody>
<ModalFooter
primaryButtonText={'Save'}
secondaryButtonText="Close"
primaryButtonDisabled={addToConceptSelectedConcept == null}
on:click:button--secondary={() => {
addToConceptText = undefined;
}}
/>
</ComposedModal>
</div>

<style lang="postcss">
/** Keyword search */
:global(.keyword-search-bg) {
Expand Down Expand Up @@ -728,4 +887,7 @@
:global(.editor-container .monaco-editor .lines-content.monaco-editor-background) {
margin-left: 10px;
}
:global(.add-concept-modal .bx--modal-container) {
@apply min-h-fit;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
return;
}
if (key.code === 'Delete' || key.code === 'Backspace') {
if (key.ctrlKey && (key.code === 'Delete' || key.code === 'Backspace')) {
openDeleteModal = true;
} else {
// Find the key code in the label keyboard shortcuts.
Expand Down

0 comments on commit 9360374

Please sign in to comment.