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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ msal.cache
artifacts/
.vs/
project-cache.json
preferences.json
localResourcesCache/

#Verify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import {cn} from '$lib/utils';
import type {Snippet} from 'svelte';
import Headwords from './Headwords.svelte';
import {useViewService} from '$lib/views/view-service.svelte';
let {
entry,
showLinks = false,
Expand All @@ -16,6 +17,7 @@
highlightSenseId = undefined,
hideExamples = false,
inline = false,
respectView = true,
...restProps
}: HTMLAttributes<HTMLDivElement> & {
entry: IEntry;
Expand All @@ -27,13 +29,22 @@
hideExamples?: boolean;
/** Render senses as one flowing line (no line break per sense) — for compact previews */
inline?: boolean;
/** false shows every writing system, not just the current view's */
respectView?: boolean;
} = $props();

$effect(() => {
lines = entry.senses.length > 1 ? entry.senses.length + 1 : 1;
});

const wsService = useWritingSystemService();
const viewService = useViewService();
const vernacularWs = $derived(
respectView ? wsService.viewVernacularNoAudio(viewService.currentView) : wsService.vernacularNoAudio,
);
const analysisWs = $derived(
(respectView ? wsService.viewAnalysis(viewService.currentView) : wsService.analysis).filter((ws) => !ws.isAudio),
);

let senses = $derived(entry.senses.map(getRenderedContent));

Expand All @@ -47,8 +58,7 @@
return {
id: sense.id,
partOfSpeech: partsOfSpeech.current.find((pos) => pos.id === sense.partOfSpeechId)?.label,
glossesAndDefs: wsService.analysis
.filter((ws) => !ws.isAudio)
glossesAndDefs: analysisWs
.map((ws) => ({
wsId: ws.wsId,
wsAbbr: ws.abbreviation,
Expand All @@ -60,18 +70,14 @@
exampleSentences: sense.exampleSentences.map((example) => ({
id: example.id,
sentences: [
...wsService.vernacular
.filter((ws) => !ws.isAudio)
.map((ws) => ({
text: asString(example.sentence[ws.wsId]),
color: wsService.wsColor(ws.wsId, 'vernacular'),
})),
...wsService.analysis
.filter((ws) => !ws.isAudio)
.map((ws) => ({
text: asString(example.translations[0]?.text?.[ws.wsId]),
color: wsService.wsColor(ws.wsId, 'analysis'),
})),
...vernacularWs.map((ws) => ({
text: asString(example.sentence[ws.wsId]),
color: wsService.wsColor(ws.wsId, 'vernacular'),
})),
...analysisWs.map((ws) => ({
text: asString(example.translations[0]?.text?.[ws.wsId]),
color: wsService.wsColor(ws.wsId, 'analysis'),
})),
].filter(({text}) => !!text),
})),
};
Expand Down Expand Up @@ -104,7 +110,7 @@
<div class="float-right group-not-[&:hover]/container:invisible relative -top-1">
{@render actions?.()}
</div>
<Headwords {entry} class={cn('mr-1', headwordClass)} />
<Headwords {entry} {respectView} class={cn('mr-1', headwordClass)} />
{#each senses as sense, i (sense.id)}
{#if senses.length > 1}
{#if inline}
Expand Down
22 changes: 17 additions & 5 deletions frontend/viewer/src/lib/components/dictionary/Headwords.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
<script lang="ts">
import type {IEntry} from '$lib/dotnet-types';
import type {IEntry, IWritingSystem} from '$lib/dotnet-types';
import {cn} from '$lib/utils';
import type {HTMLAttributes} from 'svelte/elements';
import {useWritingSystemService} from '$project/data';
import {useViewService} from '$lib/views/view-service.svelte';

let {
entry,
class: className,
placeholder,
respectView = true,
...restProps
}: HTMLAttributes<HTMLElement> & {
entry: IEntry;
placeholder?: string;
/** false shows every text vernacular, not just the current view's */
respectView?: boolean;
} = $props();

const wsService = useWritingSystemService();
const viewService = useViewService();

let headwords = $derived.by(() => {
return wsService.vernacular
.filter((ws) => !ws.isAudio)
function headwordsIn(writingSystems: IWritingSystem[]) {
return writingSystems
.map((ws) => ({
wsId: ws.wsId,
value: wsService.headword(entry, ws.wsId),
color: wsService.wsColor(ws.wsId, 'vernacular'),
}))
.filter(({value}) => !!value);
});
}

const viewHeadwords = $derived(
respectView
? headwordsIn(wsService.viewVernacularNoAudio(viewService.currentView))
: headwordsIn(wsService.vernacularNoAudio),
);
// Nothing in the view's writing systems: show the forms the entry does have; their colours say which
const headwords = $derived(viewHeadwords.length ? viewHeadwords : headwordsIn(wsService.vernacularNoAudio));
</script>

<!-- wrap-break-word: a headword can be one long unbreakable word, which otherwise overflows its container. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
const collapsedSelectionPreview = $derived.by(() => {
if (!selectedEntry) return '';
if (!selectedSense) {
return writingSystemService.headword(selectedEntry) || '';
return writingSystemService.viewBestHeadword(selectedEntry, viewService.currentView) || '';
}
return writingSystemService.firstGloss(selectedSense) || writingSystemService.firstDef(selectedSense) || '';
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
});
const hasExactWordMatch = $derived(!!matches?.some((match) => match.kind === 'same-word'));
const previewHeadwords = $derived(
[...new Set((matches ?? []).map((match) => writingSystemService.headword(match.entry)).filter(Boolean))].join(', '),
[...new Set((matches ?? []).map((match) => writingSystemService.firstHeadword(match.entry)).filter(Boolean))].join(', '),
);
const summaryMessage = $derived.by(() => {
if (hasExactWordMatch)
Expand Down Expand Up @@ -263,7 +263,8 @@
onclick={() => (expandedEntryId = isExpanded ? undefined : match.entry.id)}
>
<div class="grow min-w-0 text-sm {isExpanded ? '' : 'line-clamp-1'}">
<DictionaryEntry entry={match.entry} inline={!isExpanded} hideExamples={!isExpanded} />
<!-- Matches are found across all writing systems, so hiding any could hide the evidence -->
<DictionaryEntry entry={match.entry} inline={!isExpanded} hideExamples={!isExpanded} respectView={false} />
</div>
<Badge
variant="outline"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
const component: IComplexFormComponent = {
id: randomId(),
complexFormEntryId: entry.id,
complexFormHeadword: writingSystemService.headword(entry),
complexFormHeadword: writingSystemService.firstHeadword(entry),
componentEntryId: selection.entry.id,
componentSenseId: selection.sense?.id,
componentHeadword: writingSystemService.headword(selection.entry),
componentHeadword: writingSystemService.firstHeadword(selection.entry),
};
value = [...value, component];
onchange?.(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
const complexForm: IComplexFormComponent = {
id: randomId(),
complexFormEntryId: selection.entry.id,
complexFormHeadword: writingSystemService.headword(selection.entry),
complexFormHeadword: writingSystemService.firstHeadword(selection.entry),
componentEntryId: entry.id,
componentHeadword: writingSystemService.headword(entry),
componentHeadword: writingSystemService.firstHeadword(entry),
};
value = [...value, complexForm];
onchange?.(value);
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/lib/sandbox/Sandbox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
<div>
{#each selectedEntryHistory as selected (selected.entry.id)}
<p>
Entry: {writingSystemService.headword(selected.entry)}
Entry: {writingSystemService.firstHeadword(selected.entry)}
{#if selected.sense}
Sense: {writingSystemService.firstGloss(selected.sense)}
{/if}
Expand Down
13 changes: 10 additions & 3 deletions frontend/viewer/src/project/browse/BrowseView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import UnreadCommentBadge from '$project/browse/filter/UnreadCommentBadge.svelte';
import {QueryParamStateBool} from '$lib/utils/url.svelte';
import {watch} from 'runed';
import {useWritingSystemService} from '$project/data';

const projectContext = useProjectContext();
const viewService = useViewService();
const writingSystemService = useWritingSystemService();
const dialogsService = useDialogsService();
const features = useFeatures();
const entryListViewMode = useProjectStorage().entryListViewMode;
Expand All @@ -49,9 +51,14 @@
}, false);
let sort = $state<SortConfig>();
// Writing system to sort/display by, chosen separately from the sort field/direction.
// Undefined = the default vernacular (how it works today).
// Undefined = the current view's first vernacular.
let sortWs = $state<string>();
const sortWithWs = $derived<SortConfig | undefined>(sort ? {...sort, writingSystem: sortWs} : undefined);
// The user's pick is kept, but only applies while the current view shows that writing system.
const effectiveSortWs = $derived.by(() => {
const viewWritingSystems = writingSystemService.viewVernacularNoAudio(viewService.currentView);
return viewWritingSystems.find(ws => ws.wsId === sortWs)?.wsId ?? viewWritingSystems[0]?.wsId;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
const sortWithWs = $derived<SortConfig | undefined>(sort ? {...sort, writingSystem: effectiveSortWs} : undefined);
const entryMode: EntryListViewMode = $derived(entryListViewMode.current === 'preview' ? 'preview' : 'simple');

// Turning the filter on means the comments are what the user came for, so open the
Expand Down Expand Up @@ -98,7 +105,7 @@
<div class="my-2 flex items-center gap-2">
<SortMenu bind:value={sort}
autoSelector={() => search ? SortField.SearchRelevance : SortField.Headword} />
<SortWritingSystemMenu bind:value={sortWs} />
<SortWritingSystemMenu bind:value={() => effectiveSortWs, (v) => sortWs = v} />
{#if features.comments}
<UnreadCommentBadge bind:unreadComments/>
{/if}
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/project/browse/EntryMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
children?: Snippet
}>();

const headword = $derived((entry && writingSystemService.headword(entry)) || $t`Untitled`);
const headword = $derived((entry && writingSystemService.viewBestHeadword(entry, viewService.currentView)) || $t`Untitled`);

let open = $state(false);

Expand Down
18 changes: 16 additions & 2 deletions frontend/viewer/src/project/browse/EntryRow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type {WithoutChildrenOrChild} from 'bits-ui';
import type {Snippet} from 'svelte';
import {t} from 'svelte-i18n-lingui';
import {useViewService} from '$lib/views/view-service.svelte';

interface Props extends WithoutChildrenOrChild<ListItemProps> {
entry?: IEntry;
Expand All @@ -27,7 +28,8 @@

const writingSystemService = useWritingSystemService();
const partOfSpeechService = usePartsOfSpeech();
const sensePreview = $derived(writingSystemService.firstDefOrGlossVal(entry?.senses?.[0]));
const viewService = useViewService();
const sensePreview = $derived(writingSystemService.viewFirstDefOrGlossVal(entry?.senses?.[0], viewService.currentView));
const partOfSpeech = $derived(entry?.senses?.[0]?.partOfSpeech);

// Generate random widths for skeleton UI elements
Expand All @@ -43,6 +45,10 @@

// Calculate animation delay based on index (staggered effect)
const animationDelay = `${(Math.random() * 5) * 0.15}s`;

const defaultWs = $derived(sortWs
?? writingSystemService.viewVernacularNoAudio(viewService.currentView)[0]?.wsId
?? '');
</script>

<ListItem bind:ref {...rest}>
Expand All @@ -55,8 +61,16 @@
{:else if previewDictionary}
<DictionaryEntry {entry}/>
{:else}
<!-- Show the WS abbreviation if the headword we display does not match what the user is expecting -->
{@const best = writingSystemService.viewBestHeadwordIn(entry, viewService.currentView, defaultWs)}
{@const fallbackWs = best && best.ws.wsId !== defaultWs ? best.ws : undefined}
<h2 class="font-medium text-2xl flex min-w-0 justify-between items-center gap-2">
<span class="min-w-0 wrap-break-word">{writingSystemService.bestHeadword(entry, sortWs) || $t`Untitled`}</span>
<span class="min-w-0 wrap-break-word">
{#if fallbackWs}
<span class="-mr-1 relative -bottom-px text-xs font-normal text-foreground/80" title={fallbackWs.name}>{fallbackWs.abbreviation || fallbackWs.wsId}</span>
{/if}
{best?.value || $t`Untitled`}
</span>
{@render badge?.()}
</h2>
{#if entry.senses.length}
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/project/browse/EntryView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
let latestPersistedSnapshot = $state<ReadonlyDeep<IEntry>>();
const entryPersistence = new EntryPersistence(() => latestPersistedSnapshot);
let entry = $derived(entryResource.current ?? undefined);
const headword = $derived((entry && writingSystemService.headword(entry)) || $t`Untitled`);
const headword = $derived((entry && writingSystemService.viewBestHeadword(entry, viewService.currentView)) || $t`Untitled`);
const loadingDebounced = new Debounced(() => entryResource.loading, 50);
const dictionaryPreview: DictionaryPreviewMode = $derived(
isDictionaryPreviewMode(dictionaryPreviewStorage.current) ? dictionaryPreviewStorage.current : 'show'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
import {cn} from '$lib/utils';
import {useWritingSystemService} from '$project/data';
import {Button, buttonVariants} from '$lib/components/ui/button';
import {useViewService} from '$lib/views/view-service.svelte';

type Props = {
/** Selected writing system id to sort/display by. Undefined = the default vernacular. */
/** Selected writing system id to sort/display by. Undefined = the current view's first vernacular. */
value?: string;
};

let {value = $bindable()}: Props = $props();

const writingSystemService = useWritingSystemService();
// Vernacular writing systems, excluding audio — the ones a headword can sort by.
const writingSystems = $derived(writingSystemService.vernacularNoAudio);
const selectedWsId = $derived(value ?? writingSystemService.defaultVernacular?.wsId);
const viewService = useViewService();
// The current view's vernacular writing systems, excluding audio — the ones a headword can sort by.
const writingSystems = $derived(writingSystemService.viewVernacularNoAudio(viewService.currentView));
const selectedWsId = $derived(value ?? writingSystems[0]?.wsId);
const selectedWs = $derived(writingSystems.find(ws => ws.wsId === selectedWsId));
</script>

Expand Down
Loading
Loading