feat(live): custom channel management with review fixes (#282)

* feat(live): custom channel management — add/remove/reorder, standalone window, i18n

- Standalone channel management window (?live-channels=1) with list, add form, restore defaults
- LIVE panel: gear icon opens channel management; channel tabs reorderable via DnD
- Row click to edit; custom modal for delete confirmation (no window.confirm)
- i18n for all locales (manage, addChannel, youtubeHandle, displayName, etc.)
- UI: margin between channel list and add form in management window
- settings-window: panel display settings comment in English

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(tauri): channel management in desktop app, dev base_url fix

- Add live-channels.html and live-channels-main.ts for standalone window
- Tauri: open_live_channels_window_command, close_live_channels_window, open live-channels window (WebviewUrl::App or External from base_url)
- LiveNewsPanel: in desktop runtime invoke Tauri command with base_url (window.location.origin) so dev works when Vite runs on a different port than devUrl
- Vite: add liveChannels entry to build input
- capabilities: add live-channels window
- tauri.conf: devUrl 3000 to match vite server.port
- docs: PR_LIVE_CHANNEL_MANAGEMENT.md for PR #276

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address review issues in live channel management PR

- Revert settings button to open modal (not window.open popup)
- Revert devUrl from localhost:3000 to localhost:5173
- Guard activeChannel against empty channels (fall back to defaults)
- Escape i18n strings in innerHTML with escapeHtml() to prevent XSS
- Only store displayNameOverrides for actually renamed channels
- Use URL constructor for live-channels window URL
- Add CSP meta tag to live-channels.html
- Remove unused i18n keys (edit, editMode, done) from all locales
- Remove unused CSS classes (live-news-manage-btn/panel/wrap)
- Delete PR instruction doc (PR_LIVE_CHANNEL_MANAGEMENT.md)

---------

Co-authored-by: Masaki <yukkurihakutaku@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Elie Habib
2026-02-23 22:51:44 +00:00
committed by GitHub
parent a37dced84e
commit 6271fafd40
28 changed files with 1321 additions and 69 deletions

View File

@@ -478,6 +478,24 @@ fn close_settings_window(app: AppHandle) -> Result<(), String> {
Ok(())
}
#[tauri::command]
async fn open_live_channels_window_command(
app: AppHandle,
base_url: Option<String>,
) -> Result<(), String> {
open_live_channels_window(&app, base_url)
}
#[tauri::command]
fn close_live_channels_window(app: AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("live-channels") {
window
.close()
.map_err(|e| format!("Failed to close live channels window: {e}"))?;
}
Ok(())
}
/// Fetch JSON from Polymarket Gamma API using native TLS (bypasses Cloudflare JA3 blocking).
/// Called from frontend when browser CORS and sidecar Node.js TLS both fail.
#[tauri::command]
@@ -533,6 +551,41 @@ fn open_settings_window(app: &AppHandle) -> Result<(), String> {
Ok(())
}
fn open_live_channels_window(app: &AppHandle, base_url: Option<String>) -> Result<(), String> {
if let Some(window) = app.get_webview_window("live-channels") {
let _ = window.show();
window
.set_focus()
.map_err(|e| format!("Failed to focus live channels window: {e}"))?;
return Ok(());
}
// In dev, use the same origin as the main window (e.g. http://localhost:3001) so we don't
// get "connection refused" when Vite runs on a different port than devUrl.
let url = match base_url {
Some(ref origin) if !origin.is_empty() => {
let path = origin.trim_end_matches('/');
let full_url = format!("{}/live-channels.html", path);
WebviewUrl::External(Url::parse(&full_url).map_err(|_| "Invalid base URL".to_string())?)
}
_ => WebviewUrl::App("live-channels.html".into()),
};
let _live_channels_window = WebviewWindowBuilder::new(app, "live-channels", url)
.title("Channel management - World Monitor")
.inner_size(440.0, 560.0)
.min_inner_size(360.0, 480.0)
.resizable(true)
.background_color(tauri::webview::Color(26, 28, 30, 255))
.build()
.map_err(|e| format!("Failed to create live channels window: {e}"))?;
#[cfg(not(target_os = "macos"))]
let _ = _live_channels_window.remove_menu();
Ok(())
}
fn build_app_menu(handle: &AppHandle) -> tauri::Result<Menu<tauri::Wry>> {
let settings_item = MenuItem::with_id(
handle,
@@ -986,6 +1039,8 @@ fn main() {
open_sidecar_log_file,
open_settings_window_command,
close_settings_window,
open_live_channels_window_command,
close_live_channels_window,
open_url,
fetch_polymarket
])