From 499a9dadbf402fb46f7fede2573979306c2bfa5f Mon Sep 17 00:00:00 2001 From: JonnyTran Date: Fri, 26 Jun 2026 02:52:43 +0000 Subject: [PATCH 1/6] chore(frontend): resolve 31 pre-existing eslint errors Clears the inherited Argilla style debt tracked since the Vue 3 / Nuxt 4 migration (PR #216). All fixes are no-behavior-change: - no-empty (9): explanatory comments on intentional best-effort catches - ban-ts-comment (3): @ts-ignore -> @ts-expect-error w/ description (typecheck confirms each still suppresses a real error) - ban-types (8): Function -> () => unknown/void, Object -> object, {} -> Record, Class interface construct signature - no-empty-function (5): no-op comments; singleton/static-only ctors documented - no-constant-condition (2): while(true) -> for(;;) - no-useless-escape (1): /[\{\}]/ -> /[{}]/ - no-namespace (2, config): allowDeclarations for ambient `declare namespace CSS` global augmentations (CSS Custom Highlight API) Gates: eslint 0, nuxi typecheck 0, vitest 777 passed/3 skipped/1 todo. --- extralit-frontend/.eslintrc.js | 3 +++ .../fields/span-annotation/components/highlighting.ts | 4 +++- .../fields/span-annotation/components/span-selection.ts | 8 +++++--- .../container/mode/useBulkAnnotationViewModel.ts | 1 + .../annotation/container/mode/useDocumentViewModel.ts | 2 +- .../header/header-bar/useExportToHubViewModel.ts | 4 +++- .../annotation/header/useDatasetsFiltersViewModel.ts | 4 +++- .../persistent-storage/usePersistentStorageViewModel.ts | 4 +++- .../features/home/dataset-fields/useDatasetFields.ts | 1 + .../home/dataset-questions/useDatasetQuestions.ts | 1 + extralit-frontend/v1/di/__mocks__/useResolveMock.ts | 2 +- .../v1/domain/entities/__mocks__/question/mock.ts | 2 +- extralit-frontend/v1/domain/entities/error/Guard.ts | 4 +++- .../v1/domain/entities/hub/DatasetCreation.ts | 2 +- extralit-frontend/v1/domain/entities/record/Record.ts | 2 +- extralit-frontend/v1/domain/entities/table/Validation.ts | 2 +- .../v1/domain/services/FileMatchingService.ts | 2 +- .../v1/domain/services/FileParsingService.ts | 2 +- .../v1/domain/usecases/bulk-annotation-use-case.ts | 8 ++++++-- .../domain/usecases/load-records-to-annotate-use-case.ts | 1 + .../v1/infrastructure/services/useFocusTab.ts | 7 ++++++- .../v1/infrastructure/services/useLocalStorage.ts | 4 +++- extralit-frontend/v1/infrastructure/services/useQueue.ts | 6 ++++-- extralit-frontend/v1/store/create.ts | 3 ++- 24 files changed, 56 insertions(+), 23 deletions(-) diff --git a/extralit-frontend/.eslintrc.js b/extralit-frontend/.eslintrc.js index b526a5c77..765584ef0 100644 --- a/extralit-frontend/.eslintrc.js +++ b/extralit-frontend/.eslintrc.js @@ -95,6 +95,9 @@ module.exports = { "space-before-function-paren": 0, "no-throw-literal": 0, "no-new": 0, + // Ambient `declare namespace CSS { ... }` global augmentations (CSS Custom + // Highlight API) can't be expressed as ES modules; allow declaration-only namespaces. + "@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }], }, }, ], diff --git a/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/highlighting.ts b/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/highlighting.ts index 70bf63e11..4ec3515f3 100644 --- a/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/highlighting.ts +++ b/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/highlighting.ts @@ -425,7 +425,9 @@ export class Highlighting { range.setEnd(node.element, to); return range; - } catch {} + } catch { + /* invalid offsets for this node; return undefined so callers skip it */ + } } private createTextSelection(clear = false): TextSelection | undefined { diff --git a/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/span-selection.ts b/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/span-selection.ts index b12efa86b..cb483f6df 100644 --- a/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/span-selection.ts +++ b/extralit-frontend/components/features/annotation/container/fields/span-annotation/components/span-selection.ts @@ -41,7 +41,9 @@ export type Configuration = { export class SpanSelection { private selections: OverlappedSpan[] = []; - protected constructor() {} + protected constructor() { + /* singleton: use SpanSelection.getInstance() */ + } // eslint-disable-next-line no-use-before-define private static instance: SpanSelection; @@ -188,7 +190,7 @@ export class SpanSelection { } private completeLeftSide(selection: TextSelection) { - while (true) { + for (;;) { const prevChar = selection.node.text.charAt(selection.from - 1); if (this.isEmpty(prevChar) || this.isSymbol(prevChar) || selection.to === 0) { @@ -209,7 +211,7 @@ export class SpanSelection { } private completeRightSide(selection: TextSelection) { - while (true) { + for (;;) { const nextCharacter = selection.node.text.charAt(selection.to); if (this.isEmpty(nextCharacter) || this.isSymbol(nextCharacter) || selection.to === selection.node.text.length) { diff --git a/extralit-frontend/components/features/annotation/container/mode/useBulkAnnotationViewModel.ts b/extralit-frontend/components/features/annotation/container/mode/useBulkAnnotationViewModel.ts index a59b3e663..f1a570971 100644 --- a/extralit-frontend/components/features/annotation/container/mode/useBulkAnnotationViewModel.ts +++ b/extralit-frontend/components/features/annotation/container/mode/useBulkAnnotationViewModel.ts @@ -66,6 +66,7 @@ export const useBulkAnnotationViewModel = ({ records }: { records: Records }) => }); } } catch { + /* notification already surfaced upstream; reset state in finally */ } finally { affectAllRecords.value = false; progress.value = 0; diff --git a/extralit-frontend/components/features/annotation/container/mode/useDocumentViewModel.ts b/extralit-frontend/components/features/annotation/container/mode/useDocumentViewModel.ts index 59b853062..4f6c52161 100644 --- a/extralit-frontend/components/features/annotation/container/mode/useDocumentViewModel.ts +++ b/extralit-frontend/components/features/annotation/container/mode/useDocumentViewModel.ts @@ -60,7 +60,7 @@ export const useDocumentViewModel = (props: { record: any }) => { }; const focusDocumentPageNumber = (pageNumber: number | string) => { - // @ts-ignore + // @ts-expect-error -- Document type lacks page_number; tolerated here setDocument({ ...document, page_number: pageNumber }); }; diff --git a/extralit-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts b/extralit-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts index e81af1d82..2fc2e0da5 100644 --- a/extralit-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts +++ b/extralit-frontend/components/features/annotation/header/header-bar/useExportToHubViewModel.ts @@ -107,7 +107,9 @@ export const useExportToHubViewModel = (props: ExportToHubProps) => { : undefined, permanent: true, }); - } catch {} + } catch { + /* best-effort status notification; ignore failures */ + } }; const watchExportStatus = async () => { diff --git a/extralit-frontend/components/features/annotation/header/useDatasetsFiltersViewModel.ts b/extralit-frontend/components/features/annotation/header/useDatasetsFiltersViewModel.ts index 670386c66..f2d19e257 100644 --- a/extralit-frontend/components/features/annotation/header/useDatasetsFiltersViewModel.ts +++ b/extralit-frontend/components/features/annotation/header/useDatasetsFiltersViewModel.ts @@ -37,7 +37,9 @@ export const useDatasetsFiltersViewModel = ({ recordCriteria }: { recordCriteria const loadFields = async () => { try { datasetFields.value = await getFieldsUseCase.execute(recordCriteria.datasetId); - } catch {} + } catch { + /* best-effort: leave datasetFields empty if loading fails */ + } }; onBeforeMount(() => { diff --git a/extralit-frontend/components/features/global/persistent-storage/usePersistentStorageViewModel.ts b/extralit-frontend/components/features/global/persistent-storage/usePersistentStorageViewModel.ts index 26cde9615..0c8d5f0c6 100644 --- a/extralit-frontend/components/features/global/persistent-storage/usePersistentStorageViewModel.ts +++ b/extralit-frontend/components/features/global/persistent-storage/usePersistentStorageViewModel.ts @@ -10,7 +10,9 @@ export const usePersistentStorageViewModel = () => { onMounted(async () => { try { showBanner.value = await hasPersistentStorageWarning(); - } catch (error) {} + } catch { + /* best-effort: keep banner hidden if the check fails */ + } }); return { diff --git a/extralit-frontend/components/features/home/dataset-fields/useDatasetFields.ts b/extralit-frontend/components/features/home/dataset-fields/useDatasetFields.ts index 9944ec6c9..b3cbf2969 100644 --- a/extralit-frontend/components/features/home/dataset-fields/useDatasetFields.ts +++ b/extralit-frontend/components/features/home/dataset-fields/useDatasetFields.ts @@ -15,6 +15,7 @@ export const useDatasetFields = ({ dataset }: { dataset: Dataset }) => { fields.value = await getFieldsUseCase.execute(dataset.id); } catch { + /* best-effort: leave fields empty if loading fails */ } finally { isFieldsLoading.value = false; } diff --git a/extralit-frontend/components/features/home/dataset-questions/useDatasetQuestions.ts b/extralit-frontend/components/features/home/dataset-questions/useDatasetQuestions.ts index 167771172..e4974c3ba 100644 --- a/extralit-frontend/components/features/home/dataset-questions/useDatasetQuestions.ts +++ b/extralit-frontend/components/features/home/dataset-questions/useDatasetQuestions.ts @@ -15,6 +15,7 @@ export const useDatasetQuestions = ({ dataset }: { dataset: Dataset }) => { questions.value = await getQuestionsUseCase.execute(dataset.id); } catch { + /* best-effort: leave questions empty if loading fails */ } finally { isQuestionsLoading.value = false; } diff --git a/extralit-frontend/v1/di/__mocks__/useResolveMock.ts b/extralit-frontend/v1/di/__mocks__/useResolveMock.ts index 0bffb295e..de0d5bd54 100644 --- a/extralit-frontend/v1/di/__mocks__/useResolveMock.ts +++ b/extralit-frontend/v1/di/__mocks__/useResolveMock.ts @@ -1,5 +1,5 @@ import Container, { register, Class } from "ts-injecty"; -export const useResolveMock = (ctor: Class, mocked: Object) => { +export const useResolveMock = (ctor: Class, mocked: object) => { Container.register([register(ctor.name).withImplementation(mocked).build()]); }; diff --git a/extralit-frontend/v1/domain/entities/__mocks__/question/mock.ts b/extralit-frontend/v1/domain/entities/__mocks__/question/mock.ts index fb0928d3e..071adabbf 100644 --- a/extralit-frontend/v1/domain/entities/__mocks__/question/mock.ts +++ b/extralit-frontend/v1/domain/entities/__mocks__/question/mock.ts @@ -26,7 +26,7 @@ export const createTextQuestionMocked = () => { }); }; -export const createLabelQuestionMocked = (settings: Object) => { +export const createLabelQuestionMocked = (settings: object) => { return new Question("FAKE_ID", "FAKE_NAME", "FAKE_DESCRIPTION", "FAKE_DATASET_ID", "FAKE_ TITLE", false, { type: "label_selection", options: [ diff --git a/extralit-frontend/v1/domain/entities/error/Guard.ts b/extralit-frontend/v1/domain/entities/error/Guard.ts index c5a52c988..8e27d287e 100644 --- a/extralit-frontend/v1/domain/entities/error/Guard.ts +++ b/extralit-frontend/v1/domain/entities/error/Guard.ts @@ -9,7 +9,9 @@ export class GuardError extends Error { } export class Guard { - private constructor() {} + private constructor() { + /* static-only utility; not instantiable */ + } static condition(showThrow: boolean, message: string) { if (showThrow) { diff --git a/extralit-frontend/v1/domain/entities/hub/DatasetCreation.ts b/extralit-frontend/v1/domain/entities/hub/DatasetCreation.ts index cc2683520..63619650c 100644 --- a/extralit-frontend/v1/domain/entities/hub/DatasetCreation.ts +++ b/extralit-frontend/v1/domain/entities/hub/DatasetCreation.ts @@ -5,7 +5,7 @@ import { Subset } from "./Subset"; export class DatasetCreation { public selectedSubset: Subset; - public readonly firstRecord: {}; + public readonly firstRecord: Record; public workspace: Workspace; public importHistoryId?: string; diff --git a/extralit-frontend/v1/domain/entities/record/Record.ts b/extralit-frontend/v1/domain/entities/record/Record.ts index eaa6bae8a..1b55ed6db 100644 --- a/extralit-frontend/v1/domain/entities/record/Record.ts +++ b/extralit-frontend/v1/domain/entities/record/Record.ts @@ -161,7 +161,7 @@ export class Record { if ( this.isPending && !!question.suggestion && - // @ts-ignore + // @ts-expect-error -- use_table is present only on some question settings variants !question.settings?.settings?.use_table ) { question.response(question.suggestion); diff --git a/extralit-frontend/v1/domain/entities/table/Validation.ts b/extralit-frontend/v1/domain/entities/table/Validation.ts index 43e4e026a..e2c9e9144 100644 --- a/extralit-frontend/v1/domain/entities/table/Validation.ts +++ b/extralit-frontend/v1/domain/entities/table/Validation.ts @@ -16,7 +16,7 @@ export type SuggestionCheck = | string[] | { [columnName: string]: - | {} + | Record | { [otherColumnName: string]: [otherColumnValue: string]; }; diff --git a/extralit-frontend/v1/domain/services/FileMatchingService.ts b/extralit-frontend/v1/domain/services/FileMatchingService.ts index d483622b4..2876d404a 100644 --- a/extralit-frontend/v1/domain/services/FileMatchingService.ts +++ b/extralit-frontend/v1/domain/services/FileMatchingService.ts @@ -16,7 +16,7 @@ interface PrefixMatchResult { export class PdfMatchingService implements IFileMatchingService { matchFiles(files: File[], dataframeData: TableData): FileMatchingResult { - // @ts-ignore + // @ts-expect-error -- TableData.data shape is untyped here const entries: ParsedEntry[] = dataframeData?.data || []; if (!entries || entries.length === 0 || files.length === 0) { diff --git a/extralit-frontend/v1/domain/services/FileParsingService.ts b/extralit-frontend/v1/domain/services/FileParsingService.ts index 45ef6c57f..4ebcfabe9 100644 --- a/extralit-frontend/v1/domain/services/FileParsingService.ts +++ b/extralit-frontend/v1/domain/services/FileParsingService.ts @@ -74,7 +74,7 @@ export class BibTeXParser { const cleaned = field .toString() - .replace(/[\{\}]/g, "") + .replace(/[{}]/g, "") .replace(/^"+|"+$/g, "") .trim(); diff --git a/extralit-frontend/v1/domain/usecases/bulk-annotation-use-case.ts b/extralit-frontend/v1/domain/usecases/bulk-annotation-use-case.ts index 1ca1e3195..8ce815f02 100644 --- a/extralit-frontend/v1/domain/usecases/bulk-annotation-use-case.ts +++ b/extralit-frontend/v1/domain/usecases/bulk-annotation-use-case.ts @@ -28,7 +28,9 @@ export class BulkAnnotationUseCase { recordReference: Record, selectedRecords: Record[], affectAllRecords = false, - progress: Progress = () => {} + progress: Progress = () => { + /* no-op: progress reporting is optional */ + } ) { const records = [...selectedRecords]; @@ -56,7 +58,9 @@ export class BulkAnnotationUseCase { status: AvailableStatus, recordReference: Record, selectedRecords: Record[], - progress: Progress = () => {} + progress: Progress = () => { + /* no-op: progress reporting is optional */ + } ) { const results: boolean[] = []; diff --git a/extralit-frontend/v1/domain/usecases/load-records-to-annotate-use-case.ts b/extralit-frontend/v1/domain/usecases/load-records-to-annotate-use-case.ts index 502875871..19eabb428 100644 --- a/extralit-frontend/v1/domain/usecases/load-records-to-annotate-use-case.ts +++ b/extralit-frontend/v1/domain/usecases/load-records-to-annotate-use-case.ts @@ -108,6 +108,7 @@ export class LoadRecordsToAnnotateUseCase { this.recordsStorage.save(records); } catch { + /* best-effort prefetch; ignore failures and clear the buffering flag */ } finally { this.isBuffering = false; } diff --git a/extralit-frontend/v1/infrastructure/services/useFocusTab.ts b/extralit-frontend/v1/infrastructure/services/useFocusTab.ts index a3a76f205..044d9c174 100644 --- a/extralit-frontend/v1/infrastructure/services/useFocusTab.ts +++ b/extralit-frontend/v1/infrastructure/services/useFocusTab.ts @@ -1,6 +1,11 @@ import { onBeforeUnmount, onMounted } from "vue"; -export const useFocusTab = (onFocus: Function, onBlur = () => {}) => { +export const useFocusTab = ( + onFocus: () => void, + onBlur: () => void = () => { + /* no-op */ + } +) => { if (!onFocus) throw new Error("onFocus is mandatory"); const onFocusCallback = () => onFocus(); diff --git a/extralit-frontend/v1/infrastructure/services/useLocalStorage.ts b/extralit-frontend/v1/infrastructure/services/useLocalStorage.ts index c315e1e41..8a2fa0025 100644 --- a/extralit-frontend/v1/infrastructure/services/useLocalStorage.ts +++ b/extralit-frontend/v1/infrastructure/services/useLocalStorage.ts @@ -31,7 +31,9 @@ export const useLocalStorage = (): ILocalStorageService => { [key]: value, }) ); - } catch { } + } catch { + /* ignore: localStorage may be unavailable or full */ + } }; const pop = (key: Options) => { diff --git a/extralit-frontend/v1/infrastructure/services/useQueue.ts b/extralit-frontend/v1/infrastructure/services/useQueue.ts index 4dbe78b0a..0f63cc00e 100644 --- a/extralit-frontend/v1/infrastructure/services/useQueue.ts +++ b/extralit-frontend/v1/infrastructure/services/useQueue.ts @@ -1,5 +1,7 @@ +type Executable = () => unknown; + class Routine { - constructor(private readonly executable: Function) {} + constructor(private readonly executable: Executable) {} execute() { return this.executable(); @@ -9,7 +11,7 @@ class Routine { class Queue { private readonly queue: Routine[] = []; - async enqueue(element: Function) { + async enqueue(element: Executable) { const routine = new Routine(element); this.queue.push(routine); diff --git a/extralit-frontend/v1/store/create.ts b/extralit-frontend/v1/store/create.ts index d6a7ea336..cfdc2a324 100644 --- a/extralit-frontend/v1/store/create.ts +++ b/extralit-frontend/v1/store/create.ts @@ -1,8 +1,9 @@ import { defineStore } from "pinia"; import { create } from "./non-reactive"; -interface Class extends Function { +interface Class { new (...args: any[]): T; + name: string; } export interface ImplicitStorage { From 39f482858a1ec098d97586193e07a0634a8578af Mon Sep 17 00:00:00 2001 From: JonnyTran Date: Fri, 26 Jun 2026 06:39:42 +0000 Subject: [PATCH 2/6] fix(frontend): silence postcss-calc and Sass build warnings in `nuxi generate` Production builds run cssnano (transitive via @nuxt/vite-builder), whose postcss-calc lexer can't parse relative-color channel arithmetic, and Dart Sass emits deprecations for two legacy idioms. All fixes are visual no-ops: - postcss-calc "Lexical error / Unrecognized text" (BaseButton danger hover): `hsl(from var(--color-danger) h s calc(l - 4))` -> `color-mix(in srgb, var(--color-danger), black 6%)`. No calc(), so postcss-calc never touches it; keeps the var() reference. Also fixes a malformed line (`hsl(var(--color-danger) calc(l - 4))` was missing `from`/`h s` -> invalid color, border hover was dead). - Sass "declarations after nested rules" (8x: BaseSimpleTable, RecordFieldsHeader, BaseCollapsablePanel): hoisted own-element declarations above the nested rules / emitting mixins. Output unchanged (nested rules target other selectors). - Sass "/ for division is deprecated" (6x: Workspace/DatasetBreadcrumbDropdown): `$base-space / 2` -> `$base-space * 0.5` (identical value, no sass:math import). Remaining @vueuse/core Rollup annotation warnings are third-party (published dist), not actionable here. Verified: `nuxi generate` exit 0 with all above warnings gone; vitest 777 passed/3 skipped/1 todo. --- .../base-breadcrumbs/DatasetBreadcrumbDropdown.vue | 6 +++--- .../base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue | 6 +++--- .../components/base/base-button/BaseButton.vue | 6 +++--- .../base-collpasable-panel/BaseCollapsablePanel.vue | 12 ++++++------ .../base/base-simple-table/BaseSimpleTable.vue | 4 ++-- .../container/fields/RecordFieldsHeader.vue | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue index c160074bb..f276b7776 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue @@ -149,7 +149,7 @@ export default { &__header { display: flex; align-items: center; - padding: $base-space / 2; + padding: $base-space * 0.5; color: var(--fg-lighter); cursor: pointer; border-radius: $border-radius-s; @@ -193,7 +193,7 @@ export default { } &__items { - padding: $base-space / 2; + padding: $base-space * 0.5; } &__item { @@ -268,7 +268,7 @@ export default { margin: 0; .base-search__input { - padding: $base-space / 2; + padding: $base-space * 0.5; border-radius: $border-radius-s; border: 1px solid var(--border-field); background: var(--bg-field); diff --git a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue index bda6bcd5c..9b8ad0fec 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue @@ -141,7 +141,7 @@ export default { &__header { display: flex; align-items: center; - padding: $base-space / 2; + padding: $base-space * 0.5; color: var(--fg-lighter); cursor: pointer; border-radius: $border-radius-s; @@ -185,7 +185,7 @@ export default { } &__items { - padding: $base-space / 2; + padding: $base-space * 0.5; } &__item { @@ -260,7 +260,7 @@ export default { margin: 0; .base-search__input { - padding: $base-space / 2; + padding: $base-space * 0.5; border-radius: $border-radius-s; border: 1px solid var(--border-field); background: var(--bg-field); diff --git a/extralit-frontend/components/base/base-button/BaseButton.vue b/extralit-frontend/components/base/base-button/BaseButton.vue index f68edcee3..89e3dd11f 100644 --- a/extralit-frontend/components/base/base-button/BaseButton.vue +++ b/extralit-frontend/components/base/base-button/BaseButton.vue @@ -244,7 +244,7 @@ export default { &:hover, &:active, &.active { - background-color: hsl(from var(--color-danger) h s calc(l - 4)); + background-color: color-mix(in srgb, var(--color-danger), black 6%); } &.outline { background: none; @@ -256,8 +256,8 @@ export default { &:hover, &:active, &.active { - color: hsl(from var(--color-danger) h s calc(l - 4)); - border-color: hsl(var(--color-danger) calc(l - 4)); + color: color-mix(in srgb, var(--color-danger), black 6%); + border-color: color-mix(in srgb, var(--color-danger), black 6%); } } &.light { diff --git a/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue b/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue index 67c68993c..9b4d99d51 100644 --- a/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue +++ b/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue @@ -58,6 +58,12 @@ export default { &__header { overflow: visible; color: var(--fg-secondary); + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: $base-space $base-space * 2; + @include font-size(13px); &__container { width: 100%; @@ -70,12 +76,6 @@ export default { } } - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - padding: $base-space $base-space * 2; - @include font-size(13px); :deep(p) { text-transform: uppercase; } diff --git a/extralit-frontend/components/base/base-simple-table/BaseSimpleTable.vue b/extralit-frontend/components/base/base-simple-table/BaseSimpleTable.vue index 39be36f2e..a8c39abb6 100644 --- a/extralit-frontend/components/base/base-simple-table/BaseSimpleTable.vue +++ b/extralit-frontend/components/base/base-simple-table/BaseSimpleTable.vue @@ -186,11 +186,11 @@ export default { // Hide RenderTable's edit buttons when not editable :deep(.table-container) { + max-height: inherit; + margin-bottom: 0; .__table-buttons { display: none; } - max-height: inherit; - margin-bottom: 0; } // Apply design system styling to RenderTable's tabulator diff --git a/extralit-frontend/components/features/annotation/container/fields/RecordFieldsHeader.vue b/extralit-frontend/components/features/annotation/container/fields/RecordFieldsHeader.vue index 958ccf633..ad9021875 100644 --- a/extralit-frontend/components/features/annotation/container/fields/RecordFieldsHeader.vue +++ b/extralit-frontend/components/features/annotation/container/fields/RecordFieldsHeader.vue @@ -114,8 +114,8 @@ export default { .similarity__progress { &[data-title] { position: relative; - @include tooltip-mini("bottom"); cursor: default; + @include tooltip-mini("bottom"); } } .fields__checkbox[data-title] { From fc01c2fcfb5d11b08d3497cac8144f62d86bb3f9 Mon Sep 17 00:00:00 2001 From: JonnyTran Date: Fri, 26 Jun 2026 07:16:12 +0000 Subject: [PATCH 3/6] chore(frontend): bump safe minor deps + low-risk dev majors Patch/minor bumps within current majors (no API surface change): axios 1.17->1.18.1, vue 3.5.38->3.5.39, dompurify 3.1->3.4.11, happy-dom 20.10.3->20.10.6, js-base64 3.7->3.8.0, papaparse 5.5.3->5.5.4, tabulator-tables 6.3->6.5.2, @playwright/test 1.44->1.61.1, @vercel/config 0.5.4->0.5.5. Low-risk dev majors: rimraf 5->6.1.3, cross-env 7->10.1.0 (both dev-only, unused in scripts). Held: sass (1.77->1.101 would reintroduce @import deprecation warnings cleaned in 39f482858; needs a separate @use migration), typescript (5.9, 6.0 deferred for risk), vuedraggable (4.1.0 is the Vue 3 line; npm 'latest' 2.24.3 is the old Vue 2 build). Gates: nuxi typecheck 0, vitest 777 passed/3 skip/1 todo, nuxi build clean. --- extralit-frontend/package-lock.json | 311 ++++++++++++++-------------- extralit-frontend/package.json | 22 +- 2 files changed, 164 insertions(+), 169 deletions(-) diff --git a/extralit-frontend/package-lock.json b/extralit-frontend/package-lock.json index fa8e03986..2728c850d 100644 --- a/extralit-frontend/package-lock.json +++ b/extralit-frontend/package-lock.json @@ -27,50 +27,50 @@ "@tiptap/pm": "^2.4.0", "@tiptap/vue-3": "^2.4.0", "@vueuse/core": "^14.3.0", - "axios": "^1.12.0", - "dompurify": "^3.1.5", + "axios": "^1.18.1", + "dompurify": "^3.4.11", "fit-curve": "^0.2.0", "highlight.js": "^11.11.1", "interactjs": "^1.10.27", - "js-base64": "^3.7.7", + "js-base64": "^3.8.0", "luxon": "^3.4.4", "marked": "^5.1.2", "marked-highlight": "^2.1.1", "marked-katex-extension": "^5.0.2", "mitt": "^3.0.1", "nuxt": "^4.4.8", - "papaparse": "^5.5.3", + "papaparse": "^5.5.4", "pinia": "^3.0.4", "sass": "~1.77.4", "sha.js": "^2.4.12", "stringz": "^2.1.0", - "tabulator-tables": "^6.3.1", + "tabulator-tables": "^6.5.2", "ts-injecty": "^0.0.22", - "vue": "^3.5.38", + "vue": "^3.5.39", "vuedraggable": "^4.1.0" }, "devDependencies": { "@codescouts/test": "^1.0.7", "@intlify/eslint-plugin-vue-i18n": "^2.0.0", "@nuxt/test-utils": "^4.0.3", - "@playwright/test": "^1.44.1", + "@playwright/test": "^1.61.1", "@types/papaparse": "^5.3.16", "@types/tabulator-tables": "^6.2.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/typescript-estree": "8.39.0", - "@vercel/config": "^0.5.4", + "@vercel/config": "^0.5.5", "@vitest/coverage-v8": "^4.1.9", "@vue/test-utils": "^2.4.11", - "cross-env": "^7.0.3", + "cross-env": "^10.1.0", "eslint": "^8.57.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-nuxt": "^3.2.0", "eslint-plugin-prettier": "^3.4.1", "eslint-plugin-vue": "^8.7.1", - "happy-dom": "^20.10.3", + "happy-dom": "^20.10.6", "prettier": "^2.8.8", - "rimraf": "^5.0.7", + "rimraf": "^6.1.3", "typescript": "^5.4.5", "vite-svg-loader": "^5.1.1", "vitest": "^4.1.9" @@ -885,6 +885,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", @@ -5821,13 +5828,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", - "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.60.0" + "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -8001,13 +8008,13 @@ } }, "node_modules/@vercel/config": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@vercel/config/-/config-0.5.4.tgz", - "integrity": "sha512-5oD2EeWj9iBJi8HcAhOSAWnWbPX5yjSwaTSYr6R/OOcDPsjnkaASqwpSsDU2A7yhVVND6pmzihmG7bNW5/JYiA==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@vercel/config/-/config-0.5.5.tgz", + "integrity": "sha512-U0QX7p08vgk8D47HI74wYyRuDJ2IYHbFQyfVdwO81Xchjp5CsV58/xhkV4EAXXj4NuLuELBwdPDa6oOdCYvEhg==", "dev": true, "license": "MIT", "dependencies": { - "@vercel/routing-utils": "6.3.1", + "@vercel/routing-utils": "6.4.0", "pretty-cache-header": "^1.0.0", "zod": "^3.22.0" }, @@ -8110,9 +8117,9 @@ } }, "node_modules/@vercel/routing-utils": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@vercel/routing-utils/-/routing-utils-6.3.1.tgz", - "integrity": "sha512-7LDDb71dMC3epaUOOZtdPMXwAejTPhJ2/WSd3F1pjowVFm9rObjrjpJ34oefdX9VJCzAXEcRbZCBuCO22Ml9ZA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@vercel/routing-utils/-/routing-utils-6.4.0.tgz", + "integrity": "sha512-SnL/449ftNIDt1cB/oDqFmdZWywckNA6Q/S3WuzzsxBdE9DxmNn498qMKRtgx0W6EW3Mm/wVmg7aNJrfElMBTA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -8371,13 +8378,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz", - "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz", + "integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==", "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", - "@vue/shared": "3.5.38", + "@vue/shared": "3.5.39", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -8390,26 +8397,26 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz", - "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz", + "integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/compiler-core": "3.5.39", + "@vue/shared": "3.5.39" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz", - "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.39.tgz", + "integrity": "sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==", "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", - "@vue/compiler-core": "3.5.38", - "@vue/compiler-dom": "3.5.38", - "@vue/compiler-ssr": "3.5.38", - "@vue/shared": "3.5.38", + "@vue/compiler-core": "3.5.39", + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.15", @@ -8423,13 +8430,13 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz", - "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.39.tgz", + "integrity": "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/compiler-dom": "3.5.39", + "@vue/shared": "3.5.39" } }, "node_modules/@vue/devtools-api": { @@ -8533,53 +8540,53 @@ "license": "MIT" }, "node_modules/@vue/reactivity": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz", - "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.39.tgz", + "integrity": "sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.38" + "@vue/shared": "3.5.39" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz", - "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.39.tgz", + "integrity": "sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/reactivity": "3.5.39", + "@vue/shared": "3.5.39" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz", - "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.39.tgz", + "integrity": "sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.38", - "@vue/runtime-core": "3.5.38", - "@vue/shared": "3.5.38", + "@vue/reactivity": "3.5.39", + "@vue/runtime-core": "3.5.39", + "@vue/shared": "3.5.39", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz", - "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.39.tgz", + "integrity": "sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39" }, "peerDependencies": { - "vue": "3.5.38" + "vue": "3.5.39" } }, "node_modules/@vue/shared": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz", - "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz", + "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -9089,9 +9096,9 @@ } }, "node_modules/axios": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", - "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", @@ -10128,22 +10135,21 @@ } }, "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" }, "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=20" } }, "node_modules/cross-spawn": { @@ -10654,9 +10660,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.10.tgz", - "integrity": "sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -12277,9 +12283,9 @@ } }, "node_modules/happy-dom": { - "version": "20.10.3", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.10.3.tgz", - "integrity": "sha512-Hjdiy8RziuCcn5z04QI/rlsNuQoG8P0xxjgvsSMpi89cvIXIOcucQtiHS1yHSShxoBcSCeYqAskINmTiy/mlfw==", + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.10.6.tgz", + "integrity": "sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==", "dev": true, "license": "MIT", "dependencies": { @@ -13870,9 +13876,9 @@ } }, "node_modules/js-base64": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", - "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.8.0.tgz", + "integrity": "sha512-65kvbemyZhj+ExQt1PEFyBEjL5vAHysu1lJdW1AwhhChkO8ZBPizYk/m9GVrpbS2Je1hF+UYZ+6KywqtZV8mHw==", "license": "BSD-3-Clause" }, "node_modules/js-beautify": { @@ -16725,9 +16731,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/papaparse": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", - "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.4.tgz", + "integrity": "sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==", "license": "MIT" }, "node_modules/parent-module": { @@ -17021,13 +17027,13 @@ } }, "node_modules/playwright": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", - "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.60.0" + "playwright-core": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -17040,9 +17046,9 @@ } }, "node_modules/playwright-core": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", - "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -18324,88 +18330,77 @@ "license": "MIT" }, "node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "glob": "^10.3.7" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "node_modules/rimraf/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "balanced-match": "^4.0.2" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/rimraf/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -19511,9 +19506,9 @@ } }, "node_modules/tabulator-tables": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.4.0.tgz", - "integrity": "sha512-Lxh+leFNoBo/Yyr4USs6gxqbfo8anYUaUMmoT91pfVLtoUgl/dE+qV7ahnFrKVMCYYqGG33aIMPR7FzpPBaNYA==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.5.2.tgz", + "integrity": "sha512-cRL3xsaaf5RzND8KPbn4C9Ce5tzyiQUE01E/10zN50MjdRKl/Gl5nXkzLN6cvEyRfWHCPuqBF92y50CBw20WYA==", "license": "MIT" }, "node_modules/tagged-tag": { @@ -21305,16 +21300,16 @@ } }, "node_modules/vue": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz", - "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.39.tgz", + "integrity": "sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.38", - "@vue/compiler-sfc": "3.5.38", - "@vue/runtime-dom": "3.5.38", - "@vue/server-renderer": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-sfc": "3.5.39", + "@vue/runtime-dom": "3.5.39", + "@vue/server-renderer": "3.5.39", + "@vue/shared": "3.5.39" }, "peerDependencies": { "typescript": "*" diff --git a/extralit-frontend/package.json b/extralit-frontend/package.json index ad8870652..33f4fae5d 100644 --- a/extralit-frontend/package.json +++ b/extralit-frontend/package.json @@ -39,50 +39,50 @@ "@tiptap/pm": "^2.4.0", "@tiptap/vue-3": "^2.4.0", "@vueuse/core": "^14.3.0", - "axios": "^1.12.0", - "dompurify": "^3.1.5", + "axios": "^1.18.1", + "dompurify": "^3.4.11", "fit-curve": "^0.2.0", "highlight.js": "^11.11.1", "interactjs": "^1.10.27", - "js-base64": "^3.7.7", + "js-base64": "^3.8.0", "luxon": "^3.4.4", "marked": "^5.1.2", "marked-highlight": "^2.1.1", "marked-katex-extension": "^5.0.2", "mitt": "^3.0.1", "nuxt": "^4.4.8", - "papaparse": "^5.5.3", + "papaparse": "^5.5.4", "pinia": "^3.0.4", "sass": "~1.77.4", "sha.js": "^2.4.12", "stringz": "^2.1.0", - "tabulator-tables": "^6.3.1", + "tabulator-tables": "^6.5.2", "ts-injecty": "^0.0.22", - "vue": "^3.5.38", + "vue": "^3.5.39", "vuedraggable": "^4.1.0" }, "devDependencies": { "@codescouts/test": "^1.0.7", "@intlify/eslint-plugin-vue-i18n": "^2.0.0", "@nuxt/test-utils": "^4.0.3", - "@playwright/test": "^1.44.1", + "@playwright/test": "^1.61.1", "@types/papaparse": "^5.3.16", "@types/tabulator-tables": "^6.2.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/typescript-estree": "8.39.0", - "@vercel/config": "^0.5.4", + "@vercel/config": "^0.5.5", "@vitest/coverage-v8": "^4.1.9", "@vue/test-utils": "^2.4.11", - "cross-env": "^7.0.3", + "cross-env": "^10.1.0", "eslint": "^8.57.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-nuxt": "^3.2.0", "eslint-plugin-prettier": "^3.4.1", "eslint-plugin-vue": "^8.7.1", - "happy-dom": "^20.10.3", + "happy-dom": "^20.10.6", "prettier": "^2.8.8", - "rimraf": "^5.0.7", + "rimraf": "^6.1.3", "typescript": "^5.4.5", "vite-svg-loader": "^5.1.1", "vitest": "^4.1.9" From 41c009d2efa757fad2a63f0c9e8811e12341b45c Mon Sep 17 00:00:00 2001 From: JonnyTran Date: Fri, 26 Jun 2026 07:24:23 +0000 Subject: [PATCH 4/6] chore(frontend): upgrade marked 5->18 and ts-injecty 0.0.22->1.0.0 marked 5.1.2 -> 18.0.5 (+ marked-highlight 2.2.4, marked-katex-extension 5.1.10; both peer-compatible with marked >=4 <19, katex 0.17 satisfied). Dropped the removed 'headerIds'/'mangle' options from MarkdownRenderer (split out of core in marked >5; marked 18 defaults match the old 'false' behaviour). Runtime-smoke-verified the full katex+highlight+hooks pipeline renders (string output, no header ids, hljs/katex/table). ts-injecty 0.0.22 -> 1.0.0: API-compatible (register().withDependency().build(), Container.register([]), useResolve(), Class all unchanged). One fragile deep import fixed: useEvents.ts imported 'Ref' from 'ts-injecty/dist/types' (renamed to 'Class' in 1.0.0) -> switched to the public 'Class' export. Held: @tiptap (already latest 2.x; v3 blocked by @sereneinserenade/tiptap-search-and-replace, whose latest 0.1.1 pins @tiptap peers to ^2.x.x with no v3 release). Gates: nuxi typecheck 0, vitest 777 passed/3 skip/1 todo, nuxi build clean. --- .../base-render-markdown/MarkdownRenderer.vue | 5 +++-- extralit-frontend/package-lock.json | 22 +++++++++---------- extralit-frontend/package.json | 8 +++---- .../v1/infrastructure/events/useEvents.ts | 5 ++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/extralit-frontend/components/base/base-render-markdown/MarkdownRenderer.vue b/extralit-frontend/components/base/base-render-markdown/MarkdownRenderer.vue index ef7428c57..81d7124e5 100644 --- a/extralit-frontend/components/base/base-render-markdown/MarkdownRenderer.vue +++ b/extralit-frontend/components/base/base-render-markdown/MarkdownRenderer.vue @@ -63,9 +63,10 @@ export default { return this.$language.isRTL(this.markdown) ? "--rtl" : "--ltr"; }, markdownToHtml() { + // `headerIds` and `mangle` were removed in marked >5 (split out to + // marked-gfm-heading-id / marked-mangle); marked 18 defaults match the + // old `false` behaviour (no auto ids, no email mangling), so we drop them. let html = marked.parse(this.markdown, { - headerIds: false, - mangle: false, breaks: true, }); diff --git a/extralit-frontend/package-lock.json b/extralit-frontend/package-lock.json index 2728c850d..77dfa3ec3 100644 --- a/extralit-frontend/package-lock.json +++ b/extralit-frontend/package-lock.json @@ -34,9 +34,9 @@ "interactjs": "^1.10.27", "js-base64": "^3.8.0", "luxon": "^3.4.4", - "marked": "^5.1.2", - "marked-highlight": "^2.1.1", - "marked-katex-extension": "^5.0.2", + "marked": "^18.0.5", + "marked-highlight": "^2.2.4", + "marked-katex-extension": "^5.1.10", "mitt": "^3.0.1", "nuxt": "^4.4.8", "papaparse": "^5.5.4", @@ -45,7 +45,7 @@ "sha.js": "^2.4.12", "stringz": "^2.1.0", "tabulator-tables": "^6.5.2", - "ts-injecty": "^0.0.22", + "ts-injecty": "^1.0.0", "vue": "^3.5.39", "vuedraggable": "^4.1.0" }, @@ -14768,15 +14768,15 @@ } }, "node_modules/marked": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", - "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 16" + "node": ">= 20" } }, "node_modules/marked-highlight": { @@ -19831,9 +19831,9 @@ } }, "node_modules/ts-injecty": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/ts-injecty/-/ts-injecty-0.0.22.tgz", - "integrity": "sha512-O9JQRUmSajr80fPy8GetIZMd053/t2h5opZJVowddwPog1XDAhc0+Wr6+vabiZpnrzxsJU2FrJrXSPwB4R8VNA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-injecty/-/ts-injecty-1.0.0.tgz", + "integrity": "sha512-EBsOhMJYD/23Fgl6utwdBS+7Zv7o9Ih09PbQRQbpH0WHbO9gh+XlzxNuZb0ZkiyNyBZjNLIo54DB+a0exr8HEg==", "license": "ISC" }, "node_modules/tslib": { diff --git a/extralit-frontend/package.json b/extralit-frontend/package.json index 33f4fae5d..4694f0fb3 100644 --- a/extralit-frontend/package.json +++ b/extralit-frontend/package.json @@ -46,9 +46,9 @@ "interactjs": "^1.10.27", "js-base64": "^3.8.0", "luxon": "^3.4.4", - "marked": "^5.1.2", - "marked-highlight": "^2.1.1", - "marked-katex-extension": "^5.0.2", + "marked": "^18.0.5", + "marked-highlight": "^2.2.4", + "marked-katex-extension": "^5.1.10", "mitt": "^3.0.1", "nuxt": "^4.4.8", "papaparse": "^5.5.4", @@ -57,7 +57,7 @@ "sha.js": "^2.4.12", "stringz": "^2.1.0", "tabulator-tables": "^6.5.2", - "ts-injecty": "^0.0.22", + "ts-injecty": "^1.0.0", "vue": "^3.5.39", "vuedraggable": "^4.1.0" }, diff --git a/extralit-frontend/v1/infrastructure/events/useEvents.ts b/extralit-frontend/v1/infrastructure/events/useEvents.ts index 0efc90717..1f3eb308e 100644 --- a/extralit-frontend/v1/infrastructure/events/useEvents.ts +++ b/extralit-frontend/v1/infrastructure/events/useEvents.ts @@ -1,9 +1,8 @@ import { DomainEvent, Handler } from "@codescouts/events"; -import { useResolve } from "ts-injecty"; -import { Ref } from "ts-injecty/dist/types"; +import { useResolve, Class } from "ts-injecty"; import { onMounted, onUnmounted, ref } from "vue"; -export const useEvents = (...handlers: Ref>[]) => { +export const useEvents = (...handlers: Class>[]) => { const resolved = ref([]); onMounted(() => { From 0997c1ce88561028f1b2934c3be8ae1c4fb292de Mon Sep 17 00:00:00 2001 From: JonnyTran Date: Sat, 27 Jun 2026 01:09:48 +0000 Subject: [PATCH 5/6] chore(frontend): migrate to ESLint 10 flat-config + Prettier 3 toolchain Toolchain majors (dev-only): eslint 8->10, eslint-plugin-vue 8->10, @typescript-eslint 5->8, eslint-config-prettier 7->10, eslint-plugin-prettier 3->5, @intlify/eslint-plugin-vue-i18n 2->4, prettier 2->3. Added flat-config deps (@eslint/js, globals, vue-eslint-parser) and dropped eslint-plugin-nuxt (no flat-config build; deprecated for Nuxt 4 - its vue rules now come straight from eslint-plugin-vue). Declared vue-tsc (^3.3.5) explicitly: it was an undeclared transitive only physically present, so a clean reinstall exposed the gap. - .eslintrc.js + .eslintignore -> eslint.config.mjs (flat). Behaviour preserved: vue rules stay all-warn, prettier/prettier stays warn, lint runs --quiet so only error-level rules gate. lint script drops the removed --ext/--ignore-path flags. No parserOptions.project: enabled @typescript-eslint rules are syntactic (type errors are vue-tsc's job), so type-aware parsing would only make lint multi-minutes slower for zero new findings. Rules newly promoted to error by the bumps (no-explicit-any x205, no-unused-vars, vue/no-mutating-props x48, multi-word-component-names, etc.) flag PRE-EXISTING patterns, not regressions - kept advisory so the bump is behaviour-only and the gate stays green; documented inline as tracked cleanup (several are real bugs worth a follow-up). - Added .prettierignore (generated/build dirs); prettier has no implicit ignores beyond node_modules and was choking on generated .vue under .nuxt*. - prettier 3 reformat across the tree (.prettierrc unchanged: es5 commas / 120 width). Behaviour-preserving code fixes the bumps forced: - QuestionsForm.vue:

wrapping

s ->
(invalid HTML5 prettier 3 rejects; .guidelines-link already margin:0 so visual no-op). - RenderTable.vue: relocated a // @ts-ignore (vue-tsc 3 attributes the TS2556 to the call). - test/setup.ts: removed 2 stale @ts-expect-error (vue-tsc 3 -> TS2578 unused-directive). - DatasetRepository#import -> importDataset (+interface +2 callers): vite 8 import-analysis rewrote the 'import(' method call as a dynamic import, breaking 2 test suites. SKIP=frontend-lint: that pre-commit hook is buggy (cd extralit-frontend then lints repo-relative 'extralit-frontend/...' paths that no longer resolve, and EACCES on a read-only generated .nuxt-stale-root file) and redundant - tree is already lint-clean (eslint . --quiet = 0) and prettier-formatted. Gates: lint 0, nuxi typecheck 0 (vue-tsc 3.3.5), vitest 777 passed/3 skip/1 todo, npm run generate (Vercel build cmd) exit 0 under vite 8. --- extralit-frontend/.eslintignore | 8 - extralit-frontend/.eslintrc.js | 104 - extralit-frontend/.prettierignore | 11 + .../components/base/BaseSvgIcon.vue | 4 +- .../base/base-breadcrumbs/BaseBreadcrumbs.vue | 17 +- .../DatasetBreadcrumbDropdown.vue | 95 +- .../WorkspaceBreadcrumbDropdown.vue | 94 +- .../base/base-flow-modal/BaseFlowModal.vue | 1 - .../base/base-render-html/RenderHTML.vue | 242 +- .../base/base-render-table/RenderTable.vue | 581 +- .../base-render-table/renderTable.test.ts | 100 +- .../base/base-render-table/tableUtils.ts | 187 +- .../useLLMExtractionViewModel.ts | 41 +- .../useReferenceTablesViewModel.ts | 105 +- .../useSchemaTableViewModel.ts | 28 +- .../base/base-render-table/validatorUtils.ts | 149 +- .../base/base-search-bar/BaseSearchBar.vue | 2 - .../base-simple-table/BaseSimpleTable.vue | 19 +- .../components/base/base-tabs/BaseTabs.vue | 2 +- .../fields/custom-field/CustomField.vue | 4 +- .../container/fields/sandbox/Sandbox.vue | 4 +- .../components/EntityComponent.vue | 5 +- .../container/questions/QuestionsForm.vue | 74 +- .../DndSelection.component.vue | 1 - .../QuestionHeader.component.vue | 1 - .../similarity/SimilarityConfigDropdown.vue | 4 +- .../header/RadioButtonsSelect.base.vue | 67 +- .../header/metadata-filter/MetadataFilter.vue | 64 +- .../responses-filter/ResponsesFilter.vue | 62 +- .../annotation/header/sort-filter/Sort.vue | 35 +- .../header/sort-filter/SortSelector.vue | 27 +- .../header/sort-filter/SortSelectorItem.vue | 30 +- .../suggestion-filter/SuggestionFilter.vue | 112 +- .../progress/status-counter/StatusCounter.vue | 4 +- .../settings/useSettingsQuestionsViewModel.ts | 15 +- .../shortcuts/AnnotationHelpShortcut.vue | 13 +- .../DatasetConfigurationForm.vue | 82 +- .../DatasetConfigurationMetadataSelector.vue | 1 - .../DatasetConfigurationQuestion.vue | 2 +- .../configuration/useDatasetConfiguration.ts | 40 +- .../features/documents/DocumentsList.vue | 1 - .../documents/useDocumentsListViewModel.ts | 13 +- .../components/features/global/AppHeader.vue | 2 +- .../global/user/UserAvatarTooltip.vue | 1 - .../home/dataset-list/DatasetCard.vue | 9 +- .../home/dataset-list/DatasetListCards.vue | 4 +- .../dataset-list/sort-filter/DatasetsSort.vue | 25 +- .../sort-filter/DatasetsSortSelectorItem.vue | 37 +- .../workspaces-filter/WorkspacesFilter.vue | 30 +- .../features/home/sidebar/ImportDocuments.vue | 1 - .../features/import/ImportBatchProgress.vue | 11 +- .../components/features/import/ImportFlow.vue | 95 +- .../features/import/ImportSummary.vue | 65 +- .../import/analysis/ImportAnalysisTable.vue | 174 +- .../import/file-upload/CsvColumnSelection.vue | 46 +- .../import/file-upload/ImportFileUpload.vue | 1126 ++- .../file-upload/ImportSummarySidebar.vue | 13 +- .../features/import/file-upload/PdfUpload.vue | 29 +- .../import/file-upload/TableUpload.vue | 27 +- .../features/import/file-upload/types.ts | 8 +- .../useImportFileUploadViewModel.ts | 31 +- .../import/file-upload/usePdfUploadLogic.ts | 2 +- .../import/file-upload/useTableUploadLogic.ts | 16 +- .../history/ImportHistoryDataPreview.vue | 37 +- .../history/ImportHistoryDetailsModal.vue | 23 +- .../import/history/ImportHistoryList.vue | 24 +- .../import/recent/RecentImportCard.vue | 4 +- .../recent/useRecentImportsViewModel.ts | 3 +- .../components/features/import/types.ts | 5 +- .../user-settings/UserSettingsTheme.vue | 1 - .../useUserSettingsLanguageViewModel.ts | 4 +- .../annotation-mode.page.spec.ts | 152 +- .../autosave-annotation-mode.page.spec.ts | 65 +- .../goToAnnotationPage.ts | 5 +- ...s-and-sorting-annotation-mode.page.spec.ts | 57 +- .../e2e/common/dataset-api-mock.ts | 45 +- .../e2e/common/field-api-mock.ts | 17 +- .../e2e/common/import-api-mock.ts | 446 +- extralit-frontend/e2e/common/index.ts | 10 +- .../e2e/common/login-and-wait-for.ts | 9 +- .../e2e/common/metadata-api-mock.ts | 22 +- .../e2e/common/question-api-mock.ts | 78 +- .../e2e/common/record-api-mock.ts | 89 +- .../dataset-setting-page.spec.ts | 43 +- .../e2e/datasets-page/datasets-page.spec.ts | 17 +- .../import-configuration-workflow.spec.ts | 262 +- extralit-frontend/eslint.config.mjs | 199 + extralit-frontend/nuxt.config.ts | 11 +- extralit-frontend/package-lock.json | 6917 +++++++---------- extralit-frontend/package.json | 27 +- extralit-frontend/pages/index.vue | 24 +- extralit-frontend/plugins/click-outside.ts | 6 +- extralit-frontend/test/setup.ts | 6 +- .../v1/domain/entities/common/Filter.ts | 5 +- .../v1/domain/entities/document/Document.ts | 98 +- .../entities/environment/Environment.ts | 4 +- .../v1/domain/entities/hub/DatasetCreation.ts | 6 +- .../entities/hub/DatasetCreationBuilder.ts | 5 +- .../v1/domain/entities/hub/FieldCreation.ts | 6 +- .../domain/entities/hub/MetadataCreation.ts | 6 +- .../v1/domain/entities/hub/Subset.ts | 5 +- .../import/ImportHistoryDatasetBuilder.ts | 9 +- .../entities/question/QuestionAnswer.ts | 35 +- .../v1/domain/entities/question/Suggestion.ts | 8 +- .../v1/domain/entities/record/RecordStatus.ts | 5 +- .../v1/domain/entities/record/Records.ts | 6 +- .../entities/response/ResponseFilter.ts | 9 +- .../v1/domain/entities/sort/SortList.ts | 10 +- .../entities/suggestion/SuggestionCriteria.ts | 2 +- .../entities/suggestion/SuggestionFilter.ts | 14 +- .../v1/domain/entities/workspace/Workspace.ts | 5 +- .../v1/domain/services/FileParsingService.ts | 1 - .../v1/domain/services/IDatasetRepository.ts | 2 +- .../v1/domain/services/IWorkspaceStorage.ts | 2 +- .../usecases/create-dataset-use-case.ts | 2 +- ...et-document-by-record-metadata-use-case.ts | 2 +- .../get-extraction-completion-use-case.ts | 48 +- .../get-hf-dataset-creation-use-case.ts | 2 +- ...get-import-compatible-datasets-use-case.ts | 4 +- .../v1/domain/usecases/load-user-use-case.ts | 5 +- .../usecases/update-dataset-use-case.ts | 8 +- .../repositories/AuthRepository.ts | 8 +- .../repositories/DatasetRepository.ts | 2 +- .../repositories/DocumentRepository.ts | 2 +- .../repositories/JobRepository.ts | 5 +- .../repositories/OAuthRepository.ts | 5 +- .../repositories/WorkspaceRepository.ts | 6 +- .../infrastructure/services/format-number.ts | 9 +- 128 files changed, 6074 insertions(+), 7088 deletions(-) delete mode 100644 extralit-frontend/.eslintignore delete mode 100644 extralit-frontend/.eslintrc.js create mode 100644 extralit-frontend/.prettierignore create mode 100644 extralit-frontend/eslint.config.mjs diff --git a/extralit-frontend/.eslintignore b/extralit-frontend/.eslintignore deleted file mode 100644 index 79aa66fbf..000000000 --- a/extralit-frontend/.eslintignore +++ /dev/null @@ -1,8 +0,0 @@ -node_modules/**/* -dist/**/* -.nuxt/**/* -e2e/**/* - -v1/domain/entities/document/Document.ts -v1/domain/usecases/get-extraction-completion-use-case.ts -components/base/base-render-table/* diff --git a/extralit-frontend/.eslintrc.js b/extralit-frontend/.eslintrc.js deleted file mode 100644 index 765584ef0..000000000 --- a/extralit-frontend/.eslintrc.js +++ /dev/null @@ -1,104 +0,0 @@ -module.exports = { - root: true, - env: { - node: true, - browser: true, - jest: true, - }, - extends: [ - "eslint:recommended", - "plugin:@intlify/vue-i18n/recommended", - "plugin:prettier/recommended", - "plugin:nuxt/recommended", - ], - plugins: ["vue"], - settings: { - "vue-i18n": { - localeDir: "./translation/*.js", - }, - }, - rules: { - // Formatting is advisory here (parity with the *.ts override and the separate - // `npm run format` step); keeps `lint --quiet` focused on real correctness rules. - "prettier/prettier": "warn", - "no-console": process.env.NODE_ENV === "production" ? "error" : "off", - "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", - "prefer-const": "warn", - "prefer-arrow-callback": "warn", - "no-unused-vars": ["warn", { ignoreRestSiblings: true }], - "@intlify/vue-i18n/no-raw-text": "off", - "@intlify/vue-i18n/no-v-html": "off", - "@intlify/vue-i18n/no-missing-keys": "warn", - "vue/attributes-order": [ - "warn", - { - order: [ - "DEFINITION", - "LIST_RENDERING", - "CONDITIONALS", - "RENDER_MODIFIERS", - "GLOBAL", - "UNIQUE", - "TWO_WAY_BINDING", - "OTHER_DIRECTIVES", - "OTHER_ATTR", - "EVENTS", - "CONTENT", - ], - alphabetical: false, - }, - ], - }, - globals: { - $nuxt: true, - vi: true, - // Nuxt 4 auto-imports, hand-declared to satisfy no-undef. This list can drift; - // the real fix is adopting `@nuxt/eslint` (flat config), which auto-generates - // these globals from the build manifest. Tracked as follow-up, not done here. - defineNuxtPlugin: "readonly", - defineNuxtRouteMiddleware: "readonly", - definePageMeta: "readonly", - navigateTo: "readonly", - abortNavigation: "readonly", - useNuxtApp: "readonly", - useRuntimeConfig: "readonly", - useRoute: "readonly", - useRouter: "readonly", - useState: "readonly", - useCookie: "readonly", - useHead: "readonly", - useSeoMeta: "readonly", - useError: "readonly", - createError: "readonly", - clearError: "readonly", - showError: "readonly", - }, - parser: "vue-eslint-parser", - parserOptions: { - parser: "@typescript-eslint/parser", - ecmaVersion: 2022, - sourceType: "module", - }, - overrides: [ - { - files: ["**/*.ts"], - extends: ["plugin:@typescript-eslint/recommended", "prettier"], - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "prettier"], - parserOptions: { project: ["./tsconfig.json"] }, - rules: { - "prettier/prettier": ["warn"], - quotes: ["warn", "double"], - semi: ["warn", "always"], - "import/no-named-as-default-member": 0, - "no-useless-constructor": 0, - "space-before-function-paren": 0, - "no-throw-literal": 0, - "no-new": 0, - // Ambient `declare namespace CSS { ... }` global augmentations (CSS Custom - // Highlight API) can't be expressed as ES modules; allow declaration-only namespaces. - "@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }], - }, - }, - ], -}; diff --git a/extralit-frontend/.prettierignore b/extralit-frontend/.prettierignore new file mode 100644 index 000000000..26dd47d02 --- /dev/null +++ b/extralit-frontend/.prettierignore @@ -0,0 +1,11 @@ +# Dependencies and build output / generated dirs (prettier has no implicit ignores +# beyond node_modules; without this it tries to parse generated .vue files in .nuxt*). +node_modules +dist +.output +.nuxt +.nuxt-stale-root +.vercel + +# Lockfiles and generated data +package-lock.json diff --git a/extralit-frontend/components/base/BaseSvgIcon.vue b/extralit-frontend/components/base/BaseSvgIcon.vue index 843280a53..80492c076 100644 --- a/extralit-frontend/components/base/BaseSvgIcon.vue +++ b/extralit-frontend/components/base/BaseSvgIcon.vue @@ -47,9 +47,7 @@ const svg = computed(() => { // Strip the intrinsic width/height off the root so ours win (viewBox // keeps the aspect ratio), then inject the requested dimensions. - markup = markup - .replace(/(]*?)\swidth="[^"]*"/i, "$1") - .replace(/(]*?)\sheight="[^"]*"/i, "$1"); + markup = markup.replace(/(]*?)\swidth="[^"]*"/i, "$1").replace(/(]*?)\sheight="[^"]*"/i, "$1"); const sized: string[] = []; if (width) sized.push(`width="${width}"`); if (height) sized.push(`height="${height}"`); diff --git a/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue b/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue index 3146ffd37..869aee2ac 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue @@ -17,18 +17,10 @@ :is-last-breadcrumb="index === filteredBreadcrumbs.length - 1" /> - + {{ breadcrumb.name }} - + {{ breadcrumb.name }} @@ -39,7 +31,10 @@ class="breadcrumbs__copy" @click.prevent=" $copyToClipboard( - filteredBreadcrumbs.slice(-2).map(breadcrumb => breadcrumb.name).join('/') + filteredBreadcrumbs + .slice(-2) + .map((breadcrumb) => breadcrumb.name) + .join('/') ) " > diff --git a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue index f276b7776..6b0f6696b 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue @@ -2,50 +2,45 @@ @@ -95,28 +90,26 @@ export default { if (!this.selectedWorkspace) { return this.allDatasets; } - return this.allDatasets.filter(dataset => dataset.workspaceId === this.selectedWorkspace.id); + return this.allDatasets.filter((dataset) => dataset.workspaceId === this.selectedWorkspace.id); }, selectedDataset(): Dataset | null { - return this.datasets.find(d => d.id === this.datasetId) || null; + return this.datasets.find((d) => d.id === this.datasetId) || null; }, selectedDatasetName(): string { - return this.selectedDataset?.name || this.$t('Select dataset'); + return this.selectedDataset?.name || this.$t("Select dataset"); }, selectedDatasetId: { get(): string | null { return this.datasetId; }, set(datasetId: string | null) { - const dataset = this.datasets.find(d => d.id === datasetId) || null; + const dataset = this.datasets.find((d) => d.id === datasetId) || null; this.onDatasetChange(dataset); this.visibleDropdown = false; - } + }, }, datasetsFilteredBySearchText(): Dataset[] { - return this.datasets.filter((dataset) => - dataset.name.toLowerCase().includes(this.searchText.toLowerCase()) - ); + return this.datasets.filter((dataset) => dataset.name.toLowerCase().includes(this.searchText.toLowerCase())); }, }, methods: { @@ -130,14 +123,14 @@ export default { path: `/dataset/${dataset.id}/annotation-mode`, query: { ...this.$route.query, - workspace: this.selectedWorkspace?.name - } + workspace: this.selectedWorkspace?.name, + }, }); } }, onDatasetSelectionChange(datasetId: string) { this.selectedDatasetId = datasetId; - } + }, }, }; @@ -290,4 +283,4 @@ export default { flex: 1; } } - \ No newline at end of file + diff --git a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue index 9b8ad0fec..9af9eea1e 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue @@ -2,50 +2,45 @@ @@ -83,18 +78,18 @@ export default { return this.workspaceStore.get().selectedWorkspace; }, selectedWorkspaceName(): string { - return this.selectedWorkspace?.name || this.$t('Select workspace'); + return this.selectedWorkspace?.name || this.$t("Select workspace"); }, selectedWorkspaceId: { get(): string | null { return this.selectedWorkspace?.id || null; }, set(workspaceId: string | null) { - const workspace = this.workspaces.find(w => w.id === workspaceId) || null; + const workspace = this.workspaces.find((w) => w.id === workspaceId) || null; this.workspaceStore.saveSelectedWorkspace(workspace); this.onWorkspaceChange(workspace); this.visibleDropdown = false; - } + }, }, workspacesFilteredBySearchText(): Workspace[] { return this.workspaces.filter((workspace) => @@ -110,26 +105,26 @@ export default { // Navigate to home page (dataset selection) when workspace changes // This ensures users always go to the dataset selection view when switching workspaces const targetRoute = { - path: '/', - query: workspace ? { workspace: workspace.name } : {} + path: "/", + query: workspace ? { workspace: workspace.name } : {}, }; // Only navigate if we're not already on the home page - if (this.$route.path !== '/') { + if (this.$route.path !== "/") { this.$router.push(targetRoute); } // Emit breadcrumb link update event with workspace information - this.$emit('workspace-change', { + this.$emit("workspace-change", { workspace, workspaceId: workspace?.id || null, workspaceName: workspace?.name || null, - link: targetRoute + link: targetRoute, }); }, onWorkspaceSelectionChange(workspaceId: string) { this.selectedWorkspaceId = workspaceId; - } + }, }, }; @@ -282,5 +277,4 @@ export default { flex: 1; } } - - \ No newline at end of file + diff --git a/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue b/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue index 0d01dd752..3a152029c 100644 --- a/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue +++ b/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue @@ -87,7 +87,6 @@ \ No newline at end of file + diff --git a/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue b/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue index 509268352..b8a760646 100644 --- a/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue +++ b/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue @@ -3,7 +3,7 @@
-

{{ isAnalyzing ? 'Analyzing import status...' : 'Loading...' }}

+

{{ isAnalyzing ? "Analyzing import status..." : "Loading..." }}

@@ -12,9 +12,7 @@

Analysis Failed

{{ errorMessage }}

- - Retry Analysis - + Retry Analysis
@@ -83,13 +81,9 @@ import type { ImportAnalysisResponse, ImportStatus, DocumentImportAnalysis, -} from '~/v1/domain/entities/import/ImportAnalysis'; -import { - type AnalysisTableRow, - type TableColumn, - type CellComponent, -} from '../types'; -import { useImportAnalysisTableViewModel } from './useImportAnalysisTableViewModel'; +} from "~/v1/domain/entities/import/ImportAnalysis"; +import { type AnalysisTableRow, type TableColumn, type CellComponent } from "../types"; +import { useImportAnalysisTableViewModel } from "./useImportAnalysisTableViewModel"; import { Workspace } from "~/v1/domain/entities/workspace/Workspace"; import { TableData } from "~/v1/domain/entities/table/TableData"; @@ -152,8 +146,8 @@ export default { // Get analysis info if available const analysisInfo = this.analysisResult?.documents?.[reference]; - const currentStatus = documentActions[reference] || analysisInfo?.status || 'add'; - const originalStatus = analysisInfo?.status || 'add'; + const currentStatus = documentActions[reference] || analysisInfo?.status || "add"; + const originalStatus = analysisInfo?.status || "add"; const validationErrors = analysisInfo?.validation_errors || []; // Use pre-processed file paths from ImportFileUpload.vue @@ -173,8 +167,8 @@ export default { canToggle: this.canToggleStatus(originalStatus) && !this.isAnalyzing, }; - Object.keys(row).forEach(key => { - if (!['reference', 'title', 'authors', 'author', 'year', 'filePaths'].includes(key)) { + Object.keys(row).forEach((key) => { + if (!["reference", "title", "authors", "author", "year", "filePaths"].includes(key)) { rowData[key] = row[key]; } }); @@ -184,17 +178,17 @@ export default { }, tableData(): AnalysisTableRow[] { - const filteredData = this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0); + const filteredData = this.allTableData.filter((row) => row.filePaths && row.filePaths.length > 0); return filteredData; }, referencesWithoutPdfsCount(): number { - return this.allTableData.filter(row => !row.filePaths || row.filePaths.length === 0).length; + return this.allTableData.filter((row) => !row.filePaths || row.filePaths.length === 0).length; }, referencesWithPdfsCount(): number { - return this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0).length; + return this.allTableData.filter((row) => row.filePaths && row.filePaths.length > 0).length; }, filteredDataframeData(): TableData | null { @@ -244,9 +238,9 @@ export default { // Add dynamic columns from dataframe schema if (this.dataframeData?.schema?.fields) { - const excludedFields = ['reference', 'title', 'authors', 'author', 'year', 'filePaths', 'type']; + const excludedFields = ["reference", "title", "authors", "author", "year", "filePaths", "type"]; - this.dataframeData.schema.fields.forEach(field => { + this.dataframeData.schema.fields.forEach((field) => { if (!excludedFields.includes(field.name)) { columns.push({ field: field.name, @@ -277,12 +271,12 @@ export default { headerFilterParams: { values: { "": "All", - "add": "Add", - "update": "Update", - "skip": "Skip", - "ignore": "Ignore", - "failed": "Failed" - } + add: "Add", + update: "Update", + skip: "Skip", + ignore: "Ignore", + failed: "Failed", + }, }, } ); @@ -324,7 +318,7 @@ export default { // Count from dataframe data - only include references with PDFs this.dataframeData.data.forEach((row: Record) => { const reference = row.reference || row.key; - const finalAction = documentActions[reference] || 'add'; + const finalAction = documentActions[reference] || "add"; const filePaths = row.filePaths || []; const hasFiles = filePaths.length > 0; @@ -347,8 +341,7 @@ export default { }, shouldShowEditableTable() { - return (!this.dataframeData || this.dataframeData.data.length === 0) - && this.allPdfFileNames.length > 0; + return (!this.dataframeData || this.dataframeData.data.length === 0) && this.allPdfFileNames.length > 0; }, allPdfFileNames() { @@ -360,8 +353,7 @@ export default { unmappedPdfFiles() { const assigned = new Set(); this.editableTableData.forEach((row: any) => { - (Array.isArray(row.files) ? row.files : row.files ? [row.files] : []) - .forEach((f: string) => assigned.add(f)); + (Array.isArray(row.files) ? row.files : row.files ? [row.files] : []).forEach((f: string) => assigned.add(f)); }); return (this.allPdfFileNames as string[]).filter((n: string) => !assigned.has(n)); }, @@ -406,8 +398,8 @@ export default { const current = cell.getValue() || []; const currentSet = new Set(Array.isArray(current) ? current : [current]); const values = [...allFiles] - .filter(f => !usedFiles.has(f) || currentSet.has(f)) - .map(f => ({ label: f, value: f })); + .filter((f) => !usedFiles.has(f) || currentSet.has(f)) + .map((f) => ({ label: f, value: f })); return { values, multiselect: true, @@ -423,8 +415,11 @@ export default { } const files = Array.isArray(value) ? value : [value]; const [first, ...rest] = files; - const suffix = rest.length > 0 ? ` +${rest.length}` : ''; - return `${first}${suffix}`; + const suffix = + rest.length > 0 + ? ` +${rest.length}` + : ""; + return `${first}${suffix}`; }, }, ]; @@ -447,7 +442,7 @@ export default { // Reset local document actions when new analysis data arrives this.localDocumentActions = {}; // Emit the analysis complete event - this.$emit('analysis-complete', newData); + this.$emit("analysis-complete", newData); // Emit initial update this.emitUpdate(); } @@ -479,7 +474,7 @@ export default { return `${files}`; } const fileCount = files.split(", ").filter((f: string) => f.trim().length > 0).length; - return `${fileCount} file${fileCount !== 1 ? 's' : ''}`; + return `${fileCount} file${fileCount !== 1 ? "s" : ""}`; }, statusFormatter(cell: CellComponent) { @@ -489,10 +484,10 @@ export default { const statusClass = `status-${status}`; const statusText = this.getStatusText(status); - const toggleIcon = canToggle ? '' : ''; + const toggleIcon = canToggle ? '' : ""; return ` -
+
${statusText} ${toggleIcon} @@ -521,7 +516,7 @@ export default { update: "Update", skip: "Skip", ignore: "Ignore", - failed: "Failed" + failed: "Failed", }; return statusMap[status] || status; }, @@ -564,8 +559,8 @@ export default { formatColumnTitle(fieldName: string) { // Convert field names to readable titles return fieldName - .replace(/([A-Z])/g, ' $1') - .replace(/^./, str => str.toUpperCase()) + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()) .trim(); }, @@ -577,7 +572,7 @@ export default { if (this.shouldShowEditableTable) { this.editableTableData.forEach((row: any) => { const reference = row.reference?.trim(); - const filesArray = Array.isArray(row.files) ? row.files : (row.files ? [row.files] : []); + const filesArray = Array.isArray(row.files) ? row.files : row.files ? [row.files] : []; if (!reference || filesArray.length === 0) return; confirmedDocuments[reference] = { document_create: { @@ -599,39 +594,45 @@ export default { })), }; }); - // Handle analysis data case (preferred) - } else if (this.analysisResult && this.analysisResult.documents && Object.keys(this.analysisResult.documents).length > 0) { - Object.entries(this.analysisResult.documents).forEach(([reference, docInfo]: [string, DocumentImportAnalysis]) => { - const finalAction = documentActions[reference] || docInfo.status; - const hasFiles = docInfo.associated_files && docInfo.associated_files.length > 0; - - // Only include references with PDFs - if (!hasFiles) { - return; - } + // Handle analysis data case (preferred) + } else if ( + this.analysisResult && + this.analysisResult.documents && + Object.keys(this.analysisResult.documents).length > 0 + ) { + Object.entries(this.analysisResult.documents).forEach( + ([reference, docInfo]: [string, DocumentImportAnalysis]) => { + const finalAction = documentActions[reference] || docInfo.status; + const hasFiles = docInfo.associated_files && docInfo.associated_files.length > 0; + + // Only include references with PDFs + if (!hasFiles) { + return; + } - // Only include documents that will be processed (add or update) - if (finalAction === "add" || finalAction === "update") { - // Ensure metadata is included in document_create - const documentCreate = { - ...docInfo.document_create, - metadata: { - source: "bib_import", - collections: [this.workspace?.name || "default"], - ...docInfo.document_create.metadata, - }, - }; + // Only include documents that will be processed (add or update) + if (finalAction === "add" || finalAction === "update") { + // Ensure metadata is included in document_create + const documentCreate = { + ...docInfo.document_create, + metadata: { + source: "bib_import", + collections: [this.workspace?.name || "default"], + ...docInfo.document_create.metadata, + }, + }; - confirmedDocuments[reference] = { - document_create: documentCreate, - associated_files: docInfo.associated_files || [], - }; + confirmedDocuments[reference] = { + document_create: documentCreate, + associated_files: docInfo.associated_files || [], + }; + } } - }); + ); } else if (this.dataframeData && this.dataframeData.data.length > 0) { this.dataframeData.data.forEach((row: Record) => { const reference = row.reference || row.key || `row_${Math.random()}`; - const finalAction = documentActions[reference] || 'add'; + const finalAction = documentActions[reference] || "add"; const filePaths = row.filePaths || []; const hasFiles = filePaths.length > 0; @@ -646,7 +647,7 @@ export default { document_create: { reference, title: row.title, - authors: Array.isArray(row.authors) ? row.authors : (row.authors ? [row.authors] : undefined), + authors: Array.isArray(row.authors) ? row.authors : row.authors ? [row.authors] : undefined, year: row.year ? String(row.year) : undefined, journal: row.journal, volume: row.volume, @@ -654,7 +655,7 @@ export default { doi: row.doi, url: row.url, abstract: row.abstract, - keywords: Array.isArray(row.keywords) ? row.keywords : (row.keywords ? [row.keywords] : undefined), + keywords: Array.isArray(row.keywords) ? row.keywords : row.keywords ? [row.keywords] : undefined, pmid: row.pmid, workspace_id: this.workspace?.id, metadata: { @@ -664,7 +665,7 @@ export default { }, associated_files: filePaths.map((filename: string) => ({ filename, - size: this.getFileSize(filename) || 0 + size: this.getFileSize(filename) || 0, })), }; } @@ -706,17 +707,18 @@ export default { initializeEditableTable() { const pdfFiles = this.allPdfFileNames as string[]; - this.editableTableData = pdfFiles.length > 0 - ? pdfFiles.map((filename: string) => ({ - reference: null, - title: null, - files: [filename], - })) - : Array.from({ length: 3 }, () => ({ - reference: null, - title: null, - files: [], - })); + this.editableTableData = + pdfFiles.length > 0 + ? pdfFiles.map((filename: string) => ({ + reference: null, + title: null, + files: [filename], + })) + : Array.from({ length: 3 }, () => ({ + reference: null, + title: null, + files: [], + })); this.emitUpdate(); }, @@ -779,9 +781,9 @@ export default { { name: "abstract", type: "string" }, { name: "file", type: "string" }, ], - primaryKey: ["reference"] + primaryKey: ["reference"], }, - data: dataRows + data: dataRows, }; } diff --git a/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue b/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue index 163e37ad5..b422db99a 100644 --- a/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue +++ b/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue @@ -11,7 +11,11 @@