tags converted to highlight spans
# - Placeholder lines for translation
# - Font styling based on config
# - Dark mode CSS support
# Generate back side HTML (answer)
translation = "研究表明了一种相关性。"
original_html = "suggest: v. to put forward for consideration
"
back_html = get_processed_back_html(sentence, translation, original_html)
# Returns styled HTML with:
# - Full sentence and translation (with highlights)
# - Original card content in separate section
# - Responsive styling for different screen sizes
# Process highlight tags
text = "The important word here."
highlighted = process_highlight(text)
# Returns: 'The important word here.'
# Update all card templates after font config change
success = update_card_templates()
# Updates "ContextFlow例句翻译" note type templates
# Returns: True if successful, False if note type not found
```
--------------------------------
### Discover Available Models from API Provider (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Retrieves a list of available models from an API provider for dynamic model selection in a configuration UI. It requires the API URL and API key, returning a list of model names or an empty list if the request fails.
```python
from api_client import fetch_available_models
api_url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
api_key = "your-api-key"
models = fetch_available_models(api_url, api_key)
# Returns: ["deepseek-v3-250324", "doubao-seed-1-6-flash", "qwen3-max", ...]
if models:
print(f"Found {len(models)} available models:")
for model in models:
print(f" - {model}")
else:
print("No models found or API request failed")
```
--------------------------------
### Test API Connectivity and Credentials (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Tests API connectivity and validates credentials before configuration, supporting both thinking-enabled and standard completion modes. It checks for successful connection and returns either an error message or the content of a successful response.
```python
from api_client import test_api_sync
api_url = "https://api.deepseek.com/v1/chat/completions"
api_key = "your-api-key-here"
model_name = "deepseek-v3-250324"
# Test API connection with timeout
response_content, error_message = test_api_sync(
api_url=api_url,
api_key=api_key,
model_name=model_name,
timeout_seconds=30
)
if error_message:
print(f"API Test Failed: {error_message}")
# Example errors:
# "API错误 401: Invalid API key"
# "请求在 30 秒后超时。"
else:
print(f"API Test Succeeded: {response_content}")
# Expected output: "Hello" (model repeats the test word)
```
--------------------------------
### Background Task Queue Management (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Manages a priority-based background sentence generation queue using a thread pool. Supports both single-threaded and multi-threaded modes, and allows for reorganizing task priorities based on keywords or repopulating the cache.
```python
from main_logic import reorganize_task_queue, get_upcoming_cards
from anki.cards import Card
# Reorganize queue with new priority keyword
current_keyword = "dangerous"
reorganize_task_queue(current_keyword, is_repopulate=False)
# Adds keyword with priority 0 (highest) if not in cache
# Updates existing task priorities in queue
# Repopulate cache when exhausted (lowest priority)
reorganize_task_queue(current_keyword, is_repopulate=True)
# Adds with priority 999 (lowest) for background refill
# Get upcoming cards for preloading
card = Card(mw.col) # Current card
deck_name = "English::Vocabulary"
upcoming_keywords = get_upcoming_cards(card, deck_name)
# Returns: ["adventure", "dangerous", "cereal", ...]
# Fetches 100 cards for single-threaded, 10 for multi-threaded
# Filters out keywords already in cache
# Reorganize queue for batch preloading
reorganize_task_queue(upcoming_keywords)
# Sets priorities: position 1 = priority 1, position 2 = priority 2, etc.
```
--------------------------------
### JavaScript Data Filtering and Chart Initialization for Study Time Analysis
Source: https://github.com/yfftyhwsdj/contextflow/blob/main/templates/stats.html
This JavaScript code defines functions to filter study data based on a specified time range (e.g., month, year) and initialize Chart.js instances. It handles data slicing, finding the first non-zero data points, and setting up various chart types (bar and line) with common options for responsiveness and scales. It also includes error handling for Chart.js loading.
```javascript
let cardsChart, timeChart, avgTimeChart, totalCardsChart, totalTimeChart;
let allDates = {dates};
let allCardsData = {cards_data};
let allTimeData = {time_data};
let allAvgTimeData = {avg_time_data};
let allTotalCardsData = {total_cards_data};
let allTotalTimeData = {total_time_data};
function filterDataByRange(range) {
let days = 7;
if (range === 'month') days = 30;
if (range === 'year') days = Math.min(365, allDates.length);
if (range === '5year') days = Math.min(1825, allDates.length);
days = Math.min(days, allDates.length);
let startIndex = Math.max(0, allDates.length - days);
let filteredDates = allDates.slice(startIndex);
let filteredCards = allCardsData.slice(startIndex);
let filteredTime = allTimeData.slice(startIndex);
let filteredAvgTime = allAvgTimeData.slice(startIndex);
let filteredTotalCards = allTotalCardsData.slice(startIndex);
let filteredTotalTime = allTotalTimeData.slice(startIndex);
let firstNonZeroIndex = 0;
for (let i = 0; i < filteredCards.length; i++) {
if (filteredCards[i] > 0 || filteredTime[i] > 0) {
firstNonZeroIndex = i;
break;
}
if (i === filteredCards.length - 1) {
firstNonZeroIndex = i;
}
}
return {
dates: filteredDates.slice(firstNonZeroIndex),
cards: filteredCards.slice(firstNonZeroIndex),
time: filteredTime.slice(firstNonZeroIndex),
avgTime: filteredAvgTime.slice(firstNonZeroIndex),
totalCards: filteredTotalCards.slice(firstNonZeroIndex),
totalTime: filteredTotalTime.slice(firstNonZeroIndex)
};
}
function updateCharts(range) {
const filtered = filterDataByRange(range);
const charts = [
{ chart: cardsChart, data: filtered.cards },
{ chart: timeChart, data: filtered.time },
{ chart: avgTimeChart, data: filtered.avgTime },
{ chart: totalCardsChart, data: filtered.totalCards },
{ chart: totalTimeChart, data: filtered.totalTime }
];
charts.forEach(item => {
if (item.chart) {
item.chart.data.labels = filtered.dates;
item.chart.data.datasets[0].data = item.data;
item.chart.update();
}
});
}
function initCharts() {
const maxRetries = 10;
let retryCount = 0;
const loadingElements = document.querySelectorAll('.loading');
const canvasElements = document.querySelectorAll('canvas');
function tryInit() {
if (typeof Chart === 'undefined') {
if (retryCount < maxRetries) {
retryCount++;
setTimeout(tryInit, 100);
return;
}
loadingElements.forEach(el => {
el.textContent = '图表库加载失败,请检查网络连接。';
el.style.color = 'red';
});
return;
}
loadingElements.forEach(el => el.style.display = 'none');
canvasElements.forEach(cv => cv.style.display = 'block');
const defaultRange = document.querySelector('input[name="timeRange"]:checked').value;
const initialFilteredData = filterDataByRange(defaultRange);
const commonLineOptions = {
responsive: true,
maintainAspectRatio: false,
tension: 0.2,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
position: 'nearest',
intersect: false,
yAlign: 'top',
xAlign: 'center'
}
},
scales: {
x: { grid: { display: false } },
y: { beginAtZero: true, grid: { color: '#e9ecef' } }
}
};
const commonBarOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false }
},
scales: {
x: { grid: { display: false } },
y: { beginAtZero: true, grid: { color: '#e9ecef' } }
}
};
// --- 修改点 1: 每日学习卡片数改为柱状图 ---
cardsChart = new Chart(document.getElementById('cardsChart'), {
type: 'bar', // 修改 type 为 'bar'
data: {
labels: initialFilteredData.dates,
datasets: [{
data: initialFilteredData.cards,
backgroundColor: 'rgba(75, 192, 192, 0.7)', // 应用柱状图颜色
borderColor: 'rgb(75, 192, 192)',
borderWidth: 1,
borderRadius: 4,
}]
},
options: { ...commonBarOptions } // 应用柱状图通用配置
});
timeChart = new Chart(document.getElementById('timeChart'), {
type: 'bar',
data: {
labels: initialFilteredData.dates,
datasets: [{
data: initialFilteredData.time,
backgroundColor: 'rgba(54, 162, 235, 0.7)',
borderColor: 'rgb(54, 162, 235)',
borderWidth: 1,
borderRadius: 4,
}]
},
options: { ...commonBarOptions }
});
// --- 修改点 2: 卡片平均学习时间改为无填充折线图 ---
avgTimeChart = new Chart(document.getElementById('avgTimeChart'), {
type: 'line',
data: {
labels: initialFilteredData.dates,
datasets: [{
data: initialFilteredData.avgTime,
borderColor: 'rgb(255, 99, 132)',
fill: false, // 修改 fill 为 false
borderWidth: 2
}]
},
options: { ...commonLineOptions }
});
totalCardsChart = new Chart(document.getElementById('totalCardsChart'), {
type: 'line',
data: {
labels: initialFilteredData.dates,
datasets: [{
data: initialFilteredData.totalCards,
borderColor: 'rgb(153, 102, 255)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
fill: true,
borderWidth: 2
}]
},
options: { ...commonLineOptions }
});
totalTimeChart = new Chart(document.getElementById('totalTimeChart'), {
type: 'line',
data: {
labels: initialFilteredData.dates,
datasets: [{
data: initialFilteredData.totalTime,
borderColor: 'rgb(255, 159, 64)',
backgroundColor: 'rgba(255, 159, 64,
```
--------------------------------
### Anki Card Creation for Saved Sentences (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Facilitates the creation of new Anki cards for favorite sentences. It includes functions to validate sentence and translation data, check for the existence of a target deck (creating it if necessary), and then create the card with custom note types and styling.
```python
from anki_card_creator import (
create_sentence_card,
check_deck_exists,
get_available_decks,
validate_card_data
)
# Validate sentence data before creation
sentence = "The research suggests a new approach."
translation = "研究表明了一种新方法。"
is_valid, message = validate_card_data(sentence, translation)
if not is_valid:
print(f"Validation failed: {message}")
# Possible errors: "例句不能为空", "翻译不能为空"
# Check if target deck exists
deck_name = "Saved Sentences"
if not check_deck_exists(deck_name):
print(f"Deck '{deck_name}' does not exist, will be created")
# Create sentence card
success = create_sentence_card(
sentence=sentence,
translation=translation,
deck_name=deck_name
)
if success:
print("Card created successfully")
# Card will have:
# - Field 1: The research suggests a new approach.
# - Field 2: 研究表明了一种新方法。
# - Field 3: ContextFlow自动生成 - 2025-11-03 15:30
else:
print("Failed to create card")
# Get all available decks for UI selection
decks = get_available_decks()
# Returns: ["Default", "English::Vocabulary", "Saved Sentences", ...]
```
--------------------------------
### Anki Card Rendering Hook (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Main hook to intercept Anki card rendering. It displays AI-generated sentences on the question side and translations on the answer side. Extracts keywords from card fields, utilizes caching, and preloads sentences for upcoming cards.
```python
from main_logic import on_card_render, register_hooks
from anki.cards import Card
# Register all hooks (call once on addon initialization)
register_hooks()
# This registers:
# - card_will_show hook for card rendering
# - profile_will_close hook for cleanup
# - stats_dialog_will_show hook for statistics
# - context menu for right-click word lookup
# Card rendering is automatic, but internally works like this:
card = Card(mw.col) # Current review card
context = "question" # or "answer"
# On question side:
# 1. Extracts keyword from card's first field
# 2. Attempts to load cached sentence
# 3. If cache miss, queues generation task and shows "例句生成中..."
# 4. Displays generated sentence when ready
# 5. Preloads sentences for upcoming 10 cards
# On answer side:
# Displays: AI sentence + translation + original card content
```
--------------------------------
### Manage Sentence Cache with SQLite and In-Memory Layer (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Manages a sentence cache using an SQLite database and an in-memory caching layer for optimal performance. Supports loading, saving, atomic popping of sentences, and clearing the entire cache. Operations return success or failure indicators.
```python
from cache_manager import load_cache, save_cache, pop_cache, clear_cache
keyword = "adventure"
# Load cached sentences for a keyword
sentence_pairs = load_cache(keyword)
# Returns: [[sentence1, translation1], [sentence2, translation2], ...]
# Returns: [] if no cache exists
# Save new sentences (merges with existing cache)
new_sentences = [
["This is an exciting adventure.", "这是一次激动人心的冒险。"],
["The adventure begins tomorrow.", "冒险明天开始。"]
]
save_cache(keyword, new_sentences)
# Returns: True on success, False on failure
# Atomically pop first sentence from cache (for card display)
sentence_pair = pop_cache(keyword)
# Returns: ["This is an exciting adventure.", "这是一次激动人心的冒险。",]
# Returns: None if cache is empty
# Automatically removes keyword from cache when last sentence is used
# Clear all caches (database and memory)
success = clear_cache()
# Returns: True on success, displays confirmation dialog
```
--------------------------------
### Difficulty-Based Keyword Selection (Python)
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
Retrieves a list of the top 100 most difficult keywords, as determined by the FSRS algorithm. This is intended for use in prompt engineering, such as inserting a second keyword for more complex sentence generation.
```python
from api_client import get_top_difficulty_keywords
# Get most difficult keywords (FSRS difficulty >= 0.6)
difficult_keywords = get_top_difficulty_keywords()
# Returns: ["phenomenon", "correlation", "inevitable", ...]
# Sorted by FSRS difficulty (highest first)
```
--------------------------------
### Process Difficult Keywords with Python
Source: https://context7.com/yfftyhwsdj/contextflow/llms.txt
This Python code snippet processes a list of difficult keywords. It prints the count of found keywords and lists the top 10. If no difficult keywords are found (less than 100), it prints a corresponding message. This logic is used internally by the generate_ai_sentence() function.
```python
if difficult_keywords:
print(f"Found {len(difficult_keywords)} difficult keywords")
print(f"Top 10: {difficult_keywords[:10]}")
else:
print("No difficult keywords found (< 100 difficult cards)")
```
--------------------------------
### Initialize and Update Charts with Time Range Selection (JavaScript)
Source: https://github.com/yfftyhwsdj/contextflow/blob/main/templates/stats.html
Initializes charts with default settings and updates them based on user-selected time ranges. Handles the display of loading indicators and chart canvases. It listens for changes in radio button selections for time range.
```javascript
canvasElements.forEach(cv => cv.style.display = 'none');
loadingElements.forEach(el => el.style.display = 'flex');
tryInit();
document.querySelectorAll('input[name="timeRange"]').forEach(radio => {
radio.addEventListener('change', function() {
loadingElements.forEach(el => el.style.display = 'flex');
canvasElements.forEach(cv => cv.style.display = 'none');
setTimeout(() => {
updateCharts(this.value);
loadingElements.forEach(el => el.style.display = 'none');
canvasElements.forEach(cv => cv.style.display = 'block');
}, 10);
});
});
document.addEventListener('DOMContentLoaded', initCharts);
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.