From de8961dc54912a1131535aa9745e8cd2f7bb08ee Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 16:43:01 +0700 Subject: [PATCH 01/14] remove a bunch of dead stuff --- frontend/viewer/src/App.svelte | 2 - frontend/viewer/src/CrdtProjectView.svelte | 10 - frontend/viewer/src/FwDataProjectView.svelte | 48 --- frontend/viewer/src/FwDataWebComponent.svelte | 46 --- .../viewer/src/SvelteUxProjectView.svelte | 387 ------------------ frontend/viewer/src/lib/Editor.svelte | 70 ---- frontend/viewer/src/lib/HomeButton.svelte | 7 - frontend/viewer/src/lib/RightToolbar.svelte | 72 ---- .../src/lib/entry-editor/FieldTitle.svelte | 33 -- .../field-editors/FieldEditor.svelte | 49 --- .../lib/entry-editor/inputs/CrdtField.svelte | 146 ------- .../entry-editor/inputs/CrdtTextField.svelte | 43 -- .../object-editors/EntityEditor.svelte | 10 - .../Lexbox.ClientServer.Hubs.ts | 16 - .../Lexbox.ClientServer.Hubs.ts | 22 - .../TypedSignalR.Client/index.ts | 339 --------------- .../viewer/src/lib/layout/AppBarMenu.svelte | 70 ---- .../lib/layout/DictionaryEntryViewer.svelte | 35 -- .../viewer/src/lib/layout/EntryList.svelte | 149 ------- .../src/lib/layout/FieldListDialog.svelte | 55 --- .../src/lib/layout/IndexCharacters.svelte | 66 --- frontend/viewer/src/lib/layout/Scotty.svelte | 52 --- frontend/viewer/src/lib/layout/Toc.svelte | 43 -- .../src/lib/layout/ViewOptionsDrawer.svelte | 76 ---- .../src/lib/search-bar/SearchBar.svelte | 190 --------- .../viewer/src/lib/services/lexbox-api.ts | 29 -- .../lib/services/selected-entry-service.ts | 16 - .../lib/services/service-provider-signalr.ts | 106 ----- .../viewer/src/lib/status/SaveStatus.svelte | 119 ------ .../writing-system/WritingSystemEditor.svelte | 13 +- frontend/viewer/src/web-component.ts | 1 - 31 files changed, 6 insertions(+), 2314 deletions(-) delete mode 100644 frontend/viewer/src/CrdtProjectView.svelte delete mode 100644 frontend/viewer/src/FwDataProjectView.svelte delete mode 100644 frontend/viewer/src/FwDataWebComponent.svelte delete mode 100644 frontend/viewer/src/SvelteUxProjectView.svelte delete mode 100644 frontend/viewer/src/lib/Editor.svelte delete mode 100644 frontend/viewer/src/lib/HomeButton.svelte delete mode 100644 frontend/viewer/src/lib/RightToolbar.svelte delete mode 100644 frontend/viewer/src/lib/entry-editor/FieldTitle.svelte delete mode 100644 frontend/viewer/src/lib/entry-editor/field-editors/FieldEditor.svelte delete mode 100644 frontend/viewer/src/lib/entry-editor/inputs/CrdtField.svelte delete mode 100644 frontend/viewer/src/lib/entry-editor/inputs/CrdtTextField.svelte delete mode 100644 frontend/viewer/src/lib/entry-editor/object-editors/EntityEditor.svelte delete mode 100644 frontend/viewer/src/lib/generated-signalr-client/Lexbox.ClientServer.Hubs.ts delete mode 100644 frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs.ts delete mode 100644 frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/index.ts delete mode 100644 frontend/viewer/src/lib/layout/AppBarMenu.svelte delete mode 100644 frontend/viewer/src/lib/layout/DictionaryEntryViewer.svelte delete mode 100644 frontend/viewer/src/lib/layout/EntryList.svelte delete mode 100644 frontend/viewer/src/lib/layout/FieldListDialog.svelte delete mode 100644 frontend/viewer/src/lib/layout/IndexCharacters.svelte delete mode 100644 frontend/viewer/src/lib/layout/Scotty.svelte delete mode 100644 frontend/viewer/src/lib/layout/Toc.svelte delete mode 100644 frontend/viewer/src/lib/layout/ViewOptionsDrawer.svelte delete mode 100644 frontend/viewer/src/lib/search-bar/SearchBar.svelte delete mode 100644 frontend/viewer/src/lib/services/lexbox-api.ts delete mode 100644 frontend/viewer/src/lib/services/selected-entry-service.ts delete mode 100644 frontend/viewer/src/lib/services/service-provider-signalr.ts delete mode 100644 frontend/viewer/src/lib/status/SaveStatus.svelte diff --git a/frontend/viewer/src/App.svelte b/frontend/viewer/src/App.svelte index 7735a3c790..b1c57c1f0e 100644 --- a/frontend/viewer/src/App.svelte +++ b/frontend/viewer/src/App.svelte @@ -19,7 +19,6 @@ import DotnetProjectView from './DotnetProjectView.svelte'; import {setupGlobalErrorHandlers} from '$lib/errors/global-errors'; import {ModeWatcher} from 'mode-watcher'; - import {initScottyPortalContext} from '$lib/layout/Scotty.svelte'; let url = ''; @@ -69,7 +68,6 @@ /* eslint-enable @typescript-eslint/naming-convention */ setupGlobalErrorHandlers(); - initScottyPortalContext(); diff --git a/frontend/viewer/src/CrdtProjectView.svelte b/frontend/viewer/src/CrdtProjectView.svelte deleted file mode 100644 index 5cbbeb0e3a..0000000000 --- a/frontend/viewer/src/CrdtProjectView.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/frontend/viewer/src/FwDataProjectView.svelte b/frontend/viewer/src/FwDataProjectView.svelte deleted file mode 100644 index 14f30d5fa7..0000000000 --- a/frontend/viewer/src/FwDataProjectView.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/frontend/viewer/src/FwDataWebComponent.svelte b/frontend/viewer/src/FwDataWebComponent.svelte deleted file mode 100644 index ed615bda4a..0000000000 --- a/frontend/viewer/src/FwDataWebComponent.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - {css} - - -
- -
diff --git a/frontend/viewer/src/SvelteUxProjectView.svelte b/frontend/viewer/src/SvelteUxProjectView.svelte deleted file mode 100644 index 8e2d2b2c09..0000000000 --- a/frontend/viewer/src/SvelteUxProjectView.svelte +++ /dev/null @@ -1,387 +0,0 @@ - - - - {projectName} - - - -{#if projectLoaded} -{#if !readonly} - -{/if} -
- -
-

{projectName}

-
-
-
- -
- -
- {#if $features.write} -
- -
- {/if} - -
-
- navigateToEntry(e.detail.entry, e.detail.search)} - {projectName} - createNew={!readonly} - on:createNew={(e) => openNewEntryDialog(e.detail)} /> -
-
-
-
- {#if !readonly} - openNewEntryDialog()} /> - {/if} - {#if $features.feedback && fwLiteConfig.feedbackUrl} - - {/if} -
-
- showOptionsDialog = true} {about} /> -
-
-
-
-
-
- $state.userPickedEntry = true} /> -
-
- {#if $selectedEntry} -
- -
- { - updateEntryInList($selectedEntry = e.detail.entry); - $entries = $entries; - }} - on:delete={onEntryDeleted} /> - {:else} -
- No entry selected - {#if !readonly} - openNewEntryDialog()} /> - {/if} -
- {/if} -
- showOptionsDialog = true} /> -
-
- - -
-{/if} diff --git a/frontend/viewer/src/lib/Editor.svelte b/frontend/viewer/src/lib/Editor.svelte deleted file mode 100644 index 1aa0e1ef1b..0000000000 --- a/frontend/viewer/src/lib/Editor.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - -
- {#key entry.id} - - {/key} -
- - diff --git a/frontend/viewer/src/lib/HomeButton.svelte b/frontend/viewer/src/lib/HomeButton.svelte deleted file mode 100644 index 8fad32da5d..0000000000 --- a/frontend/viewer/src/lib/HomeButton.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - - - {/if} - - - - {$currentView.label} - - - - - - - - {/if} - - {#if unacceptedChanges} - - {/if} - - - diff --git a/frontend/viewer/src/lib/entry-editor/inputs/CrdtTextField.svelte b/frontend/viewer/src/lib/entry-editor/inputs/CrdtTextField.svelte deleted file mode 100644 index 8d209eb9dc..0000000000 --- a/frontend/viewer/src/lib/entry-editor/inputs/CrdtTextField.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - - onEditorValueChange(e.detail.inputValue)} - on:blur={(e) => {if (e.target) save()}} - actions={(el) => { - addTitleToLabel(el); - // autofocus requires a delay otherwise it won't work in a dialog - // also we can't use the autofocus attribute because of https://github.com/techniq/svelte-ux/issues/532 - return autofocus ? [autoFocusFunc(el, {delay: 5})] : []; - }} - value={editorValue} - disabled={readonly} - class="ws-field gap-0 text-right" - classes={{ root: `${hasHadValue.pushAndGet(editorValue) ? '' : 'unused'} ${readonly ? 'readonly' : ''}`, input: 'field-input', container: 'field-container', label: 'pr-2' }} - {label} - {labelPlacement} - {placeholder}> - - - diff --git a/frontend/viewer/src/lib/entry-editor/object-editors/EntityEditor.svelte b/frontend/viewer/src/lib/entry-editor/object-editors/EntityEditor.svelte deleted file mode 100644 index 15f758f0e4..0000000000 --- a/frontend/viewer/src/lib/entry-editor/object-editors/EntityEditor.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -{#each customFieldConfigs as fieldConfig} - -{/each} diff --git a/frontend/viewer/src/lib/generated-signalr-client/Lexbox.ClientServer.Hubs.ts b/frontend/viewer/src/lib/generated-signalr-client/Lexbox.ClientServer.Hubs.ts deleted file mode 100644 index 04bf8ba650..0000000000 --- a/frontend/viewer/src/lib/generated-signalr-client/Lexbox.ClientServer.Hubs.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* THIS (.ts) FILE IS GENERATED BY Tapper */ -/* eslint-disable */ -/* tslint:disable */ - -/** Transpiled from Lexbox.ClientServer.Hubs.JsonOperation */ -export type JsonOperation = { - /** Transpiled from string */ - op: string; - /** Transpiled from string */ - path: string; - /** Transpiled from object? */ - value?: any; - /** Transpiled from string? */ - from?: string; -} - diff --git a/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs.ts b/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs.ts deleted file mode 100644 index 69dac37931..0000000000 --- a/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* THIS (.ts) FILE IS GENERATED BY TypedSignalR.Client.TypeScript */ -/* eslint-disable */ -/* tslint:disable */ - - -import type {IEntry, IMiniLcmJsInvokable} from '$lib/dotnet-types'; - -export type ILexboxApiHub = IMiniLcmJsInvokable; - -export type ILexboxClient = { - /** - * @param entry Transpiled from lexboxClientContracts.Entry - * @returns Transpiled from System.Threading.Tasks.Task - */ - OnEntryUpdated(entry: IEntry): Promise; - OnProjectClosed(reason: CloseReason): Promise; -} - -export enum CloseReason { - User = 0, - Locked = 1, -} diff --git a/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/index.ts b/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/index.ts deleted file mode 100644 index 2515f822fd..0000000000 --- a/frontend/viewer/src/lib/generated-signalr-client/TypedSignalR.Client/index.ts +++ /dev/null @@ -1,339 +0,0 @@ -/* THIS (.ts) FILE IS GENERATED BY TypedSignalR.Client.TypeScript */ -/* eslint-disable */ -/* tslint:disable */ - -import type {ILexboxClient} from './Lexbox.ClientServer.Hubs'; - -import {HubConnection} from '@microsoft/signalr'; -import type { - IComplexFormType, IEntry, IExampleSentence, - ISense, - IPartOfSpeech, - ISemanticDomain, - IWritingSystem, - IWritingSystems, - WritingSystemType, - IQueryOptions, - IComplexFormComponent, - IMiniLcmFeatures, - IMiniLcmJsInvokable -} from '$lib/dotnet-types'; -import type { IPublication } from '$lib/dotnet-types/generated-types/MiniLcm/Models/IPublication'; - -// components - -export type Disposable = { - dispose(): void; -} - -export type HubProxyFactory = { - createHubProxy(connection: HubConnection): T; -} - -export type ReceiverRegister = { - register(connection: HubConnection, receiver: T): Disposable; -} - -type ReceiverMethod = { - methodName: string, - method: (...args: any[]) => void -} - -class ReceiverMethodSubscription implements Disposable { - - public constructor( - private connection: HubConnection, - private receiverMethod: ReceiverMethod[]) { - } - - public readonly dispose = () => { - for (const it of this.receiverMethod) { - this.connection.off(it.methodName, it.method); - } - }; -} - -// API - -export type HubProxyFactoryProvider = { - (hubType: 'ILexboxApiHub'): HubProxyFactory; -} - -export const getHubProxyFactory = ((hubType: string) => { - if (hubType === 'ILexboxApiHub') { - return IMiniLcmJsInvokable_HubProxyFactory.Instance; - } -}) as HubProxyFactoryProvider; - -export type ReceiverRegisterProvider = { - (receiverType: 'ILexboxClient'): ReceiverRegister; -} - -export const getReceiverRegister = ((receiverType: string) => { - if (receiverType === 'ILexboxClient') { - return ILexboxClient_Binder.Instance; - } -}) as ReceiverRegisterProvider; - -// HubProxy - -class IMiniLcmJsInvokable_HubProxyFactory implements HubProxyFactory { - public static Instance = new IMiniLcmJsInvokable_HubProxyFactory(); - - private constructor() { - } - - public readonly createHubProxy = (connection: HubConnection): IMiniLcmJsInvokable => { - return new IMiniLcmJsInvokable_HubProxy(connection); - }; -} - -class IMiniLcmJsInvokable_HubProxy implements IMiniLcmJsInvokable { - - public constructor(private connection: HubConnection) { - } - - public readonly getWritingSystems = async (): Promise => { - return await this.connection.invoke("GetWritingSystems"); - } - - public readonly createWritingSystem = async (type: WritingSystemType, writingSystem: IWritingSystem): Promise => { - return await this.connection.invoke("CreateWritingSystem", type, writingSystem); - } - - public readonly getPartsOfSpeech = async (): Promise => { - return new Promise((resolve, reject) => { - let partsOfSpeech: IPartOfSpeech[] = []; - this.connection.stream('GetPartsOfSpeech').subscribe({ - next(value: IPartOfSpeech) { - partsOfSpeech.push(value); - }, - error(err: any) { - reject(err); - }, - complete() { - resolve(partsOfSpeech); - } - }); - }); - } - - public readonly getSemanticDomains = async (): Promise => { - return new Promise((resolve, reject) => { - let semanticDomains: ISemanticDomain[] = []; - this.connection.stream('GetSemanticDomains').subscribe({ - next(value: ISemanticDomain) { - semanticDomains.push(value); - }, - error(err: any) { - reject(err); - }, - complete() { - resolve(semanticDomains); - } - }); - }); - } - - public readonly getComplexFormTypes = async (): Promise => { - return new Promise((resolve, reject) => { - let complexFormTypes: IComplexFormType[] = []; - this.connection.stream('GetComplexFormTypes').subscribe({ - next(value: IComplexFormType) { - complexFormTypes.push(value); - }, - error(err: any) { - reject(err); - }, - complete() { - resolve(complexFormTypes); - } - }); - }); - } - - public readonly getEntries = async (options: IQueryOptions): Promise => { - return await this.connection.invoke("GetEntries", options); - } - - public readonly searchEntries = (query: string, options: IQueryOptions): Promise => { - return new Promise((resolve, reject) => { - let entries: IEntry[] = []; - this.connection.stream('SearchEntries', query, options).subscribe({ - next(value: IEntry) { - entries.push(value); - }, - error(err: any) { - reject(err); - }, - complete() { - resolve(entries); - } - }); - }); - }; - - public readonly getEntry = async (id: string): Promise => { - return await this.connection.invoke("GetEntry", id); - } - - public readonly createEntry = async (entry: IEntry): Promise => { - return await this.connection.invoke("CreateEntry", entry); - } - - public readonly updateEntry = async (before: IEntry, after: IEntry): Promise => { - return await this.connection.invoke("UpdateEntry", before, after); - } - - public readonly deleteEntry = async (id: string): Promise => { - return await this.connection.invoke("DeleteEntry", id); - } - - public readonly createSense = async (entryId: string, sense: ISense): Promise => { - return await this.connection.invoke("CreateSense", entryId, sense); - } - - public readonly deleteSense = async (entryId: string, senseId: string): Promise => { - return await this.connection.invoke("DeleteSense", entryId, senseId); - } - - public readonly createExampleSentence = async (entryId: string, senseId: string, exampleSentence: IExampleSentence): Promise => { - return await this.connection.invoke("CreateExampleSentence", entryId, senseId, exampleSentence); - } - - public readonly deleteExampleSentence = async (entryId: string, senseId: string, exampleSentenceId: string): Promise => { - return await this.connection.invoke("DeleteExampleSentence", entryId, senseId, exampleSentenceId); - } - - supportedFeatures(): Promise { - throw new Error('Method not implemented.'); - } - - getComplexFormType(id: string): Promise { - throw new Error('Method not implemented.'); - } - - getSense(entryId: string, id: string): Promise { - throw new Error('Method not implemented.'); - } - - getPartOfSpeech(id: string): Promise { - throw new Error('Method not implemented.'); - } - - getSemanticDomain(id: string): Promise { - throw new Error('Method not implemented.'); - } - - getExampleSentence(entryId: string, senseId: string, id: string): Promise { - throw new Error('Method not implemented.'); - } - - updateWritingSystem(before: IWritingSystem, after: IWritingSystem): Promise { - throw new Error('Method not implemented.'); - } - - createPartOfSpeech(partOfSpeech: IPartOfSpeech): Promise { - throw new Error('Method not implemented.'); - } - - updatePartOfSpeech(before: IPartOfSpeech, after: IPartOfSpeech): Promise { - throw new Error('Method not implemented.'); - } - - deletePartOfSpeech(id: string): Promise { - throw new Error('Method not implemented.'); - } - - createSemanticDomain(semanticDomain: ISemanticDomain): Promise { - throw new Error('Method not implemented.'); - } - - updateSemanticDomain(before: ISemanticDomain, after: ISemanticDomain): Promise { - throw new Error('Method not implemented.'); - } - - deleteSemanticDomain(id: string): Promise { - throw new Error('Method not implemented.'); - } - - createComplexFormType(complexFormType: IComplexFormType): Promise { - throw new Error('Method not implemented.'); - } - - updateComplexFormType(before: IComplexFormType, after: IComplexFormType): Promise { - throw new Error('Method not implemented.'); - } - - deleteComplexFormType(id: string): Promise { - throw new Error('Method not implemented.'); - } - - createComplexFormComponent(complexFormComponent: IComplexFormComponent): Promise { - throw new Error('Method not implemented.'); - } - - deleteComplexFormComponent(complexFormComponent: IComplexFormComponent): Promise { - throw new Error('Method not implemented.'); - } - - addComplexFormType(entryId: string, complexFormTypeId: string): Promise { - throw new Error('Method not implemented.'); - } - - removeComplexFormType(entryId: string, complexFormTypeId: string): Promise { - throw new Error('Method not implemented.'); - } - - updateSense(entryId: string, before: ISense, after: ISense): Promise { - throw new Error('Method not implemented.'); - } - - addSemanticDomainToSense(senseId: string, semanticDomain: ISemanticDomain): Promise { - throw new Error('Method not implemented.'); - } - - removeSemanticDomainFromSense(senseId: string, semanticDomainId: string): Promise { - throw new Error('Method not implemented.'); - } - - updateExampleSentence(entryId: string, senseId: string, before: IExampleSentence, after: IExampleSentence): Promise { - throw new Error('Method not implemented.'); - } - - getPublications(): Promise { - throw new Error('Method not implemented.'); - } - - dispose(): Promise { - throw new Error('Method not implemented.'); - } -} - - -// Receiver - -class ILexboxClient_Binder implements ReceiverRegister { - - public static Instance = new ILexboxClient_Binder(); - - private constructor() { - } - - public readonly register = (connection: HubConnection, receiver: ILexboxClient): Disposable => { - - const __onEntryUpdated = (...args: Parameters) => receiver.OnEntryUpdated(...args); - const __onProjectClosed = (...args: Parameters) => receiver.OnProjectClosed(...args); - - connection.on("OnEntryUpdated", __onEntryUpdated); - connection.on("OnProjectClosed", __onProjectClosed); - - const methodList: ReceiverMethod[] = [ - { methodName: "OnEntryUpdated", method: __onEntryUpdated }, - { methodName: "OnProjectClosed", method: __onProjectClosed }, - ] - - return new ReceiverMethodSubscription(connection, methodList); - } -} - diff --git a/frontend/viewer/src/lib/layout/AppBarMenu.svelte b/frontend/viewer/src/lib/layout/AppBarMenu.svelte deleted file mode 100644 index 5acc8776d2..0000000000 --- a/frontend/viewer/src/lib/layout/AppBarMenu.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - - -{#key $projectViewState.userPickedEntry} - - - - - -{/key} - -{#if about} - -{/if} - - - diff --git a/frontend/viewer/src/lib/layout/DictionaryEntryViewer.svelte b/frontend/viewer/src/lib/layout/DictionaryEntryViewer.svelte deleted file mode 100644 index 6245242d66..0000000000 --- a/frontend/viewer/src/lib/layout/DictionaryEntryViewer.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
-
-
- -
-
- {#if dictionaryEntryLines > 2} -
- - diff --git a/frontend/viewer/src/lib/layout/EntryList.svelte b/frontend/viewer/src/lib/layout/EntryList.svelte deleted file mode 100644 index 5d1691382c..0000000000 --- a/frontend/viewer/src/lib/layout/EntryList.svelte +++ /dev/null @@ -1,149 +0,0 @@ - - -
-
- -
- -
- {#if loading} - - {/if} -
-
-
- - -
-
-
- {#if !entries || entries.length == 0} -
- No entries found - {#if $selectedCharacter} - in - - {/if} -
- {:else} - - {#each visibleItems as entry (entry.id)} -
- {#if dictionaryMode} - - {:else} - selectEntry(entry)} - noShadow - class="!rounded-none" - /> - {/if} -
- {/each} -
- {/if} -
-
-
- - diff --git a/frontend/viewer/src/lib/layout/FieldListDialog.svelte b/frontend/viewer/src/lib/layout/FieldListDialog.svelte deleted file mode 100644 index e96b4f0fbd..0000000000 --- a/frontend/viewer/src/lib/layout/FieldListDialog.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - -
- -
-
- {#each filteredFields as field} - - {:else} -
- No matching fields -
- {/each} -
-
-
actions
-
- - diff --git a/frontend/viewer/src/lib/layout/IndexCharacters.svelte b/frontend/viewer/src/lib/layout/IndexCharacters.svelte deleted file mode 100644 index 5df70de0cb..0000000000 --- a/frontend/viewer/src/lib/layout/IndexCharacters.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - -{#if characters?.length} - - -
- {#if $selectedCharacter} - - - - {/if} - {#each characters ?? [] as character} - - - - {/each} -
-
- -
-{/if} - - diff --git a/frontend/viewer/src/lib/layout/Scotty.svelte b/frontend/viewer/src/lib/layout/Scotty.svelte deleted file mode 100644 index b20478dbc9..0000000000 --- a/frontend/viewer/src/lib/layout/Scotty.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/frontend/viewer/src/lib/layout/Toc.svelte b/frontend/viewer/src/lib/layout/Toc.svelte deleted file mode 100644 index dfb49b536f..0000000000 --- a/frontend/viewer/src/lib/layout/Toc.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -{#key entry?.id} - {#if entry} - {@const _headword = writingSystemService.headword(entry) ?? ''} - {@const locationWithoutHash = window.location.href.split('#')[0]} -
- {fieldName({id: 'entry'}, $currentView.i18nKey)}:{_headword || '—'} - {#each entry.senses as sense, i (sense.id)} - {@const _sense = writingSystemService.firstDefOrGlossVal(sense) ?? ''} - {fieldName({id: 'sense'}, $currentView.i18nKey)}:{_sense || '—'} - {#each sense.exampleSentences as example, j (example.id)} - {@const _example = writingSystemService.firstSentenceOrTranslationVal(example) ?? ''} - Example:{_example || '—'} - {/each} - {/each} -
- {/if} -{/key} - - diff --git a/frontend/viewer/src/lib/layout/ViewOptionsDrawer.svelte b/frontend/viewer/src/lib/layout/ViewOptionsDrawer.svelte deleted file mode 100644 index d2103ea907..0000000000 --- a/frontend/viewer/src/lib/layout/ViewOptionsDrawer.svelte +++ /dev/null @@ -1,76 +0,0 @@ - - - -
-
-
- ({ value: view.label, label: view.label, group: view.label }))} - value={activeView.label} - on:change={({detail}) => { - // We can't use the view itself as the value, because it gets stringified - // and contains circular references - const view = views.find((view) => view.label === detail.value); - if (view) activeView = view; - }} - classes={{root: 'view-select w-auto', options: 'view-select-options', field: { container: 'border-surface-content/20' }}} - clearable={false} - labelPlacement="top" - clearSearchOnOpen={false} - fieldActions={(elem) => /* a hack to disable typing/filtering */ {elem.readOnly = true; return [];}} - search={() => /* a hack to always show all options */ Promise.resolve()}> - -
- -
- -
- -
- Debug - - - -
-
-
-
- - diff --git a/frontend/viewer/src/lib/search-bar/SearchBar.svelte b/frontend/viewer/src/lib/search-bar/SearchBar.svelte deleted file mode 100644 index 5e5f1a4107..0000000000 --- a/frontend/viewer/src/lib/search-bar/SearchBar.svelte +++ /dev/null @@ -1,190 +0,0 @@ - - - - - $search = ''} - classes={{root: 'items-start', title: 'px-2 py-0 max-md:pl-0', dialog: 'md:h-auto md:max-h-[min(50rem, 100%)]'}}> -
- - { - if (e.key === 'Enter' && $displayedEntries.length > 0) { - e.preventDefault(); - selectEntry($displayedEntries[0]); - } - }} - placeholder={`Find ${fieldName({id: 'entry'}, $currentView.i18nKey).toLowerCase()}...`} - class="flex-grow-[2] cursor-pointer opacity-80 hover:opacity-100" - classes={{ prepend: 'text-sm', append: 'flex-row-reverse', container: 'border-surface-content/20' }} - icon={mdiBookSearchOutline}> -
- {#if $loading} - - {/if} -
-
-
-
-
- {#each $displayedEntries as entry} - selectEntry(entry)} - /> - {/each} -
- {#if $displayedEntries.length === 0} -
- {#if $result.search} - No entries found - {:else} - {#if $loading} - - {:else} - Search for an entry - {/if} - {/if} -
- {/if} - {#if $result.search && createNew} - { - dispatch('createNew', $result.search ?? ''); - $showSearchDialog = false; - }} - /> - {/if} - {#if $result.entries.length > $displayedEntries.length} -
- {$result.entries.length - $displayedEntries.length} - {#if $result.entries.length === fetchCount}+{/if} -
- more matching entries... - -
-
- {/if} -
-
diff --git a/frontend/viewer/src/lib/services/lexbox-api.ts b/frontend/viewer/src/lib/services/lexbox-api.ts deleted file mode 100644 index 096cb19d49..0000000000 --- a/frontend/viewer/src/lib/services/lexbox-api.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { - IComplexFormType, - IEntry, - IExampleSentence, - ISense, - IPartOfSpeech, - IQueryOptions, - ISemanticDomain, - IWritingSystem, - IWritingSystems, - IMiniLcmJsInvokable, - WritingSystemType -} from '$lib/dotnet-types'; - - -export type { - IComplexFormType, - IEntry, - IExampleSentence, - ISense, - IPartOfSpeech, - IQueryOptions, - ISemanticDomain, - IWritingSystem, - IWritingSystems, - WritingSystemType -}; - -export type LexboxApiClient = IMiniLcmJsInvokable; diff --git a/frontend/viewer/src/lib/services/selected-entry-service.ts b/frontend/viewer/src/lib/services/selected-entry-service.ts deleted file mode 100644 index 8472ab6642..0000000000 --- a/frontend/viewer/src/lib/services/selected-entry-service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {IEntry} from '$lib/dotnet-types'; -import {derived, get, type Readable} from 'svelte/store'; - -/** - * Returns a store like selectedEntry, but only emits when a completely different entry is selected (or no entry is selected), - * rather than when the currently selected entry experiences a change (which may result in selectedEntry emitting). - */ -export function getSelectedEntryChangedStore(selectedEntry: Readable): Readable { - let previousSelectedEntry = get(selectedEntry); - return derived(selectedEntry, ($selectedEntry, set) => { - if (previousSelectedEntry?.id !== $selectedEntry?.id) { - previousSelectedEntry = $selectedEntry; - set($selectedEntry); - } - }); -} diff --git a/frontend/viewer/src/lib/services/service-provider-signalr.ts b/frontend/viewer/src/lib/services/service-provider-signalr.ts deleted file mode 100644 index 6c6cfe156d..0000000000 --- a/frontend/viewer/src/lib/services/service-provider-signalr.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {getHubProxyFactory, getReceiverRegister} from '../generated-signalr-client/TypedSignalR.Client'; - -import { - type HubConnection, - HubConnectionBuilder, - HubConnectionState, - type IHttpConnectionOptions -} from '@microsoft/signalr'; -import type {LexboxApiClient} from './lexbox-api'; -import {onDestroy} from 'svelte'; -import {type Readable, type Writable, writable} from 'svelte/store'; -import {AppNotification} from '../notifications/notifications'; -import type { - CloseReason, -} from '../generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs'; -import {useEventBus} from './event-bus'; -import {type IMiniLcmFeatures, type IEntry, type IMiniLcmJsInvokable} from '$lib/dotnet-types'; -import {initProjectContext} from '$lib/project-context.svelte'; - -// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -type ErrorContext = {error: Error|unknown, methodName?: string, origin: 'method'|'connection'}; -type ErrorHandler = (errorContext: ErrorContext) => {handled: boolean}; -export function SetupSignalR( - url: string, - features: IMiniLcmFeatures, - onError?: ErrorHandler, - options: IHttpConnectionOptions = {}) : { connected: Readable, lexboxApi: LexboxApiClient } { - const {connection, connected} = setupConnection(url, options, errorContext => { - if (onError && onError(errorContext).handled) { - return {handled: true}; - } - if (errorContext.error instanceof Error) { - const message = errorContext.error.message; - AppNotification.display('Connection error: ' + message, 'error', 'long'); - } else { - AppNotification.display('Unknown Connection error', 'error', 'long'); - } - return {handled: true}; - }); - const hubFactory = getHubProxyFactory('ILexboxApiHub'); - const hubProxy = hubFactory.createHubProxy(connection); - - const lexboxApiHubProxy = Object.assign(hubProxy, { - supportedFeatures(): Promise { - return Promise.resolve(features); - } - } satisfies Partial); - const changeEventBus = useEventBus(); - getReceiverRegister('ILexboxClient').register(connection, { - OnProjectClosed(reason: CloseReason): Promise { - changeEventBus.notifyProjectClosed(reason); - return Promise.resolve(); - }, - OnEntryUpdated(entry: IEntry): Promise { - changeEventBus.notifyEntryUpdated(entry); - return Promise.resolve(); - } - }); - initProjectContext({api: lexboxApiHubProxy, projectName: 'todo-change-me', projectCode: 'todo-change-me'}); - return {connected, lexboxApi: lexboxApiHubProxy}; -} - -function setupConnection(url: string, options: IHttpConnectionOptions, onError: ErrorHandler): {connection: HubConnection, connected: Writable} { - const connected = writable(false) - let connection = new HubConnectionBuilder() - .withUrl(url, options) - .withAutomaticReconnect() - .build(); - - if (import.meta.env.DEV) connection.serverTimeoutInMilliseconds = 60_000 * 10; - console.debug('SignalR connection timeout', connection.serverTimeoutInMilliseconds); - - onDestroy(() => connection.stop()); - connection.onclose((error) => { - connected.set(false); - if (!error) return; - console.error('Connection closed', error); - onError({error, origin: 'connection'}); - }); - void connection.start() - .then(() => connected.set(connection.state == HubConnectionState.Connected)) - .catch(err => { - onError({error: err, origin: 'connection'}); - console.error('error connecting to signalr', err); - }); - connection = new Proxy(connection, { - get(target, prop: keyof HubConnection, receiver) { - if (prop === 'invoke') { - /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */ - return async (methodName: string, ...args: any[]) => { - try { - return await target.invoke(methodName, ...args); - } catch (e) { - onError({error: e, methodName, origin: 'method'}); - throw e; - } - } - /* eslint-enable */ - } else { - return Reflect.get(target, prop, receiver); - } - } - }); - return {connection, connected}; -} diff --git a/frontend/viewer/src/lib/status/SaveStatus.svelte b/frontend/viewer/src/lib/status/SaveStatus.svelte deleted file mode 100644 index 98806163bb..0000000000 --- a/frontend/viewer/src/lib/status/SaveStatus.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - - -
-
- -
- {#if lastSaved} - {@const savedAgo = Date.now() - lastSaved.getTime()} - Last saved - {#if savedAgo < 30_000} - a few seconds - {:else if savedAgo < 90_000} - a minute - {:else} - {humanizeDuration({ duration: { milliseconds: savedAgo }, minUnits: DurationUnits.Minute })} - {/if} - ago - - {:else if lastStatus === 'saved-to-disk'} - - Saved to disk - - - {:else if lastStatus === 'failed-to-save'} - Save failed - {/if} -
-
- -
-
-
diff --git a/frontend/viewer/src/lib/writing-system/WritingSystemEditor.svelte b/frontend/viewer/src/lib/writing-system/WritingSystemEditor.svelte index 71fe031e62..49fcd82e4f 100644 --- a/frontend/viewer/src/lib/writing-system/WritingSystemEditor.svelte +++ b/frontend/viewer/src/lib/writing-system/WritingSystemEditor.svelte @@ -1,9 +1,8 @@ 
- onChange()} bind:value={writingSystem.wsId}/> + - onChange()} bind:value={writingSystem.name}/> - onChange()} bind:value={writingSystem.abbreviation}/> - onChange()} bind:value={writingSystem.font}/> + + + {#if newWs} - + {:else} Changes are saved automatically {/if} diff --git a/frontend/viewer/src/web-component.ts b/frontend/viewer/src/web-component.ts index 528d870f76..15a55bc86d 100644 --- a/frontend/viewer/src/web-component.ts +++ b/frontend/viewer/src/web-component.ts @@ -1,4 +1,3 @@ export * from './lib/services/service-provider-dotnet'; export { default as WebComponent } from './WebComponent.svelte'; -export { default as FwDataWebComponent } from './FwDataWebComponent.svelte'; From db8d54266bed0fbddeadd4102b36623015a64e36 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 16:49:37 +0700 Subject: [PATCH 02/14] remove config-data and types, move some useful code to where it was used --- frontend/viewer/src/lib/config-data.ts | 77 ------------------- frontend/viewer/src/lib/config-types.ts | 72 ----------------- frontend/viewer/src/lib/i18n.ts | 3 +- .../src/lib/writing-system-service.svelte.ts | 7 +- 4 files changed, 8 insertions(+), 151 deletions(-) delete mode 100644 frontend/viewer/src/lib/config-data.ts delete mode 100644 frontend/viewer/src/lib/config-types.ts diff --git a/frontend/viewer/src/lib/config-data.ts b/frontend/viewer/src/lib/config-data.ts deleted file mode 100644 index 5c2955aa09..0000000000 --- a/frontend/viewer/src/lib/config-data.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { - BaseEntityFieldConfig, - CustomFieldConfig, - FieldConfig, -} from './config-types'; -import type { IEntry, IExampleSentence, ISense } from '$lib/dotnet-types'; - -import type { I18nType } from './i18n'; -import type { ConditionalPickDeep, ValueOf } from 'type-fest'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const allFieldConfigs = ({ - entry: { - lexemeForm: { id: 'lexemeForm', type: 'multi', ws: 'vernacular', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Entry_level_fields/Lexeme_Form_field.htm' }, - citationForm: { id: 'citationForm', type: 'multi', ws: 'vernacular', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Entry_level_fields/Citation_Form_field.htm' }, - literalMeaning: { id: 'literalMeaning', type: 'rich-multi', ws: 'vernacular', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Entry_level_fields/Literal_Meaning_field.htm' }, - note: { id: 'note', type: 'rich-multi', ws: 'analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Entry_level_fields/Note_field.htm' }, - }, - customEntry: { - // custom1: { id: 'entry-custom-001', type: 'multi', ws: 'vernacular', name: 'Custom 1', custom: true }, - }, - sense: { - gloss: { id: 'gloss', type: 'multi', ws: 'analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/Gloss_field_Sense.htm' }, - definition: { id: 'definition', type: 'rich-multi', ws: 'analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/definition_field.htm' }, - partOfSpeechId: { id: 'partOfSpeechId', type: 'option', optionType: 'part-of-speech', ws: 'first-analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/Grammatical_Info_field.htm' }, - semanticDomains: { id: 'semanticDomains', type: 'multi-option', optionType: 'semantic-domain', ws: 'first-analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/semantic_domains_field.htm' } - }, - customSense: { - // custom1: { id: 'sense-custom-001', type: 'multi', ws: 'first-analysis', name: 'Custom sense', custom: true }, - }, - example: { - sentence: { id: 'sentence', type: 'rich-multi', ws: 'vernacular', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/example_field.htm' }, - translation: { id: 'translation', type: 'rich-multi', ws: 'analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/Translation_field.htm' }, - reference: { id: 'reference', type: 'rich-single', ws: 'first-analysis', helpId: 'User_Interface/Field_Descriptions/Lexicon/Lexicon_Edit_fields/Sense_level_fields/Reference_field.htm' }, - }, - customExample: { - }, -} as const) satisfies { - entry: Record>, - customEntry: Record, - sense: Record>, - customSense: Record, - example: Record>, - customExample: Record, -}; - -type FieldOptionType = T['optionType']; -type OptionFields = ValueOf>>; -export type WellKnownSingleOptionType = FieldOptionType; -export type WellKnownMultiOptionType = FieldOptionType; - -export function allFields(viewConfig: ViewConfig): Readonly { - return [ - ...Object.values(viewConfig.entry), - ...Object.values(viewConfig.customEntry ?? {}), - ...Object.values(viewConfig.sense), - ...Object.values(viewConfig.customSense ?? {}), - ...Object.values(viewConfig.example), - ...Object.values(viewConfig.customExample ?? {}), - ] as const; -} - -type FieldsWithViewConfigProps>> = - { [K in keyof T]: FieldWithViewConfigProps }; - -type FieldWithViewConfigProps> = T; - -interface ViewConfig { - label: string, - i18n?: I18nType, - entry: Partial>, - sense: Partial, - example: Partial, - customEntry?: Partial, - customSense?: Partial, - customExample?: Partial, -} diff --git a/frontend/viewer/src/lib/config-types.ts b/frontend/viewer/src/lib/config-types.ts deleted file mode 100644 index 0bcb6294e5..0000000000 --- a/frontend/viewer/src/lib/config-types.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { - IEntry, - IExampleSentence, - IMultiString, IRichMultiString, - ISemanticDomain, - ISense -} from '$lib/dotnet-types'; - -import type {ConditionalKeys} from 'type-fest'; -import type { IRichString } from '$lib/dotnet-types/generated-types/MiniLcm/Models/IRichString'; - -export type WritingSystemType = 'vernacular' | 'analysis'; -export type WritingSystemSelection = WritingSystemType | `first-${WritingSystemType}` | 'vernacular-analysis' | 'analysis-vernacular'; -export type FieldType = 'rich-multi' | 'multi' | 'rich-single' | 'single' | 'option' | 'multi-option'; -export type WellKnownFieldId = Exclude - -export type BaseFieldConfig = { - type: FieldType; - id: string; - name?: string; - extra?: boolean; - helpId?: string; - ws: WritingSystemSelection; - readonly?: true; -} - -export type CustomFieldConfig = BaseFieldConfig & { - name: string; - custom: true; -} - -export type OptionFieldConfig = { - type: `option`; - optionType: string; - ws: `first-${WritingSystemType}`; -} - -export type BaseEntityFieldConfig = (({ - type: 'multi'; - id: ConditionalKeys; -} | { - type: 'rich-multi'; - id: ConditionalKeys; -} | { - type: 'single'; - id: ConditionalKeys; - ws: `first-${WritingSystemType}`; -} | { - type: 'rich-single'; - id: ConditionalKeys; - ws: `first-${WritingSystemType}`; -} | { - type: 'option', - optionType: string, - id: ConditionalKeys, - ws: `first-${WritingSystemType}` -} | { - type: `multi-option`; - optionType: string; - id: ConditionalKeys; - ws: `first-${WritingSystemType}`; -}) & BaseFieldConfig & { - id: WellKnownFieldId -}); - -export type EntityFieldConfig = BaseEntityFieldConfig | BaseEntityFieldConfig | BaseEntityFieldConfig; -export type FieldConfig = EntityFieldConfig | CustomFieldConfig; - -export type LexboxPermissions = { - write: boolean, - comment: boolean, -} diff --git a/frontend/viewer/src/lib/i18n.ts b/frontend/viewer/src/lib/i18n.ts index 3da7ec3146..b1f5e9fc44 100644 --- a/frontend/viewer/src/lib/i18n.ts +++ b/frontend/viewer/src/lib/i18n.ts @@ -1,5 +1,5 @@ import type {FieldIds} from './entry-editor/field-data'; -import type {WellKnownFieldId} from './config-types'; +import type {IEntry, IExampleSentence, ISense} from '$lib/dotnet-types'; // type I18n = Record & Record, string>; type I18nKey = FieldIds; @@ -7,6 +7,7 @@ type I18nKey = FieldIds; * I18n type is used to specify which i18n group to use for a field. If empty, the default i18n is used. */ export type I18nType = 'fieldworks' | ''; +export type WellKnownFieldId = Exclude; const defaultI18n: Record = { // entry diff --git a/frontend/viewer/src/lib/writing-system-service.svelte.ts b/frontend/viewer/src/lib/writing-system-service.svelte.ts index 159137a780..88bba1babf 100644 --- a/frontend/viewer/src/lib/writing-system-service.svelte.ts +++ b/frontend/viewer/src/lib/writing-system-service.svelte.ts @@ -7,12 +7,17 @@ IWritingSystem, IWritingSystems } from '$lib/dotnet-types'; -import type {WritingSystemSelection} from './config-types'; import {firstTruthy} from './utils'; import {type ProjectContext, useProjectContext} from '$lib/project-context.svelte'; import {type ResourceReturn} from 'runed'; import type {IRichString} from '$lib/dotnet-types/generated-types/MiniLcm/Models/IRichString'; +export type WritingSystemType = 'vernacular' | 'analysis'; +export type WritingSystemSelection = + WritingSystemType + | `first-${WritingSystemType}` + | 'vernacular-analysis' + | 'analysis-vernacular'; const symbol = Symbol.for('fw-lite-ws-service'); export function useWritingSystemService(): WritingSystemService { const projectContext = useProjectContext(); From 4ae71702ef5b3ab33c72d8227d31f407d78a7dc3 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 16:49:44 +0700 Subject: [PATCH 03/14] remove some more dead code --- frontend/viewer/src/app.postcss | 2 - frontend/viewer/src/lib/commands.ts | 20 ----- frontend/viewer/src/lib/debug.ts | 3 - .../entry-editor/EntryOrSensePicker.postcss | 45 ----------- .../viewer/src/lib/entry-editor/field.postcss | 80 ------------------- 5 files changed, 150 deletions(-) delete mode 100644 frontend/viewer/src/lib/commands.ts delete mode 100644 frontend/viewer/src/lib/debug.ts delete mode 100644 frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.postcss delete mode 100644 frontend/viewer/src/lib/entry-editor/field.postcss diff --git a/frontend/viewer/src/app.postcss b/frontend/viewer/src/app.postcss index 3815af075f..4c8ab47477 100644 --- a/frontend/viewer/src/app.postcss +++ b/frontend/viewer/src/app.postcss @@ -1,6 +1,4 @@ @import 'theme.postcss'; -@import './lib/entry-editor/field.postcss'; -@import './lib/entry-editor/EntryOrSensePicker.postcss'; @import 'theme.postcss'; /* this import is removed by vite-plugin-webfont-dl, and the styles are inlined in webfonts.css */ @import 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap'; diff --git a/frontend/viewer/src/lib/commands.ts b/frontend/viewer/src/lib/commands.ts deleted file mode 100644 index f6b8b44fb7..0000000000 --- a/frontend/viewer/src/lib/commands.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {getContext, setContext} from 'svelte'; - -import type {IEntry} from '$lib/dotnet-types'; - -export type NewEntryDialogOptions = { - dontSelect?: true; -}; -export type ProjectCommands = { - createNewEntry: (headword: string, options?: NewEntryDialogOptions) => Promise, -}; - -const commandsContextName = 'projectCommands'; -export function initProjectCommands(commands: ProjectCommands): ProjectCommands { - setContext(commandsContextName, commands); - return commands; -} - -export function useProjectCommands(): ProjectCommands { - return getContext(commandsContextName); -} diff --git a/frontend/viewer/src/lib/debug.ts b/frontend/viewer/src/lib/debug.ts deleted file mode 100644 index 5310f560a8..0000000000 --- a/frontend/viewer/src/lib/debug.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {writable} from 'svelte/store'; - -export const generateExternalChanges = writable(false); diff --git a/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.postcss b/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.postcss deleted file mode 100644 index 8118d33eef..0000000000 --- a/frontend/viewer/src/lib/entry-editor/EntryOrSensePicker.postcss +++ /dev/null @@ -1,45 +0,0 @@ -/* These styles belong in EntryOrSensePicker.svelte, but the mixture of global and features like :dark and :is, are buggy if they're there 😞 */ -.entry-sense-picker { - - .Collapse { - @apply border-2 !border-t-2 rounded overflow-hidden m-0 transition-none; - } - - .entry:not(.disabled.disable-expand) .Collapse { - button:hover { - & * { - @apply bg-transparent; - } - } - } - - /* We need this, because there's a bug that adds aria-expanded="true" even when the expansion panel is disabled */ - .entry:not(.disable-expand) .Collapse[aria-expanded="true"] { - } - - .CollapseContent .ListItem { - @apply !rounded-none; - } - - .entry:is(.selected, :has(.selected)) .Collapse { - - } - - .selected.sense, .selected.entry button:not(.sense) { - - - & > * { - @apply bg-transparent; - } - } - - .disabled { - &.entry [slot="trigger"] { - @apply brightness-50 pointer-events-none; - } - - &.sense { - @apply brightness-50 pointer-events-none; - } - } -} diff --git a/frontend/viewer/src/lib/entry-editor/field.postcss b/frontend/viewer/src/lib/entry-editor/field.postcss deleted file mode 100644 index f1da2b226f..0000000000 --- a/frontend/viewer/src/lib/entry-editor/field.postcss +++ /dev/null @@ -1,80 +0,0 @@ -.field { - @apply grid grid-cols-subgrid items-center; - - &:not(:last-child) { - @apply mb-4; - } - - &.multi-field { - @apply items-baseline; - } - - .fields { - @apply grid grid-cols-subgrid col-span-2 gap-1; - } - - &.multi-field .fields { - .ws-field, .ws-field-wrapper { - @apply grid grid-cols-subgrid col-span-2; - } - } - - &.single-field .fields { - @apply grid grid-cols-subgrid; - grid-column-start: 3; - } - - .item-list-field { - grid-column-start: 3; - } - - &.TextField, .TextField { - [class~="border"] { - filter: contrast(0.95); - border-radius: 0; - border-style: none; - border-bottom-style: solid; - @apply !shadow-none; - border-color: hsl(var(--color-surface-content) / 30%); - } - } - - &.readonly.TextField, .readonly.TextField, .readonly .TextField { - /* prevent other "disabled-state styles" from impacting readability */ - opacity: 1; - - [class~="border"] { - border-color: hsl(var(--color-surface-content) / 20%); - background: transparent; - } - - input { - /* enable standard text selection */ - pointer-events: auto; - } - - .append, .prepend { - display: none; - } - } - - .field-title, .fields, .ws-field-wrapper, .ws-field, .item-list-field { - @apply sm-form:!col-span-full; - - & :has(> .field-container) { - @apply sm-form:col-span-2; - } - - &.field-title { - @apply sm-form:mb-2; - } - - &.item-list-field { - @apply sm-form:pl-0; - } - - &.ws-field > .label { - @apply xs-form:text-left; - } - } - } From ffc049a58ae82a211b949868cbf635edaee11759 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 16:51:28 +0700 Subject: [PATCH 04/14] move CloseReason into EventBus for now --- frontend/viewer/src/lib/services/event-bus.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/viewer/src/lib/services/event-bus.ts b/frontend/viewer/src/lib/services/event-bus.ts index 85a8e2441d..5c8b460c83 100644 --- a/frontend/viewer/src/lib/services/event-bus.ts +++ b/frontend/viewer/src/lib/services/event-bus.ts @@ -1,5 +1,4 @@ -import {type CloseReason} from '../generated-signalr-client/TypedSignalR.Client/Lexbox.ClientServer.Hubs'; -import {DotnetService, type IEntry} from '$lib/dotnet-types'; +import {DotnetService, type IEntry} from '$lib/dotnet-types'; import {useService} from '$lib/services/service-provider'; import type {IJsEventListener} from '$lib/dotnet-types/generated-types/FwLiteShared/Events/IJsEventListener'; import type {IFwEvent} from '$lib/dotnet-types/generated-types/FwLiteShared/Events/IFwEvent'; @@ -11,6 +10,11 @@ import {ProjectDataFormat} from '$lib/dotnet-types/generated-types/MiniLcm/Model import {type ProjectContext, useProjectContext} from '$lib/project-context.svelte'; import {onDestroy} from 'svelte'; +export enum CloseReason { + User = 0, + Locked = 1, +} + export class EventBus { private _onEvent = new Set<(event: IFwEvent) => void>(); private _onProjectClosed = new Set<(reason: CloseReason) => void>(); From 68406c1775740a126959dc10e1d0e604567739ca Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 17:08:34 +0700 Subject: [PATCH 05/14] replace some simple uses of svelte-ux code --- frontend/viewer/src/ProjectLoader.svelte | 5 +++-- .../src/lib/entry-editor/FieldHelpIcon.svelte | 5 ++--- .../src/lib/layout/ShowEmptyFieldsSwitch.svelte | 14 +++++++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/viewer/src/ProjectLoader.svelte b/frontend/viewer/src/ProjectLoader.svelte index fa3863da08..97abb21511 100644 --- a/frontend/viewer/src/ProjectLoader.svelte +++ b/frontend/viewer/src/ProjectLoader.svelte @@ -1,7 +1,7 @@ From 8700698019cf7cd22ff19a8ccaff4b86b1904732 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 17:12:56 +0700 Subject: [PATCH 06/14] remove EmptyFieldsSwitch since it's not doing anything for now. --- .../viewer/src/lib/history/HistoryView.svelte | 26 ++++++++----------- .../lib/layout/ShowEmptyFieldsSwitch.svelte | 15 ----------- 2 files changed, 11 insertions(+), 30 deletions(-) delete mode 100644 frontend/viewer/src/lib/layout/ShowEmptyFieldsSwitch.svelte diff --git a/frontend/viewer/src/lib/history/HistoryView.svelte b/frontend/viewer/src/lib/history/HistoryView.svelte index 3b18d889eb..f876b607b0 100644 --- a/frontend/viewer/src/lib/history/HistoryView.svelte +++ b/frontend/viewer/src/lib/history/HistoryView.svelte @@ -1,5 +1,4 @@  - - From 5b2c334d2588ce76abd85f3d8c592e35309a8bc0 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 17:17:17 +0700 Subject: [PATCH 07/14] shift help icon down a bit --- frontend/viewer/src/lib/entry-editor/FieldHelpIcon.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/viewer/src/lib/entry-editor/FieldHelpIcon.svelte b/frontend/viewer/src/lib/entry-editor/FieldHelpIcon.svelte index 253c821e61..48c524bac6 100644 --- a/frontend/viewer/src/lib/entry-editor/FieldHelpIcon.svelte +++ b/frontend/viewer/src/lib/entry-editor/FieldHelpIcon.svelte @@ -11,7 +11,7 @@ {#if href} - + {/if} From 128c90b61e8f0f861760f8b977e5ce034b74555e Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 4 Jun 2025 17:21:00 +0700 Subject: [PATCH 08/14] delete more dead code --- .../pull-to-refresh/pull-to-refresh.svelte | 77 ------------------- frontend/viewer/src/lib/utils/MapBind.svelte | 36 --------- .../viewer/src/lib/utils/MapBind.test.svelte | 14 ---- frontend/viewer/src/lib/utils/MapBind.test.ts | 49 ------------ frontend/viewer/src/lib/utils/size.ts | 6 -- frontend/viewer/src/lib/utils/test-utils.ts | 6 -- .../lib/views/project-view-state-service.ts | 29 ------- 7 files changed, 217 deletions(-) delete mode 100644 frontend/viewer/src/lib/components/pull-to-refresh/pull-to-refresh.svelte delete mode 100644 frontend/viewer/src/lib/utils/MapBind.svelte delete mode 100644 frontend/viewer/src/lib/utils/MapBind.test.svelte delete mode 100644 frontend/viewer/src/lib/utils/MapBind.test.ts delete mode 100644 frontend/viewer/src/lib/utils/size.ts delete mode 100644 frontend/viewer/src/lib/utils/test-utils.ts delete mode 100644 frontend/viewer/src/lib/views/project-view-state-service.ts diff --git a/frontend/viewer/src/lib/components/pull-to-refresh/pull-to-refresh.svelte b/frontend/viewer/src/lib/components/pull-to-refresh/pull-to-refresh.svelte deleted file mode 100644 index 97d7253bbe..0000000000 --- a/frontend/viewer/src/lib/components/pull-to-refresh/pull-to-refresh.svelte +++ /dev/null @@ -1,77 +0,0 @@ - - -
- {#if pulling} -
- {#if shouldRefresh} - - {:else} - - {/if} -
- {/if} - -
- {@render children?.()} -
-
diff --git a/frontend/viewer/src/lib/utils/MapBind.svelte b/frontend/viewer/src/lib/utils/MapBind.svelte deleted file mode 100644 index 06e7ca4265..0000000000 --- a/frontend/viewer/src/lib/utils/MapBind.svelte +++ /dev/null @@ -1,36 +0,0 @@ - diff --git a/frontend/viewer/src/lib/utils/MapBind.test.svelte b/frontend/viewer/src/lib/utils/MapBind.test.svelte deleted file mode 100644 index b382a5af37..0000000000 --- a/frontend/viewer/src/lib/utils/MapBind.test.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - (_a ? { id: _a } : undefined)} - unmap={(_b) => (_b ? _b.id : undefined)} -/> diff --git a/frontend/viewer/src/lib/utils/MapBind.test.ts b/frontend/viewer/src/lib/utils/MapBind.test.ts deleted file mode 100644 index 8365519701..0000000000 --- a/frontend/viewer/src/lib/utils/MapBind.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {expect, test, beforeEach} from 'vitest' -import { get, writable, type Writable } from 'svelte/store' - -import MapBindTest from './MapBind.test.svelte' -import {render} from '@testing-library/svelte' -import { tick } from 'svelte' - -let inStore: Writable; -let outStore: Writable<{ id: string } | undefined>; - -beforeEach(() => { - inStore = writable('World'); - outStore = writable<{ id: string } | undefined>(undefined); - render(MapBindTest, { - a: inStore, - b: outStore, - }); -}); - -test('syncs initial value from in->out', () => { - expect(get(inStore)).toBe('World'); - expect(get(outStore)).toStrictEqual({ id: 'World' }); -}); - - -test('syncs future values from in->out and out->in', async () => { - inStore.set('Hello'); - expect(get(inStore)).toBe('Hello'); - await tick(); - expect(get(outStore)).toStrictEqual({ id: 'Hello' }); - - outStore.set({ id: 'Goodbye' }); - expect(get(outStore)).toStrictEqual({ id: 'Goodbye' }); - await tick(); - expect(get(inStore)).toBe('Goodbye'); - - outStore.set(undefined); - await tick(); - expect(get(outStore)).toBe(undefined); - expect(get(inStore)).toBe(undefined); -}); - -test('in wins conflicts', async () => { - inStore.set('Nope'); - outStore.set({ id: 'Yes' }); - await tick(); - expect(get(outStore)).toStrictEqual({ id: 'Nope' }); - expect(get(inStore)).toBe('Nope'); -}); diff --git a/frontend/viewer/src/lib/utils/size.ts b/frontend/viewer/src/lib/utils/size.ts deleted file mode 100644 index db43645ae2..0000000000 --- a/frontend/viewer/src/lib/utils/size.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function getAvailableHeightForElement(elem: HTMLElement): number { - const elementIsBiggerThanScreenSpace = (elem.scrollHeight + elem.offsetTop) > window.innerHeight; - // Only grow if we need to, otherwise it looks weird when a footer gets pushed down - const growWithScroll = elementIsBiggerThanScreenSpace ? window.scrollY : 0; - return window.innerHeight - Math.max(0, elem.offsetTop - growWithScroll); -} diff --git a/frontend/viewer/src/lib/utils/test-utils.ts b/frontend/viewer/src/lib/utils/test-utils.ts deleted file mode 100644 index 4ca56e6773..0000000000 --- a/frontend/viewer/src/lib/utils/test-utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type {ComponentProps, SvelteComponent} from 'svelte'; - -export function getState(component: T): ComponentProps { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - return (component.$capture_state() as unknown as ComponentProps); -} diff --git a/frontend/viewer/src/lib/views/project-view-state-service.ts b/frontend/viewer/src/lib/views/project-view-state-service.ts deleted file mode 100644 index 17ea291548..0000000000 --- a/frontend/viewer/src/lib/views/project-view-state-service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {getContext, setContext} from 'svelte'; -import {type Readable, type Writable, writable} from 'svelte/store'; - -const projectViewStateContextName = 'project-view-state'; - -export type ProjectViewState = { - /** - * Tracks whether the user has explicitly collapsed the right toolbar. - * Used to inform the toolbar contents, so it can behave accordingly. - * 🤔 I suppose this could largely be handled by css container queries. - */ - rightToolbarCollapsed: boolean, - /** - * Tracks whether the user has explicitly picked an entry to view. - * (whether via the entry list, an entry-ID in the URL, creating a new entry etc.) - * Used on smaller screen e.g. mobile to determine whether to show the entry list or the entry editor. - */ - userPickedEntry: boolean, -}; - -export function initProjectViewState(defaultFeatures: ProjectViewState): Writable { - const projectViewStateStore = writable(defaultFeatures); - setContext>(projectViewStateContextName, projectViewStateStore); - return projectViewStateStore; -} - -export function useProjectViewState(): Readable { - return getContext>(projectViewStateContextName); -} From d7a840f3b27653794b98013d3cf760dade7fa57e Mon Sep 17 00:00:00 2001 From: Tim Haasdyk Date: Thu, 12 Jun 2025 21:24:12 +0200 Subject: [PATCH 09/14] Remove redundant import --- frontend/viewer/src/app.postcss | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/viewer/src/app.postcss b/frontend/viewer/src/app.postcss index 4c8ab47477..cd120d94bf 100644 --- a/frontend/viewer/src/app.postcss +++ b/frontend/viewer/src/app.postcss @@ -1,5 +1,4 @@ @import 'theme.postcss'; -@import 'theme.postcss'; /* this import is removed by vite-plugin-webfont-dl, and the styles are inlined in webfonts.css */ @import 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap'; @tailwind base; From 7e6c59c268690cf83a37230de60afa48d4688e8c Mon Sep 17 00:00:00 2001 From: Tim Haasdyk Date: Thu, 12 Jun 2025 21:24:32 +0200 Subject: [PATCH 10/14] Migrate writing-system-dialog (rough and untested) --- .../writing-system/WritingSystemDialog.svelte | 61 +++++++----- .../writing-system/WritingSystemEditor.svelte | 94 ++++++++++++------- 2 files changed, 96 insertions(+), 59 deletions(-) diff --git a/frontend/viewer/src/lib/writing-system/WritingSystemDialog.svelte b/frontend/viewer/src/lib/writing-system/WritingSystemDialog.svelte index f2e4f848a2..490ada22cb 100644 --- a/frontend/viewer/src/lib/writing-system/WritingSystemDialog.svelte +++ b/frontend/viewer/src/lib/writing-system/WritingSystemDialog.svelte @@ -1,24 +1,30 @@  - -
Edit Writing Systems
- - onCreate(e.detail.writingSystem)} - on:change={(e) => onChange(e.detail.writingSystem)}/> -
- -
-
+ + + + Edit Writing Systems + + + + + + + + {#if newWs} + + {:else} + Changes are saved automatically + {/if} From 40afb463beca67161e94f8a71ac507e133b0386c Mon Sep 17 00:00:00 2001 From: Tim Haasdyk Date: Thu, 12 Jun 2025 21:24:42 +0200 Subject: [PATCH 11/14] Remove dead class --- frontend/viewer/src/project/browse/EntryView.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/viewer/src/project/browse/EntryView.svelte b/frontend/viewer/src/project/browse/EntryView.svelte index 30786cfbe6..4bee53cb0b 100644 --- a/frontend/viewer/src/project/browse/EntryView.svelte +++ b/frontend/viewer/src/project/browse/EntryView.svelte @@ -1,7 +1,6 @@ diff --git a/frontend/viewer/src/lib/entry-editor/NewEntryButton.svelte b/frontend/viewer/src/lib/entry-editor/NewEntryButton.svelte index c505455e53..76bb8aa27d 100644 --- a/frontend/viewer/src/lib/entry-editor/NewEntryButton.svelte +++ b/frontend/viewer/src/lib/entry-editor/NewEntryButton.svelte @@ -1,13 +1,13 @@ diff --git a/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte b/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte index 4229eb55d8..58de1bc776 100644 --- a/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte +++ b/frontend/viewer/src/lib/entry-editor/NewEntryDialog.svelte @@ -1,6 +1,5 @@