Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/db_tools/parse-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class ParseVersion {
'Stillness (non-distracting progress indicators)',
'Use In-Process Machine for Suggestions',
'Use Serval for Suggestions',
'Use Echo for Pre-Translation Drafting',
'Allow Echo for Pre-Translation Drafting',
'Allow Fast Pre-Translation Training',
'Upload Paratext Zip Files for Pre-Translation Drafting',
'Allow mixing in an additional training source',
'Updated Learning Rate For Serval',
'Dark Mode',
'Dark mode',
'Enable Lynx insights',
'Preview new draft history interface',
'USFM Format',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,22 @@ export async function generateDraft(
await user.click(page.getByRole('button', { name: 'Configure sources' }));
await screenshot(page, { pageName: 'configure_sources_initial', ...context });

const trainingDataSection = page.locator('mat-card').nth(0);
const translationDataSection = page.locator('mat-card').nth(1);

// Step 1: Reference projects
await user.click(page.getByRole('combobox').first());
await user.click(trainingDataSection.getByRole('combobox').first());
await user.type('ntv');
await user.click(page.getByRole('option', { name: 'NTV - Nueva Traducción' }));
await user.click(page.getByRole('button', { name: 'Add another reference project' }));
await user.click(page.getByRole('combobox').last());
await user.click(trainingDataSection.getByRole('combobox').nth(1));
await user.type('dhh94');
await user.click(page.getByRole('option', { name: 'DHH94 - Spanish: Dios Habla' }));
await user.click(page.getByRole('button', { name: 'Next' }));

// Step 2: Source project
await user.click(page.getByRole('combobox'));
await user.click(translationDataSection.getByRole('combobox'));
await user.type('ntv');
await user.click(page.getByRole('option', { name: 'NTV - Nueva Traducción' }));
await user.click(page.getByRole('button', { name: 'Next' }));

// Step 3: Main project and other training data
await user.check(page.getByRole('checkbox', { name: 'All the language codes are correct' }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,49 +214,48 @@ export async function localizedScreenshots(
// Increase the height of the viewport to ensure all elements are visible
await page.setViewportSize({ width: originalViewportSize.width, height: 1200 });

await page.getByRole('combobox').fill('ntv');
const trainingDataSection = page.locator('mat-card').nth(0);
const translationDataSection = page.locator('mat-card').nth(1);

await trainingDataSection.getByRole('combobox').first().fill('ntv');
await page.getByRole('option', { name: 'NTV - Nueva Traducción' }).click();

const addReference = page.locator('.add-another-project');
const nextButton = page.locator('.step-button-wrapper').getByRole('button').last();

await forEachLocale(async locale => {
await user.hover(addReference, defaultArrowLocation);
await screenshotElements(
page,
[page.locator('app-draft-sources > .draft-sources-stepper'), page.locator('app-draft-sources > .overview')],
[trainingDataSection, translationDataSection],
{ ...context, pageName: 'configure_sources_draft_reference', locale },
{ margin: 8 }
);
});
await page.getByRole('combobox').fill('ntv');
await page.getByRole('option', { name: 'NTV - Nueva Traducción' }).click();

await user.click(addReference);
await page.getByRole('combobox').last().fill('dhh94');
await trainingDataSection.getByRole('combobox').nth(1).fill('dhh94');
await page.getByRole('option', { name: 'DHH94 - Spanish: Dios Habla' }).click();
await nextButton.click();

await forEachLocale(async locale => {
await page.getByRole('combobox').fill('ntv');
await translationDataSection.getByRole('combobox').fill('ntv');
await page.getByRole('option', { name: 'NTV - Nueva Traducción' }).click();
await user.hover(nextButton, defaultArrowLocation);
await user.hover(translationDataSection.getByRole('combobox'), defaultArrowLocation);
await screenshotElements(
page,
[page.locator('app-draft-sources > .draft-sources-stepper'), page.locator('app-draft-sources > .overview')],
[trainingDataSection, translationDataSection],
{ ...context, pageName: 'configure_sources_draft_source', locale },
{ margin: 8 }
);
});

await page.getByRole('combobox').fill('ntv');
await translationDataSection.getByRole('combobox').fill('ntv');
await page.getByRole('option', { name: 'NTV - Nueva Traducción' }).click();
await nextButton.click();

await forEachLocale(async locale => {
await user.hover(await page.getByRole('checkbox'));
await screenshotElements(
page,
[page.locator('app-draft-sources')],
[page.locator('app-configure-sources')],
{ ...context, pageName: 'configure_sources_confirm_languages', locale },
{ margin: 8 }
);
Expand Down
6 changes: 3 additions & 3 deletions src/SIL.XForge.Scripture/ClientApp/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import {
UsersAuthGuard
} from './shared/project-router.guard';
import { SyncComponent } from './sync/sync.component';
import { ConfigureSourcesComponent } from './translate/draft-generation/configure-sources/configure-sources.component';
import { DraftGenerationComponent } from './translate/draft-generation/draft-generation.component';
import { DraftOnboardingFormComponent } from './translate/draft-generation/draft-signup-form/draft-onboarding-form.component';
import { DraftSourcesComponent } from './translate/draft-generation/draft-sources/draft-sources.component';
import { DraftUsfmFormatComponent } from './translate/draft-generation/draft-usfm-format/draft-usfm-format.component';
import { EditorComponent } from './translate/editor/editor.component';
import { TranslateOverviewComponent } from './translate/translate-overview/translate-overview.component';
Expand Down Expand Up @@ -67,8 +67,8 @@ export const APP_ROUTES: Routes = [
canDeactivate: [DraftNavigationAuthGuard]
},
{
path: 'projects/:projectId/draft-generation/sources',
component: DraftSourcesComponent,
path: 'projects/:projectId/draft-generation/configure-sources',
component: ConfigureSourcesComponent,
canActivate: [NmtDraftAuthGuard],
canDeactivate: [DraftNavigationAuthGuard]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<ng-container *transloco="let t; read: 'draft_sources'">
<h1>{{ t("configure_draft_sources") }}</h1>

<mat-card>
<mat-card-header>
<mat-card-title>{{ t("train_language_model") }}</mat-card-title>
</mat-card-header>
<mat-card-content class="training-data-card-content">
<h3 class="heading-left">{{ t("overview_reference") }} {{ parentheses(referenceLanguageDisplayName) }}</h3>

<div class="description-left">{{ t("select_primary_reference") }}</div>
<div class="inputs-left">
@for (source of trainingSources; track $index) {
<app-project-select
[isDisabled]="loading || !appOnline"
[projects]="projects"
[resources]="resources"
[nonSelectableProjects]="nonSelectableProjects"
[value]="source?.paratextId"
[hiddenParatextIds]="getHiddenParatextIds(trainingSources, source?.paratextId)"
(valueChange)="sourceSelected(trainingSources, $index, $event)"
[placeholder]="projectPlaceholder(source)"
></app-project-select>
}

@if (allowAddingATrainingSource) {
<div>{{ t("some_projects_use_back_translation") }}</div>
<button
mat-button
(click)="trainingSources.push(undefined); $event.preventDefault()"
class="add-another-project"
>
<mat-icon>add</mat-icon> {{ t("add_another_reference_project") }}
</button>
}
Comment on lines +28 to +35
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Stepper removal means empty project-select slots can no longer be cleaned up

The old goToStep() method cleaned up undefined entries from trainingSources/draftingSources arrays when navigating between steps. With the stepper removed, there's no cleanup mechanism. If a user clicks "Add another reference project" but doesn't select anything, the empty slot persists with no way to remove it (the "Add another reference project" button disappears because allowAddingATrainingSource checks trainingSources.length < 2). This doesn't cause a functional bug — save() filters out undefined values with filter(notNull) — but it's a minor UX regression where users can't undo adding an empty slot.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

</div>

<span class="arrow mirror-rtl"><mat-icon>arrow_right_alt</mat-icon></span>
<h3 class="heading-right">{{ t("overview_translated_project") }} {{ parentheses(targetLanguageDisplayName) }}</h3>
<div class="description-right">
@for (portion of i18n.interpolateVariables("draft_sources.project_always_used"); track $index) {
@if (portion.id === "currentProjectShortName") {
<strong>{{ currentProjectShortName }}</strong>
}
<!-- prettier-ignore -->
@else {{{ portion.text }}}
}
<!-- Select here any other projects to be used on the target side. All of these should be in the language of the target (<strong>{{ targetLanguageDisplayName }}</strong>). -->
</div>
<div class="inputs-right">
@for (source of trainingTargets; track $index) {
<app-project-select
[isDisabled]="true"
[projects]="projects"
[resources]="resources"
[nonSelectableProjects]="nonSelectableProjects"
[value]="source?.paratextId"
[hiddenParatextIds]="getHiddenParatextIds(trainingTargets, source?.paratextId)"
[placeholder]="projectPlaceholder(source)"
></app-project-select>
}
</div>
<div class="training-files">
<mat-divider></mat-divider>
<h3>{{ t("additional_training_data_optional") }}</h3>
<div>{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}</div>
<app-training-data-multi-select
[availableTrainingData]="availableTrainingFiles"
(trainingDataSelect)="onTrainingDataSelect($event)"
></app-training-data-multi-select>
</div>
</mat-card-content>
</mat-card>

<mat-card>
<mat-card-header>
<mat-card-title> {{ t("generate_a_draft_from_the_language_model") }} </mat-card-title>
</mat-card-header>
<mat-card-content class="translation-data-card-content">
<h3 class="heading-left">{{ t("overview_source") }} {{ parentheses(sourceLanguageDisplayName) }}</h3>
<div class="description-left">{{ t("select_project_to_translate") }}</div>
<div class="inputs-left">
@for (source of draftingSources; track $index) {
<app-project-select
[isDisabled]="loading || !appOnline"
[projects]="projects"
[resources]="resources"
[nonSelectableProjects]="nonSelectableProjects"
[value]="source?.paratextId"
[hiddenParatextIds]="getHiddenParatextIds(draftingSources, source?.paratextId)"
(valueChange)="sourceSelected(draftingSources, $index, $event)"
[placeholder]="projectPlaceholder(source)"
></app-project-select>
}
</div>
</mat-card-content>
</mat-card>

<app-language-codes-confirmation
class="confirm-language-codes"
[sources]="draftSourcesAsArray"
[clearCheckbox]="clearLanguageCodeConfirmationCheckbox"
(messageIfUserTriesToContinue)="languageCodeConfirmationMessageIfUserTriesToContinue = $event"
></app-language-codes-confirmation>

<div class="component-footer">
@if (!appOnline) {
<mat-error id="offline-message">
{{ t("offline_message") }}
</mat-error>
}
<div class="page-actions">
<button mat-button (click)="cancel()"><mat-icon>close</mat-icon>{{ t("cancel") }}</button>
<button id="save_button" mat-flat-button color="primary" (click)="save()" [disabled]="!appOnline">
<mat-icon>check</mat-icon>{{ t("save_and_sync") }}
</button>
</div>
</div>

@if (getControlState("projectSettings") != null) {
<mat-card class="saving">
<mat-card-header>
<mat-card-title>{{ t("saving_draft_sources") }}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="saving-indicator">
@if (getControlState("projectSettings") === ElementState.Submitting) {
<mat-spinner [diameter]="24" color="primary"></mat-spinner> {{ t("saving") }}
} @else if (getControlState("projectSettings") === ElementState.Submitted) {
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Invalid Material Icon name checkmark renders as text instead of an icon

In configure-sources.component.html:130, the icon name checkmark is used: <mat-icon class="success">checkmark</mat-icon>. However, checkmark is not a valid Material Icon name — the correct name is check (as used on line 142 of the same file for the sync-success indicator). Since Material Icons uses font ligatures, an unrecognized name like checkmark will render as the literal text "checkmark" inside the icon element rather than displaying a ✓ icon. This means when the project settings are saved successfully, the "All changes saved" indicator will show garbled text instead of a check icon.

Suggested change
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
<mat-icon class="success">check</mat-icon> {{ t("all_changes_saved") }}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

} @else {
<mat-icon class="failure">error</mat-icon> {{ t("failed_to_save_changes") }}
}
</div>
@for (entry of syncStatus | keyvalue; track entry.key) {
<div>
@if (entry.value.knownToBeOnSF) {
@if (entry.value.isSyncing) {
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_syncing") }}
} @else {
@if (entry.value.lastSyncSuccessful) {
<mat-icon class="success">check</mat-icon> {{ entry.value.shortName }} -
{{ t("state_sync_successful") }}
} @else {
<mat-icon class="failure">error</mat-icon> {{ entry.value.shortName }} -
{{ t("state_sync_failed") }}
}
}
} @else {
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_connecting") }}
}
</div>
}
</mat-card-content>
@if (allProjectsSavedAndSynced || getControlState("projectSettings") === ElementState.Error) {
<mat-card-actions align="end">
<button mat-button (click)="navigateToDrafting()"><mat-icon>close</mat-icon> {{ t("close") }}</button>
</mat-card-actions>
}
</mat-card>
}
</ng-container>
Loading
Loading