Repository navigation
Conversation
31ddd63 to
9c10239
Compare
The transcription field stayed unchecked because font data always reports spell check as off, online and offline. Co-authored-by: Cursor <cursoragent@cursor.com> There's now one parser for Transcribe step settings, `parseTranscribeStepSettings` in the new [web/src/crud/transcribeStepSettings.ts](web/src/crud/transcribeStepSettings.ts). Every transcribe-step parse site uses it, and one test file covers it. Typecheck is clean, the full Jest suite passes (1974 passed, 3 skipped), the mobile Cypress spec passes (40/40), and lint has no warnings in the changed files. Nothing is committed. **Before you look at the diff:** your local branch was reset to `f87f35ba`, so `31ddd63c` is now only on `origin`. That reset also removed the project-level spell-check test I added earlier, which was the wrong behaviour anyway. I built on the current `HEAD` and did not pull. **What the parser does** - It accepts a JSON string (online steps) or an object (offline Work Alone steps). - It type-checks the known fields: `artifactTypeId`, `language`, `sisterlanguage`, `font`, `fontSize`, `rtl`, `spellCheck` and `namedRegion`. - `language` can be the `name|bcp47` string or the older object form. - Older steps saved some flags as text: the ASR code was checking `rtl === 'true'`. So `'true'`/`'false'` are read as real booleans. A saved `spellCheck: "false"` therefore means off, rather than falling back to the artifact default. - Any other wrong-typed known field is treated as unset, and unknown keys (the ASR settings) pass through unchanged. - It replaces the old `TranscribeStepSettingsJson` type and the separate ASR settings type, so there's one type. - `resolveStepSpellCheck` still reads only the step's `spellCheck` flag; nothing reads the project setting. **Where it's used** - `crud`: [stepSpellCheck.ts](web/src/crud/stepSpellCheck.ts), [fontChoice.ts](web/src/crud/fontChoice.ts), [transcribeStepAsrSettings.ts](web/src/crud/transcribeStepAsrSettings.ts), [getLwcTranslationAsrSettings.ts](web/src/crud/getLwcTranslationAsrSettings.ts), [useGetAsrSettings.ts](web/src/crud/useGetAsrSettings.ts). - Components: - desktop [Transcriber.tsx](web/src/components/Transcriber.tsx); - [PassageDetailTranscribe.tsx](web/src/components/PassageDetail/PassageDetailTranscribe.tsx); - [PassageDetailTranscribeMobile.tsx](web/src/components/PassageDetail/mobile/transcribe/PassageDetailTranscribeMobile.tsx), six sites; - [BoldClauseTranscriptionEditor.tsx](web/src/components/PassageDetail/lwcTranscription/BoldClauseTranscriptionEditor.tsx); - [PassageDetailLwcTranscription.tsx](web/src/components/PassageDetail/PassageDetailLwcTranscription.tsx); - the step settings editor [TranscribeStepSettings.tsx](web/src/components/StepEditor/TranscribeStepSettings.tsx), 13 sites. - Three local `parseStepSettings` copies are gone. The change is 111 lines added and 186 removed, plus the new module and its test. **Bugs fixed along the way** - `fontChoice` threw on offline Work Alone steps, because it called `JSON.parse` on settings stored as an object. - The step editor crashed on `language.split` when the language was stored in the older object form. **Behaviour change to know about:** when the step settings editor saves, it writes back the parsed settings. A stored `"true"`/`"false"` is rewritten as a real boolean, and any other wrong-typed known field is dropped. **Tests:** all in [transcribeStepSettings.test.ts](web/src/crud/transcribeStepSettings.test.ts), 7 tests. The file failed before the module existed. The text-flag test was added red and went green once the parser read `'true'`/`'false'` as booleans.
9c10239 to
c7035b6
Compare
| const isObject = (value: unknown): value is Record<string, unknown> => | ||
| typeof value === 'object' && value !== null && !Array.isArray(value); | ||
|
|
||
| /** Each returns the normalized value, or undefined to drop the field. */ | ||
| type Normalize = (value: unknown) => unknown; | ||
|
|
||
| const asLanguage: Normalize = (value) => | ||
| typeof value === 'string' || isObject(value) ? value : undefined; | ||
|
|
||
| const asString: Normalize = (value) => | ||
| typeof value === 'string' ? value : undefined; | ||
|
|
||
| // Older steps saved some flags as "true" / "false". | ||
| const asBoolean: Normalize = (value) => { | ||
| if (typeof value === 'boolean') return value; | ||
| if (value === 'true') return true; | ||
| if (value === 'false') return false; | ||
| return undefined; | ||
| }; |
There was a problem hiding this comment.
Should we move these to a more general place so that in the future we can use them for other steps? I added an item to the Priority Refactor List to do a broader centralization of JSON parsing and Orbit decoding. We could wait until then, but if we move them to a more central location now we could start using them elsewhere if opportunity arises.
We could also consider using lodash isPlainObject and isString since lodash is already a dependency, but I'm okay with keeping these for now. I've put in the refactor list that we should consider switching to zod
| language?: unknown; | ||
| }; | ||
| const { bcp47: stepLang } = parseStepLanguageField(lgSettings?.language); | ||
| const lgSettings = parsedSettings; |
There was a problem hiding this comment.
Can we just use parsedSettings and get rid of lgSettings?
| ); | ||
| const asrTip = useMemo(() => { | ||
| const step = parseStepSettings(stepSettings); | ||
| const step = parseTranscribeStepSettings(stepSettings); |
There was a problem hiding this comment.
We call parseTranscribeStepSettings(stepSettings) twice in this file, we could cover both with a memoized parsedSettings like you did in PassageDetailTranscribe.tsx. I also prefer "parsedSettings" or "settings", what does "lg" means in "lgSettings"?
| [stepSettings] | ||
| ); | ||
|
|
||
| const artifactTypeId = parsedSettings?.artifactTypeId ?? null; |
There was a problem hiding this comment.
Now that parsedSettings is always an object, we can get rid of the ? and other falsiness guards that don't do anything anymore. Also at:
| let cancelled = false; | ||
|
|
||
| const lgSettings = parseStepSettings(stepSettings); | ||
| const lgSettings = parseTranscribeStepSettings(stepSettings); |
There was a problem hiding this comment.
If you decide to implement my earlier suggestions, can we rename this one to parsedSettings or settings also? And also on Transcriber.tsx L744
|
Still in the process of reviewing |
There was a problem hiding this comment.
Since this PR is about following the step spell-check setting: this editor decides spell check with lgSettings?.spellCheck === true (L240) instead of resolveStepSpellCheck, so when the step has no spellCheck saved it skips the artifact default. For Careful Speech that's the same result (default off), but for a Phrase Back Translation bold-clause step the default is on, so this editor shows spell check off while Transcriber and mobile Transcribe show it on. Could we switch it to resolveStepSpellCheck(lgSettings, <artifact slug>, stepLang) so all three surfaces agree? Small thing, but it's one line in a file you're already editing.
|
Wow, this is a really great cleanup! |
nabalone
left a comment
There was a problem hiding this comment.
I think this is a really good fix and direction to go. I added a bunch of comments but they are mostly trivial things that an AI should be able to do all in one sweep; it just looked like your AI may have been trying hard to keep the diff small whereas I think it would be worth it to make some more clarity changes. I can also tell my AI to do them as a follow-up PR if you would prefer.
| @@ -49,13 +39,11 @@ export function defaultSpellCheckForArtifact( | |||
|
|
|||
| /** Workflow step `tool.settings` is authoritative for Transcriber spell check. */ | |||
| export function resolveStepSpellCheck( | |||
There was a problem hiding this comment.
How about calling this isStepSpellCheckOn or something like that? When I first saw a usage I could not tell what the function was doing/what it would return. Sorry, I know this is not part of the PR, but if you agree and can do it easily while you are editing here I think it could be helpful
| return !!settings.spellCheck; | ||
| } | ||
| if (settings.spellCheck !== undefined) return settings.spellCheck; | ||
| return defaultSpellCheckForArtifact(artifactTypeSlug, bcp47, availLangs); |
There was a problem hiding this comment.
How about naming defaultSpellCheckForArtifact isSpellCheckOnByDefault or something like that? Sorry, I know this is not part of the PR, but if you agree and can do it easily while you are editing here I think it could be helpful
| * offline Work Alone steps store an object. Settings come from persisted Orbit | ||
| * records, so a known field with an unusable type is treated as unset. | ||
| */ | ||
| export function parseTranscribeStepSettings( |
There was a problem hiding this comment.
Now that bad settings no longer crash anything and silently become {}, a corrupted step looks exactly like a vernacular step with no settings, and we'd never find out. Could we add some error logging so we hear about it? Suggestions:
- In the
catch(malformed JSON) and the!isObject(parsed)exit (e.g."null",[1]), skippingundefined/''since those just mean "no settings". Including a truncated copy of the raw input would help us see what was stored. - When a known field has a value but normalizes to
undefined(wrong type), log the key and its type. Not for"true"/"false"(expected legacy data) and probably not fornull. If this turns out noisy it could beSeverity.info(breadcrumb only). - Since this is a plain function, it could import
bugsnagClientdirectly likelaunchAudacity.tsdoes:logError(Severity.error, bugsnagClient, ...). That also writes to the local log when offline, which covers Work Alone users. - This runs in render memos and per-step loops (e.g. the
vernacularStepsfilters), so it should only log each distinct bad input once (a module-levelSet), or one bad record will flood the logs.
Two related spots:
- TranscribeStepSettings.tsx
emitSettingsChange: this is the one place bad data becomes lost data, since saving replaces settings we couldn't read. IftoolSettingsis non-empty but parsed to{}, logging the original string before we overwrite it would let us recover it. - fontChoice.ts L107: the outer
toolstring is still an unguardedJSON.parse, so a malformed one still throws here even though the innersettingsparse is now safe. Could we guard and log it the same way?
| return undefined; | ||
| }; | ||
|
|
||
| const fieldNormalizers: Partial<Record<string, Normalize>> = { |
There was a problem hiding this comment.
Since this PR introduced reading legacy "true"/"false" flags as booleans, should phonetic and selectRoman go in this table too (asBoolean, and on the interface)? They're stored the same way, and their readers still check for the string themselves (transcribeStepAsrSettings.ts L86, L295-L302, L306, TranscribeStepSettings.tsx L250, L487), the same pattern this PR already removed for rtl. With them in the table those checks become === true and stepPhoneticIsSet becomes !== undefined. It would also mean saving in the step editor normalizes these like it now does rtl/spellCheck, instead of leaving mixed forms in the data. Nothing's broken today since every reader handles both forms, so I'm fine with this as a follow-up if you'd rather keep the PR from growing.
| setInitialValue(json.artifactTypeId); | ||
| const { languageName, bcp47 } = parseStepLanguageField(json?.language); | ||
| const slug = slugFromId(json.artifactTypeId); | ||
| const json = parseTranscribeStepSettings(toolSettings); |
There was a problem hiding this comment.
How about "settings" or "parsedSettings" instead of "json" for the variable name? It's not JSON anymore at this point, it's the parsed settings object. (Same for the other const json = parseTranscribeStepSettings(...) lines in this file.)
| it('spellchecks the transcription when the step spell check setting is on', () => { | ||
| const workflowSteps = [ | ||
| { | ||
| id: 'step-transcribe', | ||
| type: 'orgworkflowstep', | ||
| attributes: { | ||
| sequencenum: 1, | ||
| tool: JSON.stringify({ | ||
| tool: 'transcribe', | ||
| settings: JSON.stringify({ spellCheck: true }), | ||
| }), | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| mountTranscribeMobile({ | ||
| currentstep: 'step-transcribe', | ||
| workflowSteps, | ||
| offline: false, | ||
| }); | ||
|
|
||
| cy.get('#transcriptionText', { timeout: 10000 }).should( | ||
| 'have.attr', | ||
| 'spellcheck', | ||
| 'true' | ||
| ); | ||
| }); | ||
|
|
||
| it('spellchecks the transcription offline when the step spell check setting is on', () => { | ||
| const workflowSteps = [ | ||
| { | ||
| id: 'step-transcribe', | ||
| type: 'orgworkflowstep', | ||
| attributes: { | ||
| sequencenum: 1, | ||
| tool: JSON.stringify({ | ||
| tool: 'transcribe', | ||
| settings: JSON.stringify({ spellCheck: true }), | ||
| }), | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| mountTranscribeMobile({ | ||
| currentstep: 'step-transcribe', | ||
| workflowSteps, | ||
| offline: true, | ||
| }); | ||
|
|
||
| cy.get('#transcriptionText', { timeout: 10000 }).should( | ||
| 'have.attr', | ||
| 'spellcheck', | ||
| 'true' | ||
| ); | ||
| }); |
There was a problem hiding this comment.
This looks like it is asking for the flip test, that we don't spellcheck when spellcheck has been set off. But also it seems like spellcheck is not a super important feature, so if you don't think it's worth it or if you think it will take too much time, then okay
Summary
Test plan