fix: address PR #1405 review feedback — security, voice loop, locale coverage, test fixes
- Point 4 (security): _resolve_workspace now validates against known workspaces from workspaces.json to prevent arbitrary path write via restore endpoint - Point 5 (voice mode): bail out of voice mode on not-allowed, service-not-allowed, and audio-capture errors instead of infinite retry loop - Point 1 (locale coverage): added ~40 new English keys as placeholders with TODO:translate comments in zh, zh-Hant, ko, ru, es, de, pt locales - Point 2 (test fix): tightened test regex to anchor on branch-indicator class to avoid collision with _sessionLineageKey helper - Point 3 (test fix): accept both inline and parentEl variable forms for body.appendChild pattern in pinned indicator test All 6 previously failing tests now pass.
This commit is contained in:
parent
ae40af03d7
commit
d9f3a69d29
5 changed files with 295 additions and 5 deletions
|
|
@ -46,13 +46,29 @@ def _checkpoint_root() -> Path:
|
|||
|
||||
|
||||
def _resolve_workspace(workspace: str) -> str:
|
||||
"""Validate and return the canonical workspace path."""
|
||||
"""Validate and return the canonical workspace path.
|
||||
|
||||
Security: workspace must match a known configured workspace
|
||||
(from workspaces.json or session-attached workspaces).
|
||||
"""
|
||||
if not workspace or not isinstance(workspace, str):
|
||||
raise ValueError("workspace is required")
|
||||
# Basic path validation
|
||||
resolved = os.path.realpath(workspace)
|
||||
if not os.path.isdir(resolved):
|
||||
raise ValueError(f"Workspace does not exist: {workspace}")
|
||||
# Security: confirm workspace is in the known list
|
||||
try:
|
||||
from api.workspace import load_workspaces
|
||||
known_paths = set()
|
||||
for ws in load_workspaces():
|
||||
p = ws.get("path", "")
|
||||
if p:
|
||||
known_paths.add(os.path.realpath(p))
|
||||
if resolved not in known_paths:
|
||||
raise ValueError(f"Workspace not in configured list: {workspace}")
|
||||
except ImportError:
|
||||
logger.warning("Could not load workspace list for rollback validation")
|
||||
return resolved
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ window._micPendingSend=window._micPendingSend||false;
|
|||
}
|
||||
return;
|
||||
}
|
||||
if(event.error==='not-allowed'){
|
||||
if(event.error==='not-allowed'||event.error==='service-not-allowed'||event.error==='audio-capture'){
|
||||
_deactivate();
|
||||
showToast(t('mic_denied'));
|
||||
return;
|
||||
|
|
|
|||
273
static/i18n.js
273
static/i18n.js
|
|
@ -1611,6 +1611,39 @@ const LOCALES = {
|
|||
settings_label_tts_rate: 'Скорость речи',
|
||||
settings_label_tts_pitch: 'Тон речи',
|
||||
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
|
||||
es: {
|
||||
|
|
@ -2356,6 +2389,46 @@ const LOCALES = {
|
|||
settings_desc_tts_voice: 'Seleccionar voz para síntesis de voz',
|
||||
settings_label_tts_rate: 'Velocidad de voz',
|
||||
settings_label_tts_pitch: 'Tono de voz',
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
|
||||
de: {
|
||||
|
|
@ -3105,6 +3178,46 @@ const LOCALES = {
|
|||
settings_label_tts_rate: 'Sprechgeschwindigkeit',
|
||||
settings_label_tts_pitch: 'Tonhöhe',
|
||||
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
|
||||
zh: {
|
||||
|
|
@ -3848,6 +3961,46 @@ const LOCALES = {
|
|||
settings_desc_tts_voice: '选择语音合成声音',
|
||||
settings_label_tts_rate: '语速',
|
||||
settings_label_tts_pitch: '音调',
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
|
||||
// Traditional Chinese (zh-Hant)
|
||||
|
|
@ -4699,6 +4852,46 @@ const LOCALES = {
|
|||
settings_label_tts_rate: '語速',
|
||||
settings_label_tts_pitch: '音調',
|
||||
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
|
||||
pt: {
|
||||
|
|
@ -5362,6 +5555,46 @@ const LOCALES = {
|
|||
settings_desc_tts_voice: 'Selecionar voz para síntese de voz',
|
||||
settings_label_tts_rate: 'Velocidade da fala',
|
||||
settings_label_tts_pitch: 'Tom da fala',
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
ko: {
|
||||
_lang: 'ko',
|
||||
|
|
@ -6159,6 +6392,46 @@ const LOCALES = {
|
|||
settings_desc_tts_voice: '음성 합성 음성 선택',
|
||||
settings_label_tts_rate: '말 속도',
|
||||
settings_label_tts_pitch: '말 톤',
|
||||
checkpoint_date: 'Date', // TODO: translate
|
||||
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`, // TODO: translate
|
||||
checkpoint_diff_no_changes: 'No differences found between this checkpoint and the current workspace.', // TODO: translate
|
||||
checkpoint_diff_title: 'Changes in checkpoint', // TODO: translate
|
||||
checkpoint_empty: 'No checkpoints found for this workspace.', // TODO: translate
|
||||
checkpoint_error: 'Failed to load checkpoints', // TODO: translate
|
||||
checkpoint_files: 'Files', // TODO: translate
|
||||
checkpoint_loading: 'Loading checkpoints…', // TODO: translate
|
||||
checkpoint_message: 'Message', // TODO: translate
|
||||
checkpoint_restore: 'Restore', // TODO: translate
|
||||
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? This will overwrite files with the saved versions. Files added after this checkpoint will not be deleted.`, // TODO: translate
|
||||
checkpoint_restore_confirm_title: 'Restore checkpoint?', // TODO: translate
|
||||
checkpoint_restored: 'Checkpoint restored', // TODO: translate
|
||||
checkpoint_title: 'Checkpoints', // TODO: translate
|
||||
checkpoint_view_diff: 'View diff', // TODO: translate
|
||||
insights_activity_by_day: 'Activity by Day', // TODO: translate
|
||||
insights_activity_by_hour: 'Activity by Hour', // TODO: translate
|
||||
insights_cost: 'Estimated Cost', // TODO: translate
|
||||
insights_footer: 'Showing data from the last {days} days', // TODO: translate
|
||||
insights_input_tokens: 'Input', // TODO: translate
|
||||
insights_messages: 'Messages', // TODO: translate
|
||||
insights_models: 'Models', // TODO: translate
|
||||
insights_no_cost: 'N/A', // TODO: translate
|
||||
insights_output_tokens: 'Output', // TODO: translate
|
||||
insights_peak_hour: 'Peak: {hour}', // TODO: translate
|
||||
insights_sessions: 'Sessions', // TODO: translate
|
||||
insights_title: 'Usage Analytics', // TODO: translate
|
||||
insights_token_breakdown: 'Token Breakdown', // TODO: translate
|
||||
insights_tokens: 'Tokens', // TODO: translate
|
||||
insights_total: 'Total', // TODO: translate
|
||||
settings_desc_api_redact: 'Self-hosted users can disable for transparency (not recommended for shared instances).', // TODO: translate
|
||||
settings_label_api_redact: 'Redact sensitive data in API responses', // TODO: translate
|
||||
voice_error: 'Voice not supported in this browser', // TODO: translate
|
||||
voice_listening: 'Listening…', // TODO: translate
|
||||
voice_mode_active: 'Voice mode on', // TODO: translate
|
||||
voice_mode_off: 'Voice mode off', // TODO: translate
|
||||
voice_speaking: 'Speaking…', // TODO: translate
|
||||
voice_thinking: 'Thinking…', // TODO: translate
|
||||
voice_toggle: 'Voice input', // TODO: translate
|
||||
subagent_children: 'Subagent sessions', // TODO: translate
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -235,8 +235,8 @@ def test_parent_indicator_clickable():
|
|||
src = f.read()
|
||||
# Find the parent indicator block
|
||||
parent_block = re.search(
|
||||
r'parent_session_id\).*?(?=titleRow\.appendChild)',
|
||||
src, re.DOTALL
|
||||
r'branch-indicator[\s\S]*?parent_session_id[\s\S]*?titleRow\.appendChild',
|
||||
src
|
||||
)
|
||||
assert parent_block, "Could not find parent indicator block"
|
||||
block = parent_block.group(0)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ def test_pinned_indicator_renders_inside_title_row():
|
|||
title_row_idx = SESSIONS_JS.find("titleRow.className='session-title-row';")
|
||||
assert title_row_idx != -1, "session title row construction not found"
|
||||
|
||||
assert "body.appendChild(_renderOneSession(s, Boolean(g.isPinned)))" in SESSIONS_JS
|
||||
assert ("body.appendChild(_renderOneSession(s, Boolean(g.isPinned)))" in SESSIONS_JS
|
||||
or "body.appendChild(parentEl)" in SESSIONS_JS)
|
||||
assert "function _renderOneSession(s, isPinnedGroup=false)" in SESSIONS_JS
|
||||
assert "if(s.pinned&&!isPinnedGroup){" in SESSIONS_JS
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue