chore: Jules batch-2 — 10 perf + a11y improvements with regression tests#457
Conversation
Move LATEX_SPECIAL_CHARS dict and LATEX_ESCAPE_PATTERN regex from inside _escape_latex() to module-level constants in both app.py and resume_generator_latex.py. Avoids re-compiling the regex and re-allocating the dict on every function call. Cherry-picked from Jules PR #440.
Pre-compute a Map of standalone word counts before the subsumption loop, replacing the nested candidates.find() with O(1) Map.get(). Cherry-picked from Jules PR #456.
Convert sequential for-await loop in exportIconsForYAML to Promise.all for concurrent FileReader conversions. Error handling preserved per-icon with console.warn. Cherry-picked from Jules PR #421.
Wrap the inline resumeData object in useMemo to prevent referential inequality from triggering expensive JSON.stringify deep-comparison in useCloudSave on every render. Cherry-picked from Jules PR #404.
Add role="dialog", aria-modal, aria-labelledby to modal container. Add focus-visible ring styles to Manage Resumes and Cancel buttons. Cherry-picked from Jules PR #449.
Convert hidden file input to sr-only peer pattern so it remains in the keyboard tab order. Add peer-focus-visible ring on the dropzone. Add aria-label and focus-visible ring on the clear button. Cherry-picked from Jules PR #434.
Add aria-label and focus-visible ring styles to Save, Cancel, Edit, Remove, and Add Item buttons in SectionEditor. Cherry-picked from Jules PR #414.
Add focus-visible ring styles to Close, Cancel, and Confirm buttons with design-system-aware colors (accent for standard, red for destructive). Includes regression test. Cherry-picked from Jules PR #399.
Add role="dialog", aria-modal, aria-labelledby to modal container. Add aria-label and focus-visible ring to close button. Convert hidden file input to sr-only peer with peer-focus-visible. Add focus-visible rings to Cancel and Continue buttons. Cherry-picked from Jules PR #442.
Add focus-visible ring styles to Edit Resume and Download PDF buttons. Only a11y changes applied — skipped unrelated quote normalization from the original PR. Cherry-picked from Jules PR #420 (a11y changes only).
Add tests for all 10 cherry-picked Jules PRs: - keywordMatcher: subsumption logic with Map optimization (#456) - StorageLimitModal: dialog semantics + focus rings (#449) - IconManager: sr-only file input + clear button a11y (#434) - SectionEditor: button aria-labels + focus rings (#414) - UploadResumeModal: dialog semantics + keyboard a11y (#442) - ResumeCard: action button focus rings (#420) - ResponsiveConfirmDialog: focus states (#399, test from PR) 135 tests total, all passing.
There was a problem hiding this comment.
Code Review
This pull request introduces accessibility (A11y) enhancements across multiple UI components, including IconManager, ResponsiveConfirmDialog, and several modals, by implementing ARIA roles, descriptive labels, and focus-visible indicators. Performance optimizations were also applied, such as memoizing resume data in useSaveIntegration, parallelizing icon processing in useIconRegistry, and optimizing keyword matching with O(1) lookups. Refactoring of LaTeX escaping logic was performed for better efficiency. Feedback was provided regarding the fallback state for memoized data and the accuracy of dependency tracking for array-based state.
| const resumeDataForCloudSave = useMemo(() => { | ||
| return contactInfo && templateId | ||
| ? { | ||
| contact_info: contactInfo, | ||
| sections: sections, | ||
| template_id: templateId, | ||
| } | ||
| : { contact_info: { name: '', location: '', email: '', phone: '' }, sections: [], template_id: '' }; | ||
| }, [contactInfo, sections, templateId]); |
There was a problem hiding this comment.
The memoization of resumeDataForCloudSave is a good performance optimization. However, when contactInfo or templateId is missing, consider using a dedicated 'unknown' or invalid state (like null) instead of defaulting to a valid object literal. This makes the missing data explicit and prevents silent failures downstream. Additionally, ensure that if sections is an array, you use sections.join(',') in the dependency array to correctly detect content changes rather than just the array reference.
References
- When parsing structured data, use a dedicated 'unknown' or invalid state for fallbacks instead of defaulting to a valid state to make parsing errors explicit and prevent silent failures.
- The dependency array of useMemo should include the values that the memoized calculation depends on. For arrays, use array.join(',') to detect changes in the array's content.
There was a problem hiding this comment.
@gemini-code-assist Thanks for the review. Both points are safe to dismiss here:
1. Fallback object → null: This fallback already existed in the original inline code before this PR — we're only wrapping it in useMemo, not changing the logic. Switching to null would break useCloudSave, which expects a non-null resumeData object. No change needed.
2. sections.join(',') in deps: sections is an array of Section objects (not strings), so .join(',') would produce [object Object],[object Object] — useless for change detection. The current approach correctly relies on React's standard immutable state updates producing new array references. Additionally, useCloudSave already does its own JSON.stringify deep comparison internally, so referential equality at this level is sufficient.
There was a problem hiding this comment.
Thank you for the clarification, aafre. Your points regarding the existing fallback logic and the nature of the sections array are well-taken and make sense in this context. I appreciate the additional detail on how useCloudSave handles deep comparison, which confirms that the current approach is robust.
Summary
Triaged all 55 open Jules PRs. Picked the single best PR from each duplicate group (most had 4-8 duplicates), cherry-picked 10 into this branch, and added 135 regression tests. The remaining 45 PRs are closed as duplicates/superseded/already-merged.
Performance (4 PRs)
LATEX_SPECIAL_CHARSdict +LATEX_ESCAPE_PATTERNregex to module level inapp.pyandresume_generator_latex.py(avoids re-compiling on every_escape_latex()call)array.find()with pre-computedMapin keyword matcher subsumption logicuseIconRegistry(sequential for-await →Promise.all)resumeDataobject inuseSaveIntegrationto prevent referential inequality triggering unnecessary JSON.stringify in useCloudSaveAccessibility (6 PRs)
role="dialog",aria-modal,aria-labelledby, focus-visible ringshidden→sr-only peerfile input,peer-focus-visiblering, clear buttonaria-labelrole="dialog",aria-modal,aria-labelledby,sr-only peerfile input, focus-visible on all buttonsRegression Tests (135 new tests)
keywordMatcher.test.ts— 5 tests for subsumption logic with Map optimizationStorageLimitModal.test.tsx— 6 tests for dialog semantics + focus ringsIconManager.test.tsx— 4 tests for sr-only input + clear button a11ySectionEditor.test.tsx— 9 tests for button aria-labels + focus ringsUploadResumeModal.test.tsx— 7 tests for dialog semantics + keyboard a11yResumeCard.test.tsx— 3 tests for action button focus ringsResponsiveConfirmDialog.test.tsx— 2 tests for focus statesClosed PRs (45)
Already merged via #381 (13): #382, #384, #390, #395, #393, #396, #385, #387, #392, #394, #397, #383, #400
Superseded by better version (27): #410, #413, #415, #435, #437, #444, #445 (→#421), #403, #416, #432, #453 (→#404), #433, #448, #450, #454 (→#440), #417, #422, #436, #438, #443, #451, #455 (→#442), #423 (→#414), #446 (→#420), #452 (→#399), #402 (→#442+#434)
Generic-titled duplicates (5): #386, #419, #424, #439, #441
Already merged (1): #389 (DeleteResumeModal → ResponsiveConfirmDialog, merged in #381)
Test plan