feat: add vocabulary list table view - #99
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c599f33a92
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (correctCount >= masteredTarget) { | ||
| return { label: '已掌握', tone: 'mastered' }; |
There was a problem hiding this comment.
Show due mastered words as due
When a word has reached masteryCount and its nextReview time later passes, VocabStore.getDueWords() still queues it for review, but this early return labels it only as “已掌握.” Because dueCount is also derived from this tone, the modal understates pending reviews and disagrees with the review queue. Check whether nextReview is due before returning the mastered status, or calculate the due statistic independently.
Useful? React with 👍 / 👎.
| showFeedbackMessage('词表面板未加载', 'warning'); | ||
| return; | ||
| } | ||
| state.ui.lastFocus = document.activeElement; |
There was a problem hiding this comment.
Restore focus to the visible menu trigger
When a keyboard user selects “查看词表,” the menu handler calls closeMenu() before this assignment, so document.activeElement is the menu item whose parent has just been hidden (or the browser's focus-fixup target), not the visible menu toggle. Closing the modal therefore tries to focus a hidden element and leaves keyboard users without a useful focus position; retain the menu toggle as the restoration target instead.
Useful? React with 👍 / 👎.
| name: getActiveListLabel(), | ||
| query: state.ui.listBrowserQuery || '', | ||
| learnedOnly: state.ui.listBrowserLearnedOnly, | ||
| words: getVisibleListWords() |
There was a problem hiding this comment.
Prevent filtered exports from replacing the whole list
When a search or “只看已学” filter is active, this exports only the visible subset, but the payload also contains a version string, so VocabDataIO.parseJson() classifies it as a progress backup. Importing that exported file through the existing menu then reaches performImport()'s setWords(entries) path and persistently replaces the active list with the subset, deleting every filtered-out word. Either export the complete active list or use a word-list format that the importer merges rather than replaces.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| .vocab-list-modal__body { | ||
| min-height: 220px; |
There was a problem hiding this comment.
Allow the list body to shrink on short screens
On short viewports at or below 768px wide, such as a phone in landscape, the mobile rules stack the three toolbar controls while forcing the dialog to height: 100%; the header, stacked toolbar, stats, and this 220px minimum exceed the available height. Since the dialog has overflow: hidden, part of the table viewport and its scrollbar is clipped. Let this flex child shrink with min-height: 0/flex: 1, or make the dialog itself scroll.
Useful? React with 👍 / 👎.
c599f33 to
2cf60df
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cf60df7d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const hasListId = typeof payload.listId === 'string' && payload.listId.trim(); | ||
| const hasV2ProgressEnvelope = typeof payload.version === 'string' | ||
| && isPlainObject(payload.config) | ||
| && hasListId; |
There was a problem hiding this comment.
Accept pre-v2 progress backups
Backups exported before the v2 data migration contain version, config, words, and reviewQueue, but no top-level listId. Requiring listId here makes every such backup fall through as a word list, so normalizeEntry() strips its scheduling fields and performImport() merges it instead of restoring the saved progress and configuration. Preserve the legacy reviewQueue/progress-field detection and derive the list identity from config.activeListId when importing that format.
Useful? React with 👍 / 👎.
Summary