Skip to content

Commit 246b7f6

Browse files
committed
feat: enhance card scheduling and dashboard functionality with audio playback improvements
1 parent d80a6c0 commit 246b7f6

File tree

4 files changed

+102
-42
lines changed

4 files changed

+102
-42
lines changed

src/srs/scheduler.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ export function calculatePriority(
9090
): number {
9191
let priority = 0;
9292

93+
// Special case: Learning cards (just reviewed 1-2 times, still fresh)
94+
// Give them medium-high priority for repetition in the same session
95+
if (srs.reps > 0 && srs.reps <= 2 && srs.intervalDays <= 1) {
96+
const timeSinceReview = now - (srs.lastReviewAt || srs.dueAt - srs.intervalDays * DAY_MS);
97+
const minutesSinceReview = timeSinceReview / 60000;
98+
99+
// If reviewed within last 30 minutes, give it medium priority (40-60)
100+
// This allows for spaced repetition within the same study session
101+
if (minutesSinceReview < 30) {
102+
priority += 40 + (30 - minutesSinceReview); // 40-70 points
103+
} else {
104+
priority += 35; // Base priority for learning cards
105+
}
106+
}
107+
93108
// 1. Overdue factor (0-100 points)
94109
// Cards past due get high priority
95110
const overdueMs = now - srs.dueAt;
@@ -198,13 +213,14 @@ export function getNextCard(
198213
if (loopMode && index.cards.size > 0) {
199214
const scoredAllCards: ScoredCard[] = [];
200215

201-
for (const cardId of index.dueCards) {
216+
// Include all cards (both due and not yet due)
217+
for (const [cardId, card] of index.cards) {
202218
if (excludeCardId && cardId === excludeCardId) continue;
203219

204-
const card = index.cards.get(cardId);
205220
const srs = index.srsStates.get(cardId);
206221

207-
if (card && srs && srs.reps > 0) {
222+
// Include all cards that have been reviewed at least once
223+
if (srs && srs.reps > 0) {
208224
scoredAllCards.push({
209225
card,
210226
srs,

src/webview/dashboard.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export class DashboardPanel {
192192
<head>
193193
<meta charset="UTF-8">
194194
<meta name="viewport" content="width=device-width, initial-scale=1.0">
195-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline';">
195+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; media-src https://dict.youdao.com https://translate.google.com;">
196196
<title>WordSlash Dashboard</title>
197197
<style>
198198
:root {
@@ -1462,6 +1462,10 @@ export class DashboardPanel {
14621462
14631463
// Show card details in modal
14641464
function showCardDetails(card, srs) {
1465+
// Update current card for audio playback
1466+
currentCard = card;
1467+
console.log('[WordSlash Dashboard] showCardDetails - currentCard set to:', currentCard?.front?.term);
1468+
14651469
const modal = document.getElementById('cardModal');
14661470
const term = document.getElementById('modalTerm');
14671471
const phonetic = document.getElementById('modalPhonetic');
@@ -1475,6 +1479,7 @@ export class DashboardPanel {
14751479
14761480
// Enable/disable audio button
14771481
playBtn.disabled = !card.front.term;
1482+
console.log('[WordSlash Dashboard] Audio button enabled:', !playBtn.disabled);
14781483
14791484
// Build modal body
14801485
let bodyHTML = '';
@@ -1713,7 +1718,11 @@ export class DashboardPanel {
17131718
17141719
// Play audio using configured TTS engine
17151720
async function playAudio() {
1716-
if (!currentCard || !currentCard.front.term) return;
1721+
console.log('[WordSlash Dashboard] playAudio called, currentCard:', currentCard);
1722+
if (!currentCard || !currentCard.front.term) {
1723+
console.error('[WordSlash Dashboard] Cannot play audio: currentCard is', currentCard);
1724+
return;
1725+
}
17171726
17181727
const playBtn = document.getElementById('playAudioBtn');
17191728
playBtn.disabled = true;

src/webview/panel.ts

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,41 @@ export class FlashcardPanel {
112112
vscode.commands.executeCommand('workbench.action.openSettings', 'wordslash.tts');
113113
break;
114114

115-
case 'refresh':
116-
// Clear recent cards cache and reload
115+
case 'refresh': {
116+
console.log('[WordSlash] Refresh requested');
117+
// Clear all caches and reload from storage
117118
this._recentCardIds = [];
119+
const currentCardId = this._currentCard?.id;
118120
this._currentCard = null;
119-
await this._sendNextCard();
121+
122+
// Try to reload the same card if it still exists, otherwise get next
123+
if (currentCardId) {
124+
console.log('[WordSlash] Reloading current card:', currentCardId);
125+
const cards = await this._storage.readAllCards();
126+
const events = await this._storage.readAllEvents();
127+
console.log('[WordSlash] Reloaded cards:', cards.length, 'events:', events.length);
128+
const index = buildIndex(cards, events);
129+
130+
// Get the latest version of the card from the index
131+
const reloadedCard = index.cards.get(currentCardId);
132+
133+
if (reloadedCard) {
134+
console.log('[WordSlash] Card found, reloading:', reloadedCard.front.term);
135+
console.log('[WordSlash] Card morphemes:', reloadedCard.front.morphemes);
136+
this._currentCard = reloadedCard;
137+
const srs = index.srsStates.get(reloadedCard.id);
138+
this._postMessage({ type: 'card', card: reloadedCard, srs });
139+
} else {
140+
console.log('[WordSlash] Card not found or deleted, getting next');
141+
// Card was deleted, get next one
142+
await this._sendNextCard();
143+
}
144+
} else {
145+
console.log('[WordSlash] No current card, getting next');
146+
await this._sendNextCard();
147+
}
120148
break;
149+
}
121150
}
122151
}
123152

@@ -210,7 +239,8 @@ export class FlashcardPanel {
210239
// Get current SRS state and calculate next
211240
const currentSrs = index.srsStates.get(cardId);
212241
if (currentSrs) {
213-
const nextSrs = calculateNextState(currentSrs, rating, Date.now());
242+
// Note: nextSrs calculation validates the SRS algorithm but index is already rebuilt
243+
calculateNextState(currentSrs, rating, Date.now());
214244
// Save updated index
215245
await this._storage.atomicWriteJson('index.json', {
216246
version: 1,
@@ -273,19 +303,15 @@ export class FlashcardPanel {
273303
}
274304
275305
.term-container {
276-
display: flex;
277-
align-items: center;
278-
justify-content: center;
279-
gap: 16px;
280306
margin-bottom: 16px;
281307
}
282308
283309
.term {
284-
font-size: 3.2em;
285-
font-weight: 700;
310+
font-size: 2.5em;
311+
font-weight: 500;
286312
color: var(--vscode-textLink-foreground);
287-
letter-spacing: 0.02em;
288-
font-family: var(--vscode-editor-font-family), 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
313+
letter-spacing: 0.04em;
314+
font-family: var(--vscode-editor-font-family), Monaco, 'SF Mono', 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
289315
}
290316
291317
.btn-speak {
@@ -317,16 +343,23 @@ export class FlashcardPanel {
317343
border-width: 2px;
318344
}
319345
346+
.phonetic-container {
347+
display: flex;
348+
align-items: center;
349+
justify-content: center;
350+
gap: 12px;
351+
margin-bottom: 20px;
352+
}
353+
320354
.phonetic {
321355
font-size: 1.3em;
322356
color: var(--vscode-descriptionForeground);
323-
margin-bottom: 20px;
324357
font-family: 'Lucida Sans Unicode', 'Arial Unicode MS', sans-serif;
325358
}
326359
327360
.morphemes {
328-
font-size: 1.1em;
329-
color: var(--vscode-textLink-foreground);
361+
font-size: 2.1em;
362+
color: #00d4ff;
330363
font-family: var(--vscode-editor-font-family), 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
331364
margin-top: 8px;
332365
margin-bottom: 16px;
@@ -340,7 +373,7 @@ export class FlashcardPanel {
340373
}
341374
342375
.morphemes .morpheme {
343-
font-weight: 600;
376+
font-weight: 500;
344377
}
345378
346379
.example-container {
@@ -375,11 +408,6 @@ export class FlashcardPanel {
375408
border-top: 2px solid var(--vscode-input-border);
376409
}
377410
378-
.back-content .morphemes {
379-
font-size: 1em;
380-
margin-bottom: 12px;
381-
}
382-
383411
.translation {
384412
font-size: 2.2em;
385413
margin-bottom: 16px;
@@ -523,9 +551,11 @@ export class FlashcardPanel {
523551
<div class="card" id="card-view">
524552
<div class="term-container">
525553
<div class="term" id="term"></div>
526-
<button class="btn-speak" onclick="speakTerm()" title="Pronounce term">🔊</button>
527554
</div>
528-
<div class="phonetic" id="phonetic"></div>
555+
<div class="phonetic-container" id="phonetic-container">
556+
<div class="phonetic" id="phonetic"></div>
557+
<button class="btn-speak btn-speak-small" onclick="speakTerm()" title="Pronounce term">🔊</button>
558+
</div>
529559
<div class="morphemes" id="morphemes"></div>
530560
<div class="example-container" id="example-container">
531561
<div class="example" id="example"></div>
@@ -537,7 +567,6 @@ export class FlashcardPanel {
537567
</div>
538568
539569
<div id="back-content" class="back-content hidden">
540-
<div class="morphemes" id="morphemes-back"></div>
541570
<div class="translation" id="translation"></div>
542571
<div class="example-cn" id="example-cn"></div>
543572
<div class="explanation" id="explanation"></div>
@@ -577,13 +606,15 @@ export class FlashcardPanel {
577606
// Listen for messages from extension
578607
window.addEventListener('message', event => {
579608
const message = event.data;
609+
console.log('[WordSlash UI] Received message:', message.type);
580610
581611
switch (message.type) {
582612
case 'tts_settings':
583613
ttsSettings = message.settings;
584614
console.log('[WordSlash] TTS settings:', ttsSettings);
585615
break;
586616
case 'card':
617+
console.log('[WordSlash UI] Displaying card:', message.card.front.term);
587618
showCard(message.card, message.srs);
588619
break;
589620
case 'empty':
@@ -603,8 +634,16 @@ export class FlashcardPanel {
603634
604635
// Show front
605636
document.getElementById('term').textContent = card.front.term;
606-
document.getElementById('phonetic').textContent = card.front.phonetic || '';
607-
document.getElementById('phonetic').classList.toggle('hidden', !card.front.phonetic);
637+
638+
// Show phonetic with container
639+
const phoneticContainer = document.getElementById('phonetic-container');
640+
const phoneticEl = document.getElementById('phonetic');
641+
if (card.front.phonetic) {
642+
phoneticEl.textContent = card.front.phonetic;
643+
phoneticContainer.classList.remove('hidden');
644+
} else {
645+
phoneticContainer.classList.add('hidden');
646+
}
608647
609648
// Show morphemes on front
610649
const morphemesEl = document.getElementById('morphemes');
@@ -630,17 +669,6 @@ export class FlashcardPanel {
630669
// Prepare back (including example Chinese translation)
631670
const back = card.back || {};
632671
633-
// Show morphemes on back
634-
const morphemesBackEl = document.getElementById('morphemes-back');
635-
if (card.front.morphemes && card.front.morphemes.length > 0) {
636-
morphemesBackEl.innerHTML = card.front.morphemes
637-
.map(m => '<span class="morpheme">' + m + '</span>')
638-
.join('<span class="separator">+</span>');
639-
morphemesBackEl.classList.remove('hidden');
640-
} else {
641-
morphemesBackEl.classList.add('hidden');
642-
}
643-
644672
document.getElementById('translation').textContent = back.translation || '(no translation)';
645673
646674
// Example Chinese goes to back
@@ -846,6 +874,11 @@ export class FlashcardPanel {
846874
document.getElementById('back-content').classList.remove('hidden');
847875
document.getElementById('back-buttons').classList.remove('hidden');
848876
877+
// Auto-pronounce when revealing back (if enabled)
878+
if (ttsSettings.autoPlay) {
879+
speak();
880+
}
881+
849882
vscode.postMessage({ type: 'reveal_back', cardId: currentCard.id });
850883
}
851884
@@ -861,6 +894,7 @@ export class FlashcardPanel {
861894
}
862895
863896
function refresh() {
897+
console.log('[WordSlash UI] Refresh button clicked');
864898
vscode.postMessage({ type: 'refresh' });
865899
}
866900

src/webview/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export function isValidUiMessage(msg: unknown): msg is UiToExtensionMessage {
105105
case 'start_flashcard_study':
106106
case 'open_settings':
107107
case 'get_tts_settings':
108+
case 'refresh':
108109
return true;
109110

110111
case 'get_knowledge_graph':

0 commit comments

Comments
 (0)