From d74e56f35b1dab89ba714c8f092cd916761b8089 Mon Sep 17 00:00:00 2001 From: wgqqqqq Date: Mon, 15 Jun 2026 15:01:51 +0800 Subject: [PATCH 001/100] fix: keep mcp elicitation capability legacy-compatible --- .../services-integrations/src/mcp/protocol/client_info.rs | 1 - .../services/services-integrations/tests/mcp_contracts.rs | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/crates/services/services-integrations/src/mcp/protocol/client_info.rs b/src/crates/services/services-integrations/src/mcp/protocol/client_info.rs index cceb40bc28..157ca7d3c1 100644 --- a/src/crates/services/services-integrations/src/mcp/protocol/client_info.rs +++ b/src/crates/services/services-integrations/src/mcp/protocol/client_info.rs @@ -11,7 +11,6 @@ pub fn create_mcp_client_info( .enable_roots() .enable_sampling() .enable_elicitation() - .enable_elicitation_schema_validation() .build(), Implementation::new(client_name, client_version), ) diff --git a/src/crates/services/services-integrations/tests/mcp_contracts.rs b/src/crates/services/services-integrations/tests/mcp_contracts.rs index e581c5ab31..453cdef22f 100644 --- a/src/crates/services/services-integrations/tests/mcp_contracts.rs +++ b/src/crates/services/services-integrations/tests/mcp_contracts.rs @@ -191,11 +191,8 @@ fn mcp_remote_client_info_declares_supported_client_capabilities() { assert!(info.capabilities.sampling.is_some()); assert!(info.capabilities.elicitation.is_some()); assert_eq!( - info.capabilities - .elicitation - .as_ref() - .and_then(|cap| cap.schema_validation), - Some(true) + serde_json::to_value(&info.capabilities.elicitation).unwrap(), + serde_json::json!({}) ); } From 419575dde4cea5ff7a885706e4978d36b0e310c9 Mon Sep 17 00:00:00 2001 From: limityan Date: Mon, 15 Jun 2026 16:11:12 +0800 Subject: [PATCH 002/100] perf(startup): reduce startup IPC and trace attribution --- src/apps/desktop/src/api/commands.rs | 140 ++++++++++++++++-- src/apps/desktop/src/lib.rs | 1 + src/apps/desktop/src/theme.rs | 96 +++++++----- src/web-ui/src/app/layout/AppLayout.tsx | 2 +- .../startupPerformanceContract.test.ts | 25 ++++ .../ssh-remote/SSHRemoteProvider.test.tsx | 128 ++++++++++++++++ .../features/ssh-remote/SSHRemoteProvider.tsx | 17 ++- .../api/adapters/tauri-adapter.test.ts | 21 +++ .../api/adapters/tauri-adapter.ts | 23 ++- .../api/service-api/GlobalAPI.ts | 26 ++++ .../config/services/ConfigManager.test.ts | 131 ++++++++++++++++ .../config/services/ConfigManager.ts | 93 +++++++++++- .../src/infrastructure/config/types/index.ts | 1 + .../business/workspaceManager.test.ts | 45 +++++- .../services/business/workspaceManager.ts | 49 +++++- src/web-ui/src/main.tsx | 19 +-- src/web-ui/src/shared/types/global-state.ts | 52 +++++++ tests/e2e/helpers/performance-trace.ts | 50 ++++++- 18 files changed, 828 insertions(+), 91 deletions(-) create mode 100644 src/web-ui/src/features/ssh-remote/SSHRemoteProvider.test.tsx diff --git a/src/apps/desktop/src/api/commands.rs b/src/apps/desktop/src/api/commands.rs index 49e45e245f..e11a10b5ad 100644 --- a/src/apps/desktop/src/api/commands.rs +++ b/src/apps/desktop/src/api/commands.rs @@ -35,6 +35,23 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use tauri::{AppHandle, Emitter, State}; +struct WorkspaceStateSnapshot { + current_workspace: Option, + recent_workspaces: Vec, + opened_workspaces: Vec, + legacy_remote_workspace: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WorkspaceStartupStateSnapshotDto { + pub cleanup_removed_count: usize, + pub current_workspace: Option, + pub recent_workspaces: Vec, + pub opened_workspaces: Vec, + pub legacy_remote_workspace: Option, +} + fn remote_workspace_from_info(info: &WorkspaceInfo) -> Option { if info.workspace_kind != WorkspaceKind::Remote { return None; @@ -1949,6 +1966,34 @@ pub async fn get_recent_workspaces( result } +async fn collect_workspace_state_snapshot(state: &State<'_, AppState>) -> WorkspaceStateSnapshot { + let workspace_service = &state.workspace_service; + let current_workspace = workspace_service + .get_current_workspace() + .await + .map(|info| WorkspaceInfoDto::from_workspace_info(&info)); + let recent_workspaces = workspace_service + .get_recent_workspaces() + .await + .into_iter() + .map(|info| WorkspaceInfoDto::from_workspace_info(&info)) + .collect(); + let opened_workspaces = workspace_service + .get_opened_workspaces() + .await + .into_iter() + .map(|info| WorkspaceInfoDto::from_workspace_info(&info)) + .collect(); + let legacy_remote_workspace = state.get_remote_workspace_async().await; + + WorkspaceStateSnapshot { + current_workspace, + recent_workspaces, + opened_workspaces, + legacy_remote_workspace, + } +} + #[tauri::command] pub async fn remove_recent_workspace( state: State<'_, AppState>, @@ -1968,19 +2013,88 @@ pub async fn cleanup_invalid_workspaces( startup_trace: State<'_, DesktopStartupTrace>, ) -> Result { let trace_started = Instant::now(); + cleanup_invalid_workspaces_impl( + &state, + &app, + &startup_trace, + "cleanup_invalid_workspaces", + Some("cleanup_invalid_workspaces"), + trace_started, + ) + .await +} + +#[tauri::command] +pub async fn cleanup_invalid_workspaces_and_get_workspace_state_snapshot( + state: State<'_, AppState>, + app: tauri::AppHandle, + startup_trace: State<'_, DesktopStartupTrace>, +) -> Result { + let trace_started = Instant::now(); + let cleanup_removed_count = match cleanup_invalid_workspaces_impl( + &state, + &app, + &startup_trace, + "cleanup_invalid_workspaces_and_get_workspace_state_snapshot", + None, + trace_started, + ) + .await + { + Ok(removed_count) => removed_count, + Err(error) => { + startup_trace.record_tauri_command_elapsed( + "cleanup_invalid_workspaces_and_get_workspace_state_snapshot", + None, + trace_started, + ); + return Err(error); + } + }; + + let snapshot_started = Instant::now(); + let snapshot = collect_workspace_state_snapshot(&state).await; + startup_trace.record_elapsed_step( + "tauri_command", + "cleanup_invalid_workspaces_and_get_workspace_state_snapshot.collect_workspace_state_snapshot", + snapshot_started, + ); + startup_trace.record_tauri_command_elapsed( + "cleanup_invalid_workspaces_and_get_workspace_state_snapshot", + None, + trace_started, + ); + + Ok(WorkspaceStartupStateSnapshotDto { + cleanup_removed_count, + current_workspace: snapshot.current_workspace, + recent_workspaces: snapshot.recent_workspaces, + opened_workspaces: snapshot.opened_workspaces, + legacy_remote_workspace: snapshot.legacy_remote_workspace, + }) +} + +async fn cleanup_invalid_workspaces_impl( + state: &State<'_, AppState>, + app: &tauri::AppHandle, + startup_trace: &State<'_, DesktopStartupTrace>, + trace_step_prefix: &str, + command_name: Option<&str>, + command_started: Instant, +) -> Result { let cleanup_started = Instant::now(); match state.workspace_service.cleanup_invalid_workspaces().await { Ok(local_removed_count) => { startup_trace.record_elapsed_step( "tauri_command", - "cleanup_invalid_workspaces.local_workspace_cleanup", + format!("{trace_step_prefix}.local_workspace_cleanup"), cleanup_started, ); let prune_remote_started = Instant::now(); - let remote_removed_count = prune_unrecoverable_remote_workspaces(&state).await; + let remote_removed_count = prune_unrecoverable_remote_workspaces(state).await; startup_trace.record_elapsed_step( "tauri_command", - "cleanup_invalid_workspaces.remote_workspace_prune", + format!("{trace_step_prefix}.remote_workspace_prune"), prune_remote_started, ); let removed_count = local_removed_count + remote_removed_count; @@ -1993,7 +2107,7 @@ pub async fn cleanup_invalid_workspaces( } startup_trace.record_elapsed_step( "tauri_command", - "cleanup_invalid_workspaces.apply_active_workspace_context", + format!("{trace_step_prefix}.apply_active_workspace_context"), apply_context_started, ); @@ -2010,7 +2124,7 @@ pub async fn cleanup_invalid_workspaces( } startup_trace.record_elapsed_step( "tauri_command", - "cleanup_invalid_workspaces.sync_identity_watchers", + format!("{trace_step_prefix}.sync_identity_watchers"), sync_watchers_started, ); @@ -2018,20 +2132,16 @@ pub async fn cleanup_invalid_workspaces( "Invalid workspaces cleaned up: removed_count={}", removed_count ); - startup_trace.record_tauri_command_elapsed( - "cleanup_invalid_workspaces", - None, - trace_started, - ); + if let Some(command_name) = command_name { + startup_trace.record_tauri_command_elapsed(command_name, None, command_started); + } Ok(removed_count) } Err(e) => { error!("Failed to cleanup invalid workspaces: {}", e); - startup_trace.record_tauri_command_elapsed( - "cleanup_invalid_workspaces", - None, - trace_started, - ); + if let Some(command_name) = command_name { + startup_trace.record_tauri_command_elapsed(command_name, None, command_started); + } Err(format!("Failed to cleanup invalid workspaces: {}", e)) } } diff --git a/src/apps/desktop/src/lib.rs b/src/apps/desktop/src/lib.rs index abbc6282d3..99bfd13235 100644 --- a/src/apps/desktop/src/lib.rs +++ b/src/apps/desktop/src/lib.rs @@ -1068,6 +1068,7 @@ pub async fn run() { subscribe_config_updates, get_model_configs, get_recent_workspaces, + cleanup_invalid_workspaces_and_get_workspace_state_snapshot, remove_recent_workspace, cleanup_invalid_workspaces, get_opened_workspaces, diff --git a/src/apps/desktop/src/theme.rs b/src/apps/desktop/src/theme.rs index 27ecfedebb..1c221e4969 100644 --- a/src/apps/desktop/src/theme.rs +++ b/src/apps/desktop/src/theme.rs @@ -209,6 +209,15 @@ pub struct ThemeConfig { pub accent_color: String, } +#[derive(Debug, Clone)] +struct StartupBootstrapConfig { + theme: ThemeConfig, + locale: String, + keybindings: Option, +} + +const MAX_BOOTSTRAP_KEYBINDINGS_JSON_BYTES: usize = 64 * 1024; + impl Default for ThemeConfig { fn default() -> Self { let mut theme = Self::get_builtin_theme("bitfun-light").unwrap_or_else(|| Self { @@ -322,9 +331,13 @@ impl ThemeConfig { } } - pub fn load_from_config() -> Self { - let default = Self::default(); - + fn load_startup_bootstrap_config() -> StartupBootstrapConfig { + let default_theme = Self::default(); + let default = StartupBootstrapConfig { + theme: default_theme.clone(), + locale: "zh-CN".to_string(), + keybindings: None, + }; let path_manager = match try_get_path_manager_arc() { Ok(pm) => pm, Err(e) => { @@ -346,14 +359,33 @@ impl ThemeConfig { } }; - let global_config: GlobalConfig = match serde_json::from_str(&config_content) { - Ok(config) => config, + let config_value: serde_json::Value = match serde_json::from_str(&config_content) { + Ok(value) => value, Err(e) => { debug!("Failed to parse config file, using default theme: {}", e); return default; } }; + let locale = config_value + .pointer("/app/language") + .and_then(|value| value.as_str()) + .or_else(|| { + config_value + .pointer("/i18n/currentLanguage") + .and_then(|value| value.as_str()) + }) + .unwrap_or("zh-CN") + .to_string(); + + let global_config: GlobalConfig = match serde_json::from_value(config_value) { + Ok(config) => config, + Err(e) => { + debug!("Failed to parse config file, using default theme: {}", e); + return StartupBootstrapConfig { locale, ..default }; + } + }; + let theme_id = global_config .themes .as_ref() @@ -362,15 +394,21 @@ impl ThemeConfig { let resolved_id = Self::resolve_builtin_theme_id(theme_id); - match Self::get_builtin_theme(resolved_id) { + let theme = match Self::get_builtin_theme(resolved_id) { Some(mut config) => { config.selection_id = Some(theme_id.to_string()); config } None => { warn!("Unknown theme ID: {}, using default theme", theme_id); - default + default_theme } + }; + + StartupBootstrapConfig { + theme, + locale, + keybindings: global_config.app.keybindings, } } @@ -386,30 +424,6 @@ impl ThemeConfig { theme_id } - fn load_startup_locale_from_config() -> String { - let path_manager = match try_get_path_manager_arc() { - Ok(pm) => pm, - Err(_) => return "zh-CN".to_string(), - }; - let config_file = path_manager.app_config_file(); - let Ok(config_content) = std::fs::read_to_string(config_file) else { - return "zh-CN".to_string(); - }; - let Ok(config_value) = serde_json::from_str::(&config_content) else { - return "zh-CN".to_string(); - }; - config_value - .pointer("/app/language") - .and_then(|value| value.as_str()) - .or_else(|| { - config_value - .pointer("/i18n/currentLanguage") - .and_then(|value| value.as_str()) - }) - .unwrap_or("zh-CN") - .to_string() - } - fn startup_messages_json(locale: &str) -> String { let messages = match locale { "en-US" | "en" => serde_json::json!({ @@ -437,9 +451,13 @@ impl ThemeConfig { messages.to_string() } - pub fn generate_init_script(&self, startup_trace_id: &str) -> String { + fn generate_init_script( + &self, + startup_trace_id: &str, + bootstrap_config: &StartupBootstrapConfig, + ) -> String { let theme_type = if self.is_light { "light" } else { "dark" }; - let startup_locale = Self::load_startup_locale_from_config(); + let startup_locale = &bootstrap_config.locale; let startup_locale_json = serde_json::to_string(&startup_locale).unwrap_or_else(|_| "\"zh-CN\"".to_string()); let startup_messages_json = Self::startup_messages_json(&startup_locale); @@ -460,6 +478,11 @@ impl ThemeConfig { .as_ref() .and_then(|selection| serde_json::to_string(selection).ok()) .unwrap_or_else(|| "null".to_string()); + let bootstrap_keybindings_assignment = serde_json::to_string(&bootstrap_config.keybindings) + .ok() + .filter(|json| json.len() <= MAX_BOOTSTRAP_KEYBINDINGS_JSON_BYTES) + .map(|json| format!("window.__BITFUN_BOOTSTRAP_KEYBINDINGS__ = {json};")) + .unwrap_or_default(); format!( r#" @@ -472,6 +495,7 @@ impl ThemeConfig { window.__BITFUN_SHOW_STARTUP_WINDOW_CONTROLS__ = {show_startup_window_controls}; window.__BITFUN_BOOTSTRAP_THEME_ID__ = {bootstrap_theme_id_json}; window.__BITFUN_BOOTSTRAP_THEME_SELECTION__ = {bootstrap_theme_selection_json}; + {bootstrap_keybindings_assignment} function applyTheme() {{ var root = document.documentElement; if (!root) return false; @@ -520,6 +544,7 @@ impl ThemeConfig { startup_locale_json = startup_locale_json, startup_messages_json = startup_messages_json, show_startup_window_controls = show_startup_window_controls, + bootstrap_keybindings_assignment = bootstrap_keybindings_assignment, ) } @@ -538,9 +563,10 @@ pub fn create_main_window( startup_trace: &DesktopStartupTrace, ) { let total_started_at = Instant::now(); - let theme = ThemeConfig::load_from_config(); + let bootstrap_config = ThemeConfig::load_startup_bootstrap_config(); + let theme = bootstrap_config.theme.clone(); let bg_color = theme.to_tauri_color(); - let init_script = theme.generate_init_script(startup_trace_id); + let init_script = theme.generate_init_script(startup_trace_id, &bootstrap_config); startup_trace.record_step( "native_step_end", "native_window", diff --git a/src/web-ui/src/app/layout/AppLayout.tsx b/src/web-ui/src/app/layout/AppLayout.tsx index cbf7775236..e7d387e619 100644 --- a/src/web-ui/src/app/layout/AppLayout.tsx +++ b/src/web-ui/src/app/layout/AppLayout.tsx @@ -137,7 +137,7 @@ const AppLayout: React.FC = ({ className = '' }) => { useEffect(() => { const load = async () => { try { - const raw = await configManager.getConfig('app.keybindings'); + const raw = await configManager.getOptionalConfig('app.keybindings'); const overrides = parseStoredKeybindings(raw); if (Object.keys(overrides).length > 0) { shortcutManager.loadUserOverrides(overrides); diff --git a/src/web-ui/src/app/startup/startupPerformanceContract.test.ts b/src/web-ui/src/app/startup/startupPerformanceContract.test.ts index e85491cdd2..a20c0a638d 100644 --- a/src/web-ui/src/app/startup/startupPerformanceContract.test.ts +++ b/src/web-ui/src/app/startup/startupPerformanceContract.test.ts @@ -53,6 +53,17 @@ describe('startup performance contract', () => { expect(source).toContain("import('./shared/context-menu-system')"); }); + it('keeps the required i18n provider off an async startup waterfall', () => { + const source = readSource('../../main.tsx'); + + expect(source).toContain( + 'import { I18nProvider } from "./infrastructure/i18n/providers/I18nProvider"' + ); + expect(source).not.toMatch(/import\(['"]\.\/infrastructure\/i18n['"]\)/); + expect(source).toContain("step: 'load_i18n_provider'"); + expect(source).toContain("mode: 'static'"); + }); + it('does not block first React render on frontend log-level config reads', () => { const mainSource = readSource('../../main.tsx'); const loggerSource = readSource('../../shared/utils/logger.ts'); @@ -66,6 +77,20 @@ describe('startup performance contract', () => { expect(themeSource).toContain('__BITFUN_BOOTSTRAP_LOG_LEVEL__'); }); + it('keeps startup keybindings on the bootstrap path instead of a first-window IPC', () => { + const configManagerSource = readSource('../../infrastructure/config/services/ConfigManager.ts'); + const themeSource = readSource('../../../../apps/desktop/src/theme.rs'); + + expect(themeSource).toContain('__BITFUN_BOOTSTRAP_KEYBINDINGS__'); + expect(themeSource).toContain('keybindings: global_config.app.keybindings'); + expect(themeSource).toContain('MAX_BOOTSTRAP_KEYBINDINGS_JSON_BYTES'); + expect(themeSource).toContain('.filter(|json| json.len() <= MAX_BOOTSTRAP_KEYBINDINGS_JSON_BYTES)'); + expect(configManagerSource).toContain('consumeBootstrapOptionalConfig'); + expect(configManagerSource).toContain('__BITFUN_BOOTSTRAP_KEYBINDINGS__'); + expect(configManagerSource).toContain("path !== 'app.keybindings'"); + expect(configManagerSource).toContain('delete globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__'); + }); + it('keeps built-in theme startup on the bootstrap path without pre-render config writes', () => { const mainSource = readSource('../../main.tsx'); const themeServiceSource = readSource('../../infrastructure/theme/core/ThemeService.ts'); diff --git a/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.test.tsx b/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.test.tsx new file mode 100644 index 0000000000..d0421bf66f --- /dev/null +++ b/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.test.tsx @@ -0,0 +1,128 @@ +// @vitest-environment jsdom + +import React, { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { SSHRemoteProvider } from './SSHRemoteProvider'; + +globalThis.IS_REACT_ACT_ENVIRONMENT = true; + +const workspaceManagerMock = vi.hoisted(() => ({ + getState: vi.fn(), + addEventListener: vi.fn(), + consumeStartupLegacyRemoteWorkspaceSnapshot: vi.fn(), + openRemoteWorkspace: vi.fn(), + removeRemoteWorkspace: vi.fn(), +})); + +const sshApiMock = vi.hoisted(() => ({ + getWorkspaceInfo: vi.fn(), + listSavedConnections: vi.fn(), + hasStoredPassword: vi.fn(), + isConnected: vi.fn(), + openWorkspace: vi.fn(), + disconnect: vi.fn(), + closeWorkspace: vi.fn(), + removeWorkspace: vi.fn(), +})); + +vi.mock('@/infrastructure/services/business/workspaceManager', () => ({ + workspaceManager: workspaceManagerMock, +})); + +vi.mock('./sshApi', () => ({ + sshApi: sshApiMock, +})); + +vi.mock('@/flow_chat/store/FlowChatStore', () => ({ + flowChatStore: { + initializeFromDisk: vi.fn(() => Promise.resolve()), + }, +})); + +vi.mock('@/infrastructure/api/service-api/ACPClientAPI', () => ({ + ACPClientAPI: { + probeClientRequirements: vi.fn(() => Promise.resolve()), + }, +})); + +vi.mock('@/shared/notification-system', () => ({ + notificationService: { + warning: vi.fn(), + error: vi.fn(), + success: vi.fn(), + }, +})); + +vi.mock('@/shared/utils/logger', () => ({ + createLogger: () => ({ + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }), +})); + +describe('SSHRemoteProvider startup restore', () => { + let container: HTMLDivElement; + let root: Root; + + beforeEach(() => { + vi.clearAllMocks(); + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + workspaceManagerMock.getState.mockReturnValue({ + loading: false, + openedWorkspaces: new Map(), + activeWorkspaceId: null, + }); + workspaceManagerMock.addEventListener.mockReturnValue(() => undefined); + sshApiMock.getWorkspaceInfo.mockResolvedValue(null); + sshApiMock.listSavedConnections.mockResolvedValue([]); + }); + + afterEach(() => { + act(() => { + root.unmount(); + }); + container.remove(); + }); + + async function renderProvider(): Promise { + await act(async () => { + root.render( + +
+ + ); + }); + await act(async () => { + await Promise.resolve(); + }); + } + + it('skips the legacy remote IPC when the startup snapshot is available', async () => { + workspaceManagerMock.consumeStartupLegacyRemoteWorkspaceSnapshot.mockReturnValue({ + available: true, + workspace: null, + }); + + await renderProvider(); + + expect(workspaceManagerMock.consumeStartupLegacyRemoteWorkspaceSnapshot).toHaveBeenCalledTimes(1); + expect(sshApiMock.getWorkspaceInfo).not.toHaveBeenCalled(); + }); + + it('falls back to the legacy remote IPC when no startup snapshot is available', async () => { + workspaceManagerMock.consumeStartupLegacyRemoteWorkspaceSnapshot.mockReturnValue({ + available: false, + workspace: null, + }); + + await renderProvider(); + + expect(sshApiMock.getWorkspaceInfo).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.tsx b/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.tsx index 599635952a..c4f8c4bf86 100644 --- a/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.tsx +++ b/src/web-ui/src/features/ssh-remote/SSHRemoteProvider.tsx @@ -395,12 +395,19 @@ export const SSHRemoteProvider: React.FC = ({ children } ws => ws.workspaceKind === WorkspaceKind.Remote && ws.connectionId ); - // Also check legacy single-workspace persisted in app_state + // Also check legacy single-workspace persisted in app_state. + // Startup normally receives this in the workspace snapshot, so avoid a + // second early invoke unless the snapshot was unavailable. let legacyWorkspace: RemoteWorkspace | null = null; - try { - legacyWorkspace = await sshApi.getWorkspaceInfo(); - } catch { - // Ignore + const startupLegacyWorkspace = workspaceManager.consumeStartupLegacyRemoteWorkspaceSnapshot(); + if (startupLegacyWorkspace.available) { + legacyWorkspace = startupLegacyWorkspace.workspace; + } else { + try { + legacyWorkspace = await sshApi.getWorkspaceInfo(); + } catch { + // Ignore + } } // Key by connection + path so two servers at the same remote path stay distinct. diff --git a/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.test.ts b/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.test.ts index 935c15d83c..0aceaf4f26 100644 --- a/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.test.ts +++ b/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.test.ts @@ -61,4 +61,25 @@ describe('Tauri adapter expected errors', () => { expect(timing.invokeDurationMs).toEqual(expect.any(Number)); expect(timing.transportDurationMs).toEqual(expect.any(Number)); }); + + it('records invoke timing when a request rejects', async () => { + invokeMock.mockRejectedValueOnce(new Error("Config path not found: 'font'")); + const adapter = new TauriTransportAdapter(); + const timing: { + adapterInitDurationMs?: number; + invokeDurationMs?: number; + transportDurationMs?: number; + } = {}; + + await expect(adapter.request('get_config', { + request: { + path: 'font', + skipRetryOnNotFound: true, + }, + }, timing)).rejects.toThrow("Config path not found: 'font'"); + + expect(timing.adapterInitDurationMs).toEqual(expect.any(Number)); + expect(timing.invokeDurationMs).toEqual(expect.any(Number)); + expect(timing.transportDurationMs).toEqual(expect.any(Number)); + }); }); diff --git a/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.ts b/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.ts index db91bdad2c..a7410517bf 100644 --- a/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.ts +++ b/src/web-ui/src/infrastructure/api/adapters/tauri-adapter.ts @@ -89,15 +89,22 @@ export class TauriTransportAdapter implements ITransportAdapter { throw new Error('Tauri invoke function not initialized'); } const invokeStartedAt = nowMs(); - const result = params !== undefined - ? await this.invokeFn(action, params) - : await this.invokeFn(action); - if (timing) { - timing.invokeDurationMs = elapsedMs(invokeStartedAt); - timing.transportDurationMs = elapsedMs(transportStartedAt); - } + try { + const result = params !== undefined + ? await this.invokeFn(action, params) + : await this.invokeFn(action); + if (timing) { + timing.invokeDurationMs = elapsedMs(invokeStartedAt); + timing.transportDurationMs = elapsedMs(transportStartedAt); + } - return result as T; + return result as T; + } catch (error) { + if (timing) { + timing.invokeDurationMs = elapsedMs(invokeStartedAt); + } + throw error; + } } catch (error) { if (timing) { timing.transportDurationMs = elapsedMs(transportStartedAt); diff --git a/src/web-ui/src/infrastructure/api/service-api/GlobalAPI.ts b/src/web-ui/src/infrastructure/api/service-api/GlobalAPI.ts index 5cd634b58a..1c3e82c97c 100644 --- a/src/web-ui/src/infrastructure/api/service-api/GlobalAPI.ts +++ b/src/web-ui/src/infrastructure/api/service-api/GlobalAPI.ts @@ -66,6 +66,21 @@ export interface WorkspaceInfo { sshHost?: string; } +export interface RemoteWorkspaceSnapshot { + connectionId: string; + connectionName: string; + remotePath: string; + sshHost?: string; +} + +export interface WorkspaceStartupStateSnapshot { + cleanupRemovedCount: number; + currentWorkspace: WorkspaceInfo | null; + recentWorkspaces: WorkspaceInfo[]; + openedWorkspaces: WorkspaceInfo[]; + legacyRemoteWorkspace?: RemoteWorkspaceSnapshot | null; +} + export interface UpdateAppStatusRequest { status: AppStatus; } @@ -293,6 +308,17 @@ export class GlobalAPI { } } + async cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot(): Promise { + try { + return await api.invoke('cleanup_invalid_workspaces_and_get_workspace_state_snapshot'); + } catch (error) { + throw createTauriCommandError( + 'cleanup_invalid_workspaces_and_get_workspace_state_snapshot', + error + ); + } + } + async removeRecentWorkspace(workspaceId: string): Promise { try { await api.invoke('remove_recent_workspace', { diff --git a/src/web-ui/src/infrastructure/config/services/ConfigManager.test.ts b/src/web-ui/src/infrastructure/config/services/ConfigManager.test.ts index 2c47770020..fd29f78d42 100644 --- a/src/web-ui/src/infrastructure/config/services/ConfigManager.test.ts +++ b/src/web-ui/src/infrastructure/config/services/ConfigManager.test.ts @@ -38,6 +38,7 @@ describe('ConfigManager', () => { beforeEach(() => { vi.clearAllMocks(); configManager.clearCache(); + delete globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__; }); it('deduplicates concurrent reads for the same config path', async () => { @@ -56,6 +57,136 @@ describe('ConfigManager', () => { expect(configApiMocks.getConfig).toHaveBeenCalledTimes(1); }); + it('reads optional configs without reporting expected missing paths as failures', async () => { + configApiMocks.getConfig + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce(undefined); + + await expect(configManager.getOptionalConfig('app.keybindings')).resolves.toBeUndefined(); + + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(1); + expect(configApiMocks.getConfig).toHaveBeenCalledWith( + 'app.keybindings', + { skipRetryOnNotFound: true } + ); + + await expect(configManager.getConfig('app.keybindings')).resolves.toBeUndefined(); + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(2); + expect(configApiMocks.getConfig).toHaveBeenLastCalledWith('app.keybindings'); + }); + + it('uses bootstrap keybindings for the first optional startup read without a config IPC', async () => { + const storedKeybindings = { + version: 1, + bindings: { + 'session.new': 'Ctrl+Shift+N', + }, + }; + globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__ = storedKeybindings; + configApiMocks.getConfig.mockResolvedValueOnce({ version: 1, bindings: {} }); + + await expect(configManager.getOptionalConfig('app.keybindings')).resolves.toEqual(storedKeybindings); + expect(configApiMocks.getConfig).not.toHaveBeenCalled(); + expect(Object.prototype.hasOwnProperty.call(globalThis, '__BITFUN_BOOTSTRAP_KEYBINDINGS__')).toBe(false); + + await expect(configManager.getOptionalConfig('app.keybindings')).resolves.toEqual({ version: 1, bindings: {} }); + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(1); + expect(configApiMocks.getConfig).toHaveBeenCalledWith( + 'app.keybindings', + { skipRetryOnNotFound: true } + ); + }); + + it('does not reuse bootstrap keybindings after the config path is updated', async () => { + globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__ = { + version: 1, + bindings: { + 'session.new': 'Ctrl+Shift+N', + }, + }; + configApiMocks.setConfig.mockResolvedValueOnce(undefined); + configApiMocks.getConfig.mockResolvedValueOnce({ + version: 1, + bindings: { + 'session.new': 'Ctrl+Alt+N', + }, + }); + + await configManager.setConfig('app.keybindings', { + version: 1, + bindings: { + 'session.new': 'Ctrl+N', + }, + }); + configManager.clearCache(); + + await expect(configManager.getOptionalConfig('app.keybindings')).resolves.toEqual({ + version: 1, + bindings: { + 'session.new': 'Ctrl+Alt+N', + }, + }); + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(1); + expect(configApiMocks.getConfig).toHaveBeenCalledWith( + 'app.keybindings', + { skipRetryOnNotFound: true } + ); + }); + + it('clears bootstrap keybindings with the config cache', async () => { + globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__ = { + version: 1, + bindings: { + 'session.new': 'Ctrl+Shift+N', + }, + }; + configApiMocks.getConfig.mockResolvedValueOnce({ + version: 1, + bindings: { + 'session.new': 'Ctrl+Alt+N', + }, + }); + + configManager.clearCache(); + + await expect(configManager.getOptionalConfig('app.keybindings')).resolves.toEqual({ + version: 1, + bindings: { + 'session.new': 'Ctrl+Alt+N', + }, + }); + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(1); + expect(configApiMocks.getConfig).toHaveBeenCalledWith( + 'app.keybindings', + { skipRetryOnNotFound: true } + ); + }); + + it('does not share optional in-flight reads with strict config reads', async () => { + const optionalRead = createDeferred(); + const strictRead = createDeferred>(); + configApiMocks.getConfig + .mockReturnValueOnce(optionalRead.promise) + .mockReturnValueOnce(strictRead.promise); + + const optionalPromise = configManager.getOptionalConfig('app.keybindings'); + const strictPromise = configManager.getConfig('app.keybindings'); + + expect(configApiMocks.getConfig).toHaveBeenCalledTimes(2); + expect(configApiMocks.getConfig).toHaveBeenNthCalledWith( + 1, + 'app.keybindings', + { skipRetryOnNotFound: true } + ); + expect(configApiMocks.getConfig).toHaveBeenNthCalledWith(2, 'app.keybindings'); + + optionalRead.resolve(undefined); + strictRead.resolve({ 'session.new': 'Ctrl+N' }); + + await expect(optionalPromise).resolves.toBeUndefined(); + await expect(strictPromise).resolves.toEqual({ 'session.new': 'Ctrl+N' }); + }); + it('batches cold multi-path reads and caches the returned configs', async () => { configApiMocks.getConfigs.mockResolvedValueOnce({ 'ai.models': [{ id: 'model-1' }], diff --git a/src/web-ui/src/infrastructure/config/services/ConfigManager.ts b/src/web-ui/src/infrastructure/config/services/ConfigManager.ts index 559e9711fd..ac7a4a1373 100644 --- a/src/web-ui/src/infrastructure/config/services/ConfigManager.ts +++ b/src/web-ui/src/infrastructure/config/services/ConfigManager.ts @@ -13,6 +13,12 @@ import { extractProviderSegmentFromBaseUrl, matchProviderCatalogItemByBaseUrl, n const log = createLogger('ConfigManager'); const PROVIDER_INSTANCE_METADATA_KEY = 'provider_instance_id'; +declare global { + // Injected by the desktop webview initialization script before the frontend + // bundle runs. It avoids a startup-window IPC for the initial shortcut load. + var __BITFUN_BOOTSTRAP_KEYBINDINGS__: unknown | undefined; +} + function legacyProviderInstanceId(seed: string): string { let hash = 2166136261; for (let i = 0; i < seed.length; i += 1) { @@ -118,8 +124,42 @@ class ConfigManagerImpl implements IConfigManager { - private getReadKey(path?: string): string { - return path ?? ''; + private getReadKey(path?: string, mode: 'normal' | 'optional' = 'normal'): string { + return `${mode}:${path ?? ''}`; + } + + private clearBootstrapOptionalConfigs(): void { + delete globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__; + } + + private deleteInFlightReadsForPath(path?: string): void { + this.inFlightReads.delete(this.getReadKey(path)); + if (path) { + this.inFlightReads.delete(this.getReadKey(path, 'optional')); + if (path === 'app.keybindings') { + this.clearBootstrapOptionalConfigs(); + } + } + } + + private consumeBootstrapOptionalConfig(path: string): { + available: boolean; + value: T | undefined; + } { + if (path !== 'app.keybindings') { + return { available: false, value: undefined }; + } + + if (!Object.prototype.hasOwnProperty.call(globalThis, '__BITFUN_BOOTSTRAP_KEYBINDINGS__')) { + return { available: false, value: undefined }; + } + + const value = globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__; + delete globalThis.__BITFUN_BOOTSTRAP_KEYBINDINGS__; + return { + available: true, + value: value == null ? undefined : value as T, + }; } private async readConfig(path?: string): Promise { @@ -135,6 +175,15 @@ class ConfigManagerImpl implements IConfigManager { return resolvedConfig as T; } + private async readOptionalConfig(path: string): Promise { + const config = await configAPI.getConfig(path, { skipRetryOnNotFound: true }); + const resolvedConfig = path === 'ai.models' + ? await this.migrateLegacyAiModelsIfNeeded(config) + : config; + + return resolvedConfig as T | undefined; + } + private async readConfigs(paths: string[]): Promise> { const configs = await configAPI.getConfigs(paths); const resolvedConfigs: Record = {}; @@ -210,6 +259,38 @@ class ConfigManagerImpl implements IConfigManager { } } + async getOptionalConfig(path: string): Promise { + try { + if (this.configCache.has(path)) { + return this.configCache.get(path); + } + + const bootstrap = this.consumeBootstrapOptionalConfig(path); + if (bootstrap.available) { + return bootstrap.value; + } + + const readKey = this.getReadKey(path, 'optional'); + const existingRead = this.inFlightReads.get(readKey); + if (existingRead) { + return (await existingRead) as T | undefined; + } + + const readPromise = this.readOptionalConfig(path); + this.inFlightReads.set(readKey, readPromise); + try { + return await readPromise; + } finally { + if (this.inFlightReads.get(readKey) === readPromise) { + this.inFlightReads.delete(readKey); + } + } + } catch (error) { + log.error('Failed to get optional config', { path, error }); + throw error; + } + } + async getConfigs(paths: string[]): Promise> { const uniquePaths = Array.from(new Set(paths)); const results: Record = {}; @@ -293,7 +374,7 @@ class ConfigManagerImpl implements IConfigManager { async setConfig(path: string, value: T): Promise { try { const oldValue = this.configCache.get(path); - this.inFlightReads.delete(this.getReadKey(path)); + this.deleteInFlightReadsForPath(path); await configAPI.setConfig(path, value); @@ -316,7 +397,7 @@ class ConfigManagerImpl implements IConfigManager { if (path) { this.configCache.delete(path); - this.inFlightReads.delete(this.getReadKey(path)); + this.deleteInFlightReadsForPath(path); } else { this.configCache.clear(); this.inFlightReads.clear(); @@ -359,6 +440,8 @@ class ConfigManagerImpl implements IConfigManager { this.configCache.clear(); + this.inFlightReads.clear(); + this.clearBootstrapOptionalConfigs(); } catch (error) { log.error('Failed to import config', error); throw error; @@ -378,6 +461,7 @@ class ConfigManagerImpl implements IConfigManager { try { this.configCache.clear(); this.inFlightReads.clear(); + this.clearBootstrapOptionalConfigs(); } catch (error) { log.error('Failed to refresh cache', error); } @@ -386,6 +470,7 @@ class ConfigManagerImpl implements IConfigManager { clearCache(): void { this.configCache.clear(); this.inFlightReads.clear(); + this.clearBootstrapOptionalConfigs(); } diff --git a/src/web-ui/src/infrastructure/config/types/index.ts b/src/web-ui/src/infrastructure/config/types/index.ts index dc38f2f16d..64df226434 100644 --- a/src/web-ui/src/infrastructure/config/types/index.ts +++ b/src/web-ui/src/infrastructure/config/types/index.ts @@ -480,6 +480,7 @@ export interface WorkspaceConfig { export interface IConfigManager { getConfig(path?: string): Promise; + getOptionalConfig(path: string): Promise; getConfigs(paths: string[]): Promise>; setConfig(path: string, value: T): Promise; resetConfig(path?: string): Promise; diff --git a/src/web-ui/src/infrastructure/services/business/workspaceManager.test.ts b/src/web-ui/src/infrastructure/services/business/workspaceManager.test.ts index bf09f1d021..0b52cda41f 100644 --- a/src/web-ui/src/infrastructure/services/business/workspaceManager.test.ts +++ b/src/web-ui/src/infrastructure/services/business/workspaceManager.test.ts @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; const globalStateMocks = vi.hoisted(() => ({ initializeGlobalState: vi.fn(), cleanupInvalidWorkspaces: vi.fn(), + cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot: vi.fn(), getRecentWorkspaces: vi.fn(), getOpenedWorkspaces: vi.fn(), getCurrentWorkspace: vi.fn(), @@ -43,6 +44,13 @@ vi.mock('@/shared/utils/startupTrace', () => ({ function configureGlobalState(): void { globalStateMocks.initializeGlobalState.mockResolvedValue('initialized'); globalStateMocks.cleanupInvalidWorkspaces.mockResolvedValue(0); + globalStateMocks.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot.mockResolvedValue({ + cleanupRemovedCount: 0, + recentWorkspaces: [], + openedWorkspaces: [], + currentWorkspace: null, + legacyRemoteWorkspace: null, + }); globalStateMocks.getRecentWorkspaces.mockResolvedValue([]); globalStateMocks.getOpenedWorkspaces.mockResolvedValue([]); globalStateMocks.getCurrentWorkspace.mockResolvedValue(null); @@ -73,11 +81,44 @@ describe('WorkspaceManager startup initialization', () => { expect(listenMock).toHaveBeenCalledWith('workspace-identity-changed', expect.any(Function)); expect(globalStateMocks.initializeGlobalState).toHaveBeenCalledTimes(1); - expect(globalStateMocks.getCurrentWorkspace).not.toHaveBeenCalled(); + expect(globalStateMocks.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot).not.toHaveBeenCalled(); resolveListener?.(() => undefined); await initializePromise; - expect(globalStateMocks.getCurrentWorkspace).toHaveBeenCalledTimes(1); + expect(globalStateMocks.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot).toHaveBeenCalledTimes(1); + expect(globalStateMocks.cleanupInvalidWorkspaces).not.toHaveBeenCalled(); + expect(globalStateMocks.getCurrentWorkspace).not.toHaveBeenCalled(); + expect(globalStateMocks.getRecentWorkspaces).not.toHaveBeenCalled(); + expect(globalStateMocks.getOpenedWorkspaces).not.toHaveBeenCalled(); + }); + + it('stores the startup legacy remote workspace snapshot for one reconnect pass', async () => { + const legacyRemoteWorkspace = { + connectionId: 'conn-1', + connectionName: 'Remote', + remotePath: '/repo', + sshHost: 'devbox', + }; + globalStateMocks.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot.mockResolvedValue({ + cleanupRemovedCount: 0, + recentWorkspaces: [], + openedWorkspaces: [], + currentWorkspace: null, + legacyRemoteWorkspace, + }); + listenMock.mockResolvedValue(() => undefined); + const manager = await getFreshWorkspaceManager(); + + await manager.initialize(); + + expect(manager.consumeStartupLegacyRemoteWorkspaceSnapshot()).toEqual({ + available: true, + workspace: legacyRemoteWorkspace, + }); + expect(manager.consumeStartupLegacyRemoteWorkspaceSnapshot()).toEqual({ + available: false, + workspace: null, + }); }); }); diff --git a/src/web-ui/src/infrastructure/services/business/workspaceManager.ts b/src/web-ui/src/infrastructure/services/business/workspaceManager.ts index b01a5fbd77..41da9c0d23 100644 --- a/src/web-ui/src/infrastructure/services/business/workspaceManager.ts +++ b/src/web-ui/src/infrastructure/services/business/workspaceManager.ts @@ -1,7 +1,10 @@ -import { +import type { + RemoteWorkspaceSnapshot, WorkspaceInfo, +} from '../../../shared/types'; +import { WorkspaceKind, globalStateAPI, isRemoteWorkspace, @@ -73,6 +76,9 @@ class WorkspaceManager { private isInitialized = false; private isInitializing = false; private identityEventListening = false; + private startupLegacyRemoteWorkspaceSnapshotAvailable = false; + private startupLegacyRemoteWorkspaceSnapshotConsumed = false; + private startupLegacyRemoteWorkspace: RemoteWorkspaceSnapshot | null = null; private constructor() { this.state = { @@ -107,6 +113,24 @@ class WorkspaceManager { }; } + public consumeStartupLegacyRemoteWorkspaceSnapshot(): { + available: boolean; + workspace: RemoteWorkspaceSnapshot | null; + } { + if ( + !this.startupLegacyRemoteWorkspaceSnapshotAvailable || + this.startupLegacyRemoteWorkspaceSnapshotConsumed + ) { + return { available: false, workspace: null }; + } + + this.startupLegacyRemoteWorkspaceSnapshotConsumed = true; + return { + available: true, + workspace: this.startupLegacyRemoteWorkspace, + }; + } + private emit(event: WorkspaceEvent): void { log.debug('Emitting event', { type: event.type }); this.listeners.forEach(listener => { @@ -416,16 +440,25 @@ class WorkspaceManager { await identityListenerReady; const cleanupStartedAt = markWorkspaceStartupStepStart('cleanup_invalid_workspaces'); - await globalStateAPI.cleanupInvalidWorkspaces(); - markWorkspaceStartupStepEnd('cleanup_invalid_workspaces', cleanupStartedAt); + const { + cleanupRemovedCount, + recentWorkspaces, + openedWorkspaces, + currentWorkspace, + legacyRemoteWorkspace, + } = await globalStateAPI.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot(); + this.startupLegacyRemoteWorkspace = legacyRemoteWorkspace; + this.startupLegacyRemoteWorkspaceSnapshotAvailable = true; + this.startupLegacyRemoteWorkspaceSnapshotConsumed = false; + markWorkspaceStartupStepEnd('cleanup_invalid_workspaces', cleanupStartedAt, { + removedCount: cleanupRemovedCount, + includesWorkspaceStateSnapshot: true, + includesLegacyRemoteWorkspace: true, + }); const fetchStateStartedAt = markWorkspaceStartupStepStart('fetch_workspace_state'); - const [recentWorkspaces, openedWorkspaces, currentWorkspace] = await Promise.all([ - globalStateAPI.getRecentWorkspaces(), - globalStateAPI.getOpenedWorkspaces(), - globalStateAPI.getCurrentWorkspace(), - ]); markWorkspaceStartupStepEnd('fetch_workspace_state', fetchStateStartedAt, { + source: 'startup_cleanup_snapshot', recentCount: recentWorkspaces.length, openedCount: openedWorkspaces.length, hasCurrentWorkspace: currentWorkspace !== null, diff --git a/src/web-ui/src/main.tsx b/src/web-ui/src/main.tsx index 58d05d5fe1..cdb0962152 100644 --- a/src/web-ui/src/main.tsx +++ b/src/web-ui/src/main.tsx @@ -4,6 +4,7 @@ import AgentCompanionDesktopPet from "./app/components/AgentCompanionDesktopPet/ import AppErrorBoundary from "./app/components/AppErrorBoundary"; import { STARTUP_OVERLAY_HIDDEN_EVENT } from "./app/startup/startupSignals"; import { WorkspaceProvider } from "./infrastructure/contexts/WorkspaceProvider"; +import { I18nProvider } from "./infrastructure/i18n/providers/I18nProvider"; import "./app/styles/index.scss"; // Font: Noto Sans SC is loaded via a tag in index.html. @@ -317,18 +318,12 @@ async function startApplication(): Promise { log.error('Failed to initialize BitFun (pre-render)', error); } - // I18n Provider. - const i18nProviderImportResult = await traceStartupStep( - 'startup_step', - 'load_i18n_provider', - () => measureAsyncAndLog( - log, - 'Startup step completed', - () => import('./infrastructure/i18n'), - { data: { step: 'loadI18nProvider' } } - ) - ); - const { I18nProvider } = i18nProviderImportResult.value; + startupTrace.markPhase('startup_step_start', { step: 'load_i18n_provider', mode: 'static' }); + startupTrace.markPhase('startup_step_end', { + step: 'load_i18n_provider', + durationMs: 0, + mode: 'static', + }); const isAgentCompanionWindow = new URLSearchParams(window.location.search) .get('bitfunWindow') === 'agent-companion'; diff --git a/src/web-ui/src/shared/types/global-state.ts b/src/web-ui/src/shared/types/global-state.ts index 4ac2b95169..12ad9f0bfe 100644 --- a/src/web-ui/src/shared/types/global-state.ts +++ b/src/web-ui/src/shared/types/global-state.ts @@ -6,6 +6,7 @@ import { workspaceAPI } from '@/infrastructure/api'; import type { ApplicationState as APIApplicationState, AppStatus as APIAppStatus, + RemoteWorkspaceSnapshot as APIRemoteWorkspaceSnapshot, WorkspaceInfo as APIWorkspaceInfo, } from '@/infrastructure/api/service-api/GlobalAPI'; import { createLogger } from '../utils/logger'; @@ -112,6 +113,13 @@ export interface WorkspaceInfo { sshHost?: string; } +export interface RemoteWorkspaceSnapshot { + connectionId: string; + connectionName: string; + remotePath: string; + sshHost?: string; +} + function summarizeWorkspacesForLog(workspaces: WorkspaceInfo[]) { return workspaces.reduce( (summary, workspace) => { @@ -216,6 +224,13 @@ export interface GlobalStateAPI { getCurrentWorkspace(): Promise; getOpenedWorkspaces(): Promise; getRecentWorkspaces(): Promise; + cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot(): Promise<{ + cleanupRemovedCount: number; + currentWorkspace: WorkspaceInfo | null; + recentWorkspaces: WorkspaceInfo[]; + openedWorkspaces: WorkspaceInfo[]; + legacyRemoteWorkspace: RemoteWorkspaceSnapshot | null; + }>; removeWorkspaceFromRecent(workspaceId: string): Promise; cleanupInvalidWorkspaces(): Promise; scanWorkspaceInfo(workspacePath: string): Promise; @@ -345,6 +360,21 @@ function mapWorkspaceInfo(workspace: APIWorkspaceInfo): WorkspaceInfo { }; } +function mapRemoteWorkspaceSnapshot( + workspace: APIRemoteWorkspaceSnapshot | null | undefined +): RemoteWorkspaceSnapshot | null { + if (!workspace) { + return null; + } + + return { + connectionId: workspace.connectionId, + connectionName: workspace.connectionName, + remotePath: workspace.remotePath, + sshHost: workspace.sshHost?.trim() || undefined, + }; +} + function mapApplicationState(state: APIApplicationState): ApplicationState { const now = new Date().toISOString(); return { @@ -457,6 +487,28 @@ export function createGlobalStateAPI(): GlobalStateAPI { return workspaces; }, + async cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot(): Promise<{ + cleanupRemovedCount: number; + currentWorkspace: WorkspaceInfo | null; + recentWorkspaces: WorkspaceInfo[]; + openedWorkspaces: WorkspaceInfo[]; + legacyRemoteWorkspace: RemoteWorkspaceSnapshot | null; + }> { + const snapshot = await globalAPI.cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot(); + const recentWorkspaces = snapshot.recentWorkspaces.map(mapWorkspaceInfo); + logger.debug( + 'cleanupInvalidWorkspacesAndGetWorkspaceStateSnapshot returned', + summarizeWorkspacesForLog(recentWorkspaces) + ); + return { + cleanupRemovedCount: snapshot.cleanupRemovedCount, + currentWorkspace: snapshot.currentWorkspace ? mapWorkspaceInfo(snapshot.currentWorkspace) : null, + recentWorkspaces, + openedWorkspaces: snapshot.openedWorkspaces.map(mapWorkspaceInfo), + legacyRemoteWorkspace: mapRemoteWorkspaceSnapshot(snapshot.legacyRemoteWorkspace), + }; + }, + async removeWorkspaceFromRecent(workspaceId: string): Promise { await globalAPI.removeRecentWorkspace(workspaceId); }, diff --git a/tests/e2e/helpers/performance-trace.ts b/tests/e2e/helpers/performance-trace.ts index 761c08d4d3..d6adf1c31c 100644 --- a/tests/e2e/helpers/performance-trace.ts +++ b/tests/e2e/helpers/performance-trace.ts @@ -168,12 +168,14 @@ export type StartupPerfBreakdown = { beforeInteractive: { matchedCount: number; totalFrontendDurationMs: number; + totalFrontendDurationUntilInteractiveMs: number; totalBackendDurationMs: number; totalEstimatedQueueOrBridgeMs: number; byCommand: Array<{ command: string; count: number; frontendDurationMs: number; + frontendDurationUntilInteractiveMs: number; backendDurationMs: number; estimatedQueueOrBridgeMs: number; }>; @@ -181,7 +183,9 @@ export type StartupPerfBreakdown = { command: string; target?: string; startedAtMs?: number; + endedAtMs?: number; frontendDurationMs: number; + frontendDurationUntilInteractiveMs?: number; backendDurationMs?: number; estimatedQueueOrBridgeMs?: number; transportDurationMs?: number; @@ -194,13 +198,16 @@ export type StartupPerfBreakdown = { apiBeforeInteractive: { count: number; totalDurationMs: number; + totalDurationUntilInteractiveMs: number; maxDurationMs?: number; byCommand: StartupTraceCommandAggregate[]; slowCalls: Array<{ command: string; target?: string; startedAtMs?: number; + endedAtMs?: number; durationMs: number; + durationUntilInteractiveMs?: number; outcome: 'success' | 'failure'; remote: boolean; }>; @@ -211,7 +218,9 @@ export interface StartupApiCommandSegment { command: string; target?: string; startedAtMs?: number; + endedAtMs?: number; frontendDurationMs: number; + frontendDurationUntilInteractiveMs?: number; backendDurationMs?: number; estimatedQueueOrBridgeMs?: number; transportDurationMs?: number; @@ -362,6 +371,18 @@ function aggregateApiCalls(calls: StartupTraceApiCallRecord[]): StartupTraceComm .sort((left, right) => right.totalDurationMs - left.totalDurationMs); } +function durationUntilCutoffMs( + startedAtMs: number | undefined, + durationMs: number, + cutoffMs: number | undefined, +): number { + if (startedAtMs === undefined || cutoffMs === undefined) { + return durationMs; + } + const endMs = startedAtMs + durationMs; + return Math.max(0, Math.min(endMs, cutoffMs) - startedAtMs); +} + export function summarizeApiCommandSegments( snapshot: StartupTraceSnapshot, frontendCalls: StartupTraceApiCallRecord[] = snapshot.api.calls ?? [], @@ -394,6 +415,7 @@ function matchBackendCommandSegments( command: call.command, target: call.target, startedAtMs: round(call.startedAtMs), + endedAtMs: round(call.endedAtMs), frontendDurationMs: round(call.durationMs) ?? 0, backendDurationMs: round(backendDurationMs), estimatedQueueOrBridgeMs: round( @@ -414,12 +436,20 @@ function matchBackendCommandSegments( function summarizeBackendCommandOverlap( frontendCalls: StartupTraceApiCallRecord[], nativeEvents: StartupTraceNativeEvent[], + cutoffMs?: number, ) { - const matched = matchBackendCommandSegments(frontendCalls, nativeEvents); + const matched = matchBackendCommandSegments(frontendCalls, nativeEvents) + .map(call => ({ + ...call, + frontendDurationUntilInteractiveMs: round( + durationUntilCutoffMs(call.startedAtMs, call.frontendDurationMs, cutoffMs) + ), + })); const byCommand = new Map(); @@ -428,11 +458,15 @@ function summarizeBackendCommandOverlap( command: call.command, count: 0, frontendDurationMs: 0, + frontendDurationUntilInteractiveMs: 0, backendDurationMs: 0, estimatedQueueOrBridgeMs: 0, }; existing.count += 1; existing.frontendDurationMs = round((existing.frontendDurationMs ?? 0) + call.frontendDurationMs) ?? 0; + existing.frontendDurationUntilInteractiveMs = round( + (existing.frontendDurationUntilInteractiveMs ?? 0) + (call.frontendDurationUntilInteractiveMs ?? 0) + ) ?? 0; existing.backendDurationMs = round((existing.backendDurationMs ?? 0) + (call.backendDurationMs ?? 0)) ?? 0; existing.estimatedQueueOrBridgeMs = round( (existing.estimatedQueueOrBridgeMs ?? 0) + (call.estimatedQueueOrBridgeMs ?? 0) @@ -445,6 +479,9 @@ function summarizeBackendCommandOverlap( totalFrontendDurationMs: round( matched.reduce((total, call) => total + call.frontendDurationMs, 0) ) ?? 0, + totalFrontendDurationUntilInteractiveMs: round( + matched.reduce((total, call) => total + (call.frontendDurationUntilInteractiveMs ?? call.frontendDurationMs), 0) + ) ?? 0, totalBackendDurationMs: round( matched.reduce((total, call) => total + (call.backendDurationMs ?? 0), 0) ) ?? 0, @@ -534,6 +571,7 @@ export function summarizeStartupBreakdown(snapshot: StartupTraceSnapshot): Start const backendBeforeInteractive = summarizeBackendCommandOverlap( apiBeforeInteractive, snapshot.native?.events ?? [], + interactive, ); const slowApiCalls = [...apiBeforeInteractive] .sort((left, right) => right.durationMs - left.durationMs) @@ -542,7 +580,11 @@ export function summarizeStartupBreakdown(snapshot: StartupTraceSnapshot): Start command: call.command, target: call.target, startedAtMs: round(call.startedAtMs), + endedAtMs: round(call.endedAtMs), durationMs: round(call.durationMs) ?? 0, + durationUntilInteractiveMs: round( + durationUntilCutoffMs(call.startedAtMs, call.durationMs, interactive) + ), outcome: call.outcome, remote: call.remote, })); @@ -650,6 +692,12 @@ export function summarizeStartupBreakdown(snapshot: StartupTraceSnapshot): Start apiBeforeInteractive: { count: apiBeforeInteractive.length, totalDurationMs: round(apiBeforeInteractive.reduce((total, call) => total + call.durationMs, 0)) ?? 0, + totalDurationUntilInteractiveMs: round( + apiBeforeInteractive.reduce( + (total, call) => total + durationUntilCutoffMs(call.startedAtMs, call.durationMs, interactive), + 0, + ) + ) ?? 0, maxDurationMs: apiBeforeInteractive.length > 0 ? round(Math.max(...apiBeforeInteractive.map(call => call.durationMs))) : undefined, From adab734927a78126edd827668cef96ea72f0f3d2 Mon Sep 17 00:00:00 2001 From: limityan Date: Mon, 15 Jun 2026 14:36:18 +0800 Subject: [PATCH 003/100] refactor(web-ui): expand theme token contract migration --- scripts/audit-theme-colors.mjs | 245 +++++++++++++++++- .../AgentCompanionDesktopPet.scss | 26 +- .../src/app/components/NavPanel/NavPanel.scss | 4 - .../NavPanel/NavPanelLayout.test.ts | 18 +- .../WorkspaceSessionBatchModal.scss | 2 +- .../components/SplashScreen/SplashScreen.scss | 2 +- .../mission-control/MissionControl.scss | 12 +- .../mission-control/SearchFilter.scss | 4 +- .../mission-control/ThumbnailCard.scss | 14 +- .../tab-bar/TabOverflowMenu.scss | 8 +- src/web-ui/src/app/layout/WorkspaceBody.scss | 10 +- .../scenes/agents/components/AgentCard.scss | 2 +- .../agents/components/CoreAgentCard.scss | 10 +- .../agents/components/CreateAgentPage.scss | 4 +- src/web-ui/src/app/scenes/git/GitScene.scss | 4 +- .../src/app/scenes/miniapps/MiniAppScene.scss | 22 +- .../app/scenes/my-agent/InsightsScene.scss | 4 +- .../profile/views/AssistantQuickInput.scss | 4 +- .../app/scenes/profile/views/NurseryView.scss | 2 +- .../components/KeyboardShortcutsTab.scss | 6 +- src/web-ui/src/app/scenes/shell/ShellNav.scss | 4 +- .../src/app/scenes/skills/SkillsScene.scss | 4 +- .../src/app/scenes/welcome/WelcomeScene.scss | 4 +- src/web-ui/src/app/styles/global.scss | 4 +- .../components/Badge/Badge.scss | 2 +- .../components/Button/Button.scss | 18 +- .../components/Card/Card.scss | 12 +- .../DotMatrixLoader/DotMatrixLoader.scss | 17 +- .../components/FilterPill/FilterPill.scss | 10 +- .../components/Markdown/Markdown.scss | 12 +- .../components/Markdown/Markdown.tsx | 2 +- .../components/Markdown/MermaidBlock.scss | 4 +- .../components/Markdown/markdownPrismTheme.ts | 4 +- .../components/NumberInput/NumberInput.scss | 2 +- .../components/Select/Select.scss | 4 +- .../components/Switch/Switch.scss | 14 +- .../component-library/components/Tag/Tag.tsx | 4 +- .../components/Tooltip/Tooltip.scss | 2 +- .../WindowControls/WindowControls.scss | 4 +- .../styles/btn-primary-tokens.scss | 4 +- .../src/component-library/styles/tokens.scss | 75 +++++- .../flow_chat/components/AcpPlanPanel.scss | 2 +- .../src/flow_chat/components/ChatInput.scss | 6 +- .../src/flow_chat/components/CodePreview.scss | 6 +- .../components/FileMentionPicker.scss | 6 +- .../flow_chat/components/FlowTextBlock.scss | 2 +- .../components/FlowToolCardErrorBoundary.tsx | 6 +- .../components/ImageAnalysisCard.scss | 84 +++--- .../components/InlineDiffPreview.scss | 6 +- .../TaskDetailPanel/TaskDetailPanel.scss | 2 +- .../src/flow_chat/components/UserMessage.css | 1 + .../src/flow_chat/components/UserMessage.scss | 1 + .../src/flow_chat/components/UserMessage.tsx | 1 + .../src/flow_chat/components/WelcomePanel.css | 2 +- .../components/codePreviewPrismTheme.ts | 2 +- .../components/modern/ExploreRegion.scss | 2 +- .../components/modern/FlowChatHeader.scss | 2 +- .../modern/ModernFlowChatContainer.scss | 6 +- .../components/modern/ScrollAnchor.scss | 4 +- .../components/modern/SessionFilesBadge.scss | 2 +- .../modern/SmoothHeightCollapse.scss | 4 +- .../components/modern/SubagentItems.scss | 2 +- .../modern/TurnCompletionNoticeItem.scss | 2 +- .../components/modern/UserMessageItem.scss | 8 +- .../thread-goal/ThreadGoalDialogs.scss | 6 +- .../components/usage/SessionUsagePanel.scss | 4 +- .../usage/SessionUsageReportCard.scss | 2 +- .../tool-cards/AcpPermissionActions.scss | 4 +- .../flow_chat/tool-cards/BaseToolCard.scss | 2 - .../tool-cards/CodeReviewToolCard.scss | 2 +- .../flow_chat/tool-cards/DefaultToolCard.scss | 2 +- .../tool-cards/FileOperationToolCard.scss | 6 +- .../flow_chat/tool-cards/GitToolDisplay.scss | 7 +- .../tool-cards/MiniAppToolDisplay.scss | 2 +- .../tool-cards/ModelThinkingDisplay.scss | 13 +- .../flow_chat/tool-cards/TaskToolDisplay.scss | 4 +- .../tool-cards/TerminalToolCard.scss | 5 +- .../tool-cards/ToolCardStatusSlot.scss | 4 +- .../tool-cards/_tool-card-common.scss | 18 +- .../config/components/AIFeaturesConfig.scss | 8 +- .../config/components/BasicsConfig.scss | 2 +- .../config/components/DefaultModelConfig.scss | 6 +- .../config/components/MCPResourceBrowser.scss | 2 +- .../config/components/ReviewConfig.scss | 2 +- .../common/ConfigCollectionSection.scss | 2 +- .../components/common/ConfigPageHeader.scss | 2 +- .../components/common/ConfigPageLayout.scss | 10 +- .../i18n/components/LanguageSelector.scss | 14 +- .../infrastructure/theme/core/ThemeService.ts | 18 ++ .../components/NotificationItem.scss | 2 +- .../tools/editor/components/DiffEditor.scss | 6 +- .../editor/components/MarkdownEditor.scss | 2 +- .../ReadOnlyCodeBlock/ReadOnlyCodeBlock.scss | 2 +- .../meditor/components/TiptapEditor.scss | 4 +- .../file-system/styles/FileExplorer.scss | 2 +- .../GenerativeWidgetFrame.tsx | 105 ++++---- .../GenerativeWidgetPanel.scss | 10 +- .../tools/generative-widget/themePayload.ts | 59 +++++ .../GitBranchHistoryView.scss | 10 +- .../GitDiffEditor/GitDiffEditor.scss | 2 +- .../components/GitGraphView/GitGraphView.scss | 12 +- 101 files changed, 772 insertions(+), 359 deletions(-) diff --git a/scripts/audit-theme-colors.mjs b/scripts/audit-theme-colors.mjs index ad3365d3ab..66ea1371b9 100644 --- a/scripts/audit-theme-colors.mjs +++ b/scripts/audit-theme-colors.mjs @@ -11,6 +11,17 @@ const TOKEN_PATH_PARTS = [ 'infrastructure/theme', 'theme/presets', ]; +const CONTRACT_VAR_DEFINITION_PATH_PARTS = [ + 'component-library/styles', + 'infrastructure/theme', + 'tools/generative-widget/themePayload.ts', +]; +const STATIC_CONTRACT_VAR_DEFINITION_PATH_PARTS = [ + 'component-library/styles', +]; +const RUNTIME_CONTRACT_VAR_DEFINITION_PATH_PARTS = [ + 'infrastructure/theme', +]; const EXCEPTION_PATH_PARTS = [ 'monaco', 'terminal', @@ -24,6 +35,9 @@ const COLOR_PATTERN = const CSS_VAR_USAGE_PATTERN = /var\(\s*(--[a-zA-Z0-9_-]+)/g; const CSS_VAR_DEFINITION_PATTERN = /(^|[;{\s])(--[a-zA-Z0-9_-]+)\s*:/g; const VAR_FALLBACK_PATTERN = /var\(\s*(--[a-zA-Z0-9_-]+)\s*,/g; +const CSS_VAR_SET_PROPERTY_PATTERN = /\.setProperty\(\s*['"`](--[a-zA-Z0-9_-]+)/g; +const CSS_VAR_INLINE_STYLE_PATTERN = /['"`](--[a-zA-Z0-9_-]+)['"`]\s*:/g; +const CSS_VAR_DYNAMIC_SET_PATTERN = /\.setProperty\(\s*`(--[a-zA-Z0-9_-]*)\$\{/g; function parseArgs(argv) { const options = { @@ -98,6 +112,18 @@ function isTokenFile(relativePath) { return TOKEN_PATH_PARTS.some(part => relativePath.includes(part)); } +function isContractVarDefinitionFile(relativePath) { + return CONTRACT_VAR_DEFINITION_PATH_PARTS.some(part => relativePath.includes(part)); +} + +function isStaticContractVarDefinitionFile(relativePath) { + return STATIC_CONTRACT_VAR_DEFINITION_PATH_PARTS.some(part => relativePath.includes(part)); +} + +function isRuntimeContractVarDefinitionFile(relativePath) { + return RUNTIME_CONTRACT_VAR_DEFINITION_PATH_PARTS.some(part => relativePath.includes(part)); +} + function isExceptionFile(relativePath) { return EXCEPTION_PATH_PARTS.some(part => relativePath.toLowerCase().includes(part.toLowerCase())); } @@ -106,6 +132,12 @@ function incrementMap(map, key, amount = 1) { map.set(key, (map.get(key) ?? 0) + amount); } +function addToSetMap(map, key, value) { + const values = map.get(key) ?? new Set(); + values.add(value); + map.set(key, values); +} + function collectMatches(content, pattern) { pattern.lastIndex = 0; return Array.from(content.matchAll(pattern)); @@ -196,6 +228,13 @@ function audit(options) { const fallbackTokenCounts = new Map(); const varUsageCounts = new Map(); const varDefinitionCounts = new Map(); + const varDefinitionKinds = new Map(); + const varDefinitionFiles = new Map(); + const contractVarDefinitions = new Set(); + const staticContractVarDefinitions = new Set(); + const runtimeContractVarDefinitions = new Set(); + const varUsageFiles = new Map(); + const dynamicDefinitionPrefixes = new Set(); const fileColorCounts = new Map(); const componentFileColorCounts = new Map(); const exceptionColorCounts = new Map(); @@ -232,10 +271,44 @@ function audit(options) { for (const match of collectMatches(content, CSS_VAR_USAGE_PATTERN)) { incrementMap(varUsageCounts, match[1]); + addToSetMap(varUsageFiles, match[1], relativePath); } for (const match of collectMatches(content, CSS_VAR_DEFINITION_PATTERN)) { incrementMap(varDefinitionCounts, match[2]); + addToSetMap(varDefinitionKinds, match[2], 'css'); + addToSetMap(varDefinitionFiles, match[2], relativePath); + if (isContractVarDefinitionFile(relativePath)) { + contractVarDefinitions.add(match[2]); + } + if (isStaticContractVarDefinitionFile(relativePath)) { + staticContractVarDefinitions.add(match[2]); + } + } + + for (const match of collectMatches(content, CSS_VAR_SET_PROPERTY_PATTERN)) { + incrementMap(varDefinitionCounts, match[1]); + addToSetMap(varDefinitionKinds, match[1], 'runtime'); + addToSetMap(varDefinitionFiles, match[1], relativePath); + if (isContractVarDefinitionFile(relativePath)) { + contractVarDefinitions.add(match[1]); + } + if (isRuntimeContractVarDefinitionFile(relativePath)) { + runtimeContractVarDefinitions.add(match[1]); + } + } + + for (const match of collectMatches(content, CSS_VAR_INLINE_STYLE_PATTERN)) { + incrementMap(varDefinitionCounts, match[1]); + addToSetMap(varDefinitionKinds, match[1], 'inline-style'); + addToSetMap(varDefinitionFiles, match[1], relativePath); + if (isContractVarDefinitionFile(relativePath)) { + contractVarDefinitions.add(match[1]); + } + } + + for (const match of collectMatches(content, CSS_VAR_DYNAMIC_SET_PATTERN)) { + dynamicDefinitionPrefixes.add(match[1]); } for (const match of collectMatches(content, VAR_FALLBACK_PATTERN)) { @@ -245,11 +318,73 @@ function audit(options) { } const definedVars = new Set(varDefinitionCounts.keys()); - const undefinedVars = Array.from(varUsageCounts.entries()) - .filter(([name]) => !definedVars.has(name)) + const getDefinitionKinds = name => Array.from(varDefinitionKinds.get(name) ?? ['unknown']).sort(); + const getDefinitionKind = name => { + if (definedVars.has(name)) { + return getDefinitionKinds(name).join('+'); + } + const dynamicPrefix = Array.from(dynamicDefinitionPrefixes).find(prefix => name.startsWith(prefix)); + return dynamicPrefix ? `dynamic-family:${dynamicPrefix}*` : null; + }; + const unresolvedVarEntries = Array.from(varUsageCounts.entries()) + .filter(([name]) => !getDefinitionKind(name)) + .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])); + const undefinedVars = unresolvedVarEntries .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) .slice(0, 100) .map(([key, count]) => ({ key, count })); + const fallbackOnlyVars = unresolvedVarEntries + .filter(([name]) => fallbackTokenCounts.has(name)) + .slice(0, 100) + .map(([key, count]) => ({ key, count })); + const unresolvedRequiredVars = unresolvedVarEntries + .filter(([name]) => !fallbackTokenCounts.has(name)) + .slice(0, 100) + .map(([key, count]) => ({ + key, + count, + files: Array.from(varUsageFiles.get(key) ?? []).slice(0, 5), + })); + const dynamicDefinedVars = Array.from(varUsageCounts.entries()) + .map(([key, count]) => ({ key, count, kind: getDefinitionKind(key) })) + .filter(entry => entry.kind && entry.kind !== 'css') + .sort((a, b) => b.count - a.count || a.key.localeCompare(b.key)) + .slice(0, 100); + const nonContractDefinedVars = Array.from(varUsageCounts.entries()) + .filter(([name]) => definedVars.has(name) && !contractVarDefinitions.has(name)) + .map(([key, count]) => ({ + key, + count, + definitionKinds: getDefinitionKinds(key), + definitionFiles: Array.from(varDefinitionFiles.get(key) ?? []).slice(0, 5), + usageFiles: Array.from(varUsageFiles.get(key) ?? []).slice(0, 5), + usageFileCount: (varUsageFiles.get(key) ?? new Set()).size, + })) + .filter(entry => entry.usageFileCount > 1) + .sort((a, b) => b.usageFileCount - a.usageFileCount || b.count - a.count || a.key.localeCompare(b.key)) + .slice(0, 100); + const nonContractDynamicInputVars = nonContractDefinedVars + .filter(entry => entry.definitionKinds.some(kind => kind === 'inline-style' || kind === 'runtime')); + const nonContractCssPrivateVars = nonContractDefinedVars + .filter(entry => ( + entry.definitionKinds.includes('css') + && !entry.definitionKinds.some(kind => kind === 'inline-style' || kind === 'runtime') + )); + const runtimeOnlyRequiredContractVars = Array.from(varUsageCounts.entries()) + .filter(([name]) => ( + runtimeContractVarDefinitions.has(name) + && !staticContractVarDefinitions.has(name) + && !fallbackTokenCounts.has(name) + )) + .map(([key, count]) => ({ + key, + count, + definitionFiles: Array.from(varDefinitionFiles.get(key) ?? []).slice(0, 5), + usageFiles: Array.from(varUsageFiles.get(key) ?? []).slice(0, 5), + usageFileCount: (varUsageFiles.get(key) ?? new Set()).size, + })) + .sort((a, b) => b.count - a.count || a.key.localeCompare(b.key)) + .slice(0, 100); const nearPairs = buildNearColorPairs(componentColorCounts); const uniqueComponentColors = componentColorCounts.size; @@ -276,6 +411,27 @@ function audit(options) { topFiles: topEntries(fileColorCounts, options.top), topFallbackTokens: topEntries(fallbackTokenCounts, options.top), undefinedVars, + cssVarDefinitions: { + definedUnique: definedVars.size, + contractDefinedUnique: contractVarDefinitions.size, + staticContractDefinedUnique: staticContractVarDefinitions.size, + runtimeContractDefinedUnique: runtimeContractVarDefinitions.size, + dynamicFamilyPrefixes: Array.from(dynamicDefinitionPrefixes).sort(), + unresolvedUnique: unresolvedVarEntries.length, + fallbackOnlyUnique: fallbackOnlyVars.length, + unresolvedRequiredUnique: unresolvedRequiredVars.length, + runtimeOnlyRequiredContractUnique: runtimeOnlyRequiredContractVars.length, + nonContractCrossFileUnique: nonContractDefinedVars.length, + nonContractDynamicInputUnique: nonContractDynamicInputVars.length, + nonContractCssPrivateUnique: nonContractCssPrivateVars.length, + }, + dynamicDefinedVars, + nonContractDefinedVars, + nonContractDynamicInputVars, + nonContractCssPrivateVars, + runtimeOnlyRequiredContractVars, + fallbackOnlyVars, + unresolvedRequiredVars, nearPairs, }; } @@ -307,8 +463,91 @@ function printText(report) { console.log('\nTop fallback tokens:'); console.log(printRows(report.topFallbackTokens)); - console.log('\nUndefined or dynamically-defined CSS vars (top):'); + console.log('\nUnresolved CSS vars before fallback classification (top):'); console.log(printRows(report.undefinedVars)); + console.log( + `\nCSS var definition coverage: defined=${report.cssVarDefinitions.definedUnique}, ` + + `contractDefined=${report.cssVarDefinitions.contractDefinedUnique}, ` + + `staticContract=${report.cssVarDefinitions.staticContractDefinedUnique}, ` + + `runtimeContract=${report.cssVarDefinitions.runtimeContractDefinedUnique}, ` + + `dynamicFamilies=${report.cssVarDefinitions.dynamicFamilyPrefixes.length}, ` + + `unresolved=${report.cssVarDefinitions.unresolvedUnique}, ` + + `fallbackOnly=${report.cssVarDefinitions.fallbackOnlyUnique}, ` + + `requiredMissing=${report.cssVarDefinitions.unresolvedRequiredUnique}, ` + + `runtimeOnlyRequired=${report.cssVarDefinitions.runtimeOnlyRequiredContractUnique}, ` + + `nonContractCrossFile=${report.cssVarDefinitions.nonContractCrossFileUnique}, ` + + `nonContractDynamicInputs=${report.cssVarDefinitions.nonContractDynamicInputUnique}, ` + + `nonContractCssPrivate=${report.cssVarDefinitions.nonContractCssPrivateUnique}` + ); + + console.log('\nDynamic/runtime-defined CSS vars (top):'); + console.log( + report.dynamicDefinedVars + .slice(0, 10) + .map(row => ` ${row.count.toString().padStart(5)} ${row.key} ${row.kind}`) + .join('\n') || ' none' + ); + + console.log('\nFallback-only unresolved CSS vars (top):'); + console.log(printRows(report.fallbackOnlyVars.slice(0, 10))); + + console.log('\nNon-contract CSS vars used across files (top):'); + if (report.nonContractDefinedVars.length === 0) { + console.log(' none'); + } else { + for (const row of report.nonContractDefinedVars.slice(0, 10)) { + console.log( + ` ${row.count.toString().padStart(5)} ${row.key} ` + + `usageFiles=${row.usageFileCount} kinds=${row.definitionKinds.join('+')} ` + + `definitions=${row.definitionFiles.join(', ')}` + ); + } + } + + console.log('\nNon-contract dynamic input CSS vars (top):'); + if (report.nonContractDynamicInputVars.length === 0) { + console.log(' none'); + } else { + for (const row of report.nonContractDynamicInputVars.slice(0, 10)) { + console.log( + ` ${row.count.toString().padStart(5)} ${row.key} ` + + `usageFiles=${row.usageFileCount} definitions=${row.definitionFiles.join(', ')}` + ); + } + } + + console.log('\nNon-contract component-private CSS vars (top):'); + if (report.nonContractCssPrivateVars.length === 0) { + console.log(' none'); + } else { + for (const row of report.nonContractCssPrivateVars.slice(0, 10)) { + console.log( + ` ${row.count.toString().padStart(5)} ${row.key} ` + + `usageFiles=${row.usageFileCount} definitions=${row.definitionFiles.join(', ')}` + ); + } + } + + console.log('\nRequired unresolved CSS vars (top):'); + if (report.unresolvedRequiredVars.length === 0) { + console.log(' none'); + } else { + for (const row of report.unresolvedRequiredVars.slice(0, 10)) { + console.log(` ${row.count.toString().padStart(5)} ${row.key} files=${row.files.join(', ')}`); + } + } + + console.log('\nRuntime-only contract CSS vars without fallback (top):'); + if (report.runtimeOnlyRequiredContractVars.length === 0) { + console.log(' none'); + } else { + for (const row of report.runtimeOnlyRequiredContractVars.slice(0, 10)) { + console.log( + ` ${row.count.toString().padStart(5)} ${row.key} ` + + `usageFiles=${row.usageFileCount} definitions=${row.definitionFiles.join(', ')}` + ); + } + } console.log('\nIndistinguishable component color pairs (sample):'); if (report.nearPairs.indistinguishable.length === 0) { diff --git a/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss b/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss index 20d0fdd82b..fcaf19fe94 100644 --- a/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss +++ b/src/web-ui/src/app/components/AgentCompanionDesktopPet/AgentCompanionDesktopPet.scss @@ -14,6 +14,10 @@ } .bitfun-agent-companion-window { + --bitfun-agent-companion-gap: 8px; + --bitfun-agent-companion-pet-width: 96px; + --bitfun-agent-companion-pet-height: 96px; + width: 100vw; height: 100vh; position: relative; @@ -28,25 +32,25 @@ flex-direction: row; align-items: center; justify-content: flex-end; - gap: var(--bitfun-agent-companion-gap, 8px); + gap: var(--bitfun-agent-companion-gap); width: max-content; max-height: 100vh; pointer-events: none; } &__pet { - width: min(var(--bitfun-agent-companion-pet-width, 96px), 100vw); - height: min(var(--bitfun-agent-companion-pet-height, 96px), 100vh); - max-width: var(--bitfun-agent-companion-pet-width, 96px); - max-height: var(--bitfun-agent-companion-pet-height, 96px); + width: min(var(--bitfun-agent-companion-pet-width), 100vw); + height: min(var(--bitfun-agent-companion-pet-height), 100vh); + max-width: var(--bitfun-agent-companion-pet-width); + max-height: var(--bitfun-agent-companion-pet-height); transform-origin: center bottom; } &__pet-hitbox { position: relative; flex-shrink: 0; - width: min(var(--bitfun-agent-companion-pet-width, 96px), 100vw); - height: min(var(--bitfun-agent-companion-pet-height, 96px), 100vh); + width: min(var(--bitfun-agent-companion-pet-width), 100vw); + height: min(var(--bitfun-agent-companion-pet-height), 100vh); display: grid; place-items: center; cursor: grab; @@ -74,7 +78,7 @@ scrollbar-width: thin; &--single { - height: min(var(--bitfun-agent-companion-pet-height, 96px), 100vh); + height: min(var(--bitfun-agent-companion-pet-height), 100vh); overflow-y: hidden; } @@ -113,7 +117,7 @@ } &--single { - height: min(var(--bitfun-agent-companion-pet-height, 96px), 100vh); + height: min(var(--bitfun-agent-companion-pet-height), 100vh); display: flex; flex-direction: column; overflow: hidden; @@ -195,8 +199,8 @@ } .bitfun-chat-input-pixel-pet { - --bitfun-petdex-width: min(var(--bitfun-agent-companion-pet-width, 96px), 100vw); - --bitfun-petdex-height: min(var(--bitfun-agent-companion-pet-height, 96px), 100vh); + --bitfun-petdex-width: min(var(--bitfun-agent-companion-pet-width), 100vw); + --bitfun-petdex-height: min(var(--bitfun-agent-companion-pet-height), 100vh); --bitfun-petdex-margin-top: 0; pointer-events: none; diff --git a/src/web-ui/src/app/components/NavPanel/NavPanel.scss b/src/web-ui/src/app/components/NavPanel/NavPanel.scss index 1fb2efa237..d0c7f6f626 100644 --- a/src/web-ui/src/app/components/NavPanel/NavPanel.scss +++ b/src/web-ui/src/app/components/NavPanel/NavPanel.scss @@ -38,10 +38,6 @@ $_section-header-height: 24px; background: var(--color-bg-primary); overflow: visible; user-select: none; - --bitfun-nav-row-action-size: 20px; - --bitfun-nav-row-action-icon-size: 13px; - --bitfun-nav-row-action-offset: 4px; - --bitfun-nav-row-action-gap: 4px; // ── Container ────────────────────────────── &__content { diff --git a/src/web-ui/src/app/components/NavPanel/NavPanelLayout.test.ts b/src/web-ui/src/app/components/NavPanel/NavPanelLayout.test.ts index cedb2d554f..3d1cfaa674 100644 --- a/src/web-ui/src/app/components/NavPanel/NavPanelLayout.test.ts +++ b/src/web-ui/src/app/components/NavPanel/NavPanelLayout.test.ts @@ -10,6 +10,14 @@ function readNavPanelStylesheet(): string { return stylesheet.replace(/\r\n/g, '\n'); } +function readDesignTokensStylesheet(): string { + const stylesheet = readFileSync( + fileURLToPath(new URL('../../../component-library/styles/tokens.scss', import.meta.url)), + 'utf8', + ); + return stylesheet.replace(/\r\n/g, '\n'); +} + function extractBlock(stylesheet: string, selector: string): string { const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const match = stylesheet.match(new RegExp(`${escapedSelector}\\s*\\{(?[\\s\\S]*?)\\n\\s*\\}`)); @@ -59,14 +67,14 @@ describe('NavPanel layout styles', () => { it('uses one shared row-action size for root action buttons', () => { const stylesheet = readNavPanelStylesheet(); - const rootBlock = extractBlock(stylesheet, '.bitfun-nav-panel'); + const tokenStylesheet = readDesignTokensStylesheet(); const sectionActionBlock = extractBlock(stylesheet, '&__section-action'); const itemActionBlock = extractBlock(stylesheet, '&__item-action'); - expect(rootBlock).toContain('--bitfun-nav-row-action-size: 20px;'); - expect(rootBlock).toContain('--bitfun-nav-row-action-icon-size: 13px;'); - expect(rootBlock).toContain('--bitfun-nav-row-action-offset: 4px;'); - expect(rootBlock).toContain('--bitfun-nav-row-action-gap: 4px;'); + expect(tokenStylesheet).toContain('--bitfun-nav-row-action-size: 20px;'); + expect(tokenStylesheet).toContain('--bitfun-nav-row-action-icon-size: 13px;'); + expect(tokenStylesheet).toContain('--bitfun-nav-row-action-offset: 4px;'); + expect(tokenStylesheet).toContain('--bitfun-nav-row-action-gap: 4px;'); for (const block of [sectionActionBlock, itemActionBlock]) { expect(block).toContain('width: var(--bitfun-nav-row-action-size);'); expect(block).toContain('height: var(--bitfun-nav-row-action-size);'); diff --git a/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceSessionBatchModal.scss b/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceSessionBatchModal.scss index 7cb528abf3..add98dd884 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceSessionBatchModal.scss +++ b/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceSessionBatchModal.scss @@ -103,7 +103,7 @@ gap: 10px; width: 100%; min-width: 0; - min-height: var(--button-height-sm, 32px); + min-height: var(--button-height-sm); flex-wrap: nowrap; } diff --git a/src/web-ui/src/app/components/SplashScreen/SplashScreen.scss b/src/web-ui/src/app/components/SplashScreen/SplashScreen.scss index 26fc92f024..4d957daa20 100644 --- a/src/web-ui/src/app/components/SplashScreen/SplashScreen.scss +++ b/src/web-ui/src/app/components/SplashScreen/SplashScreen.scss @@ -16,7 +16,7 @@ display: flex; align-items: center; justify-content: center; - background: var(--bitfun-startup-bg, var(--color-bg-primary)); + background: var(--bitfun-startup-bg); pointer-events: none; // ── Exit state ───────────────────────────────────────────────────────────── diff --git a/src/web-ui/src/app/components/panels/content-canvas/mission-control/MissionControl.scss b/src/web-ui/src/app/components/panels/content-canvas/mission-control/MissionControl.scss index 06ce19ef19..b2f9198b36 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/mission-control/MissionControl.scss +++ b/src/web-ui/src/app/components/panels/content-canvas/mission-control/MissionControl.scss @@ -96,7 +96,7 @@ transition: all 0.15s ease; &:hover { - background: var(--element-bg-soft, var(--element-bg-base)); + background: var(--element-bg-soft); color: var(--color-text-primary); } } @@ -126,7 +126,7 @@ gap: 4px; flex-shrink: 0; padding: 2px; - background: var(--element-bg-subtle, var(--color-bg-secondary)); + background: var(--element-bg-subtle); border: 1px solid var(--border-base); border-radius: 4px; } @@ -149,14 +149,14 @@ transition: all 0.15s ease; &:hover { - background: var(--element-bg-soft, var(--element-bg-base)); + background: var(--element-bg-soft); color: var(--color-text-secondary); } &.is-active { background: var(--group-color, var(--color-accent-500)); color: var(--color-text-primary); - box-shadow: 0 1px 3px var(--color-shadow, var(--color-overlay-black-20)); + box-shadow: 0 1px 3px var(--color-overlay-black-20); .canvas-mission-control__group-filter-indicator { opacity: 0; @@ -201,11 +201,11 @@ } &::-webkit-scrollbar-thumb { - background: var(--color-scrollbar, var(--element-bg-medium)); + background: var(--color-scrollbar); border-radius: 3px; &:hover { - background: var(--element-bg-strong, var(--element-bg-medium)); + background: var(--element-bg-strong); } } } diff --git a/src/web-ui/src/app/components/panels/content-canvas/mission-control/SearchFilter.scss b/src/web-ui/src/app/components/panels/content-canvas/mission-control/SearchFilter.scss index 5cce85c53f..7e5e625805 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/mission-control/SearchFilter.scss +++ b/src/web-ui/src/app/components/panels/content-canvas/mission-control/SearchFilter.scss @@ -15,7 +15,7 @@ border: 1px solid var(--border-base); &:hover { - border-color: var(--border-medium, var(--color-primary)); + border-color: var(--border-medium); border-style: solid; } } @@ -23,7 +23,7 @@ &.search--focused .search__wrapper { border-style: solid; border-color: var(--color-accent-500); - background: var(--color-accent-50, var(--color-accent-100)); + background: var(--color-accent-50); } } diff --git a/src/web-ui/src/app/components/panels/content-canvas/mission-control/ThumbnailCard.scss b/src/web-ui/src/app/components/panels/content-canvas/mission-control/ThumbnailCard.scss index 3d43bf2a6d..07922c5dc9 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/mission-control/ThumbnailCard.scss +++ b/src/web-ui/src/app/components/panels/content-canvas/mission-control/ThumbnailCard.scss @@ -10,7 +10,7 @@ min-height: 100px; background: transparent; // Default dashed border - use border-base for cross-theme visibility - border: 1px dashed var(--border-base, var(--border-medium)); + border: 1px dashed var(--border-base); border-radius: 6px; overflow: hidden; cursor: pointer; @@ -19,7 +19,7 @@ // Switch to solid border on hover &:hover { border-style: solid; - border-color: var(--border-medium, var(--border-base)); + border-color: var(--border-medium); .canvas-thumbnail-card__actions { opacity: 1; @@ -63,7 +63,7 @@ align-items: center; gap: 6px; padding: 6px 8px; - border-bottom: 1px dashed var(--border-base, var(--border-medium)); + border-bottom: 1px dashed var(--border-base); flex-shrink: 0; } @@ -149,7 +149,7 @@ min-height: 50px; padding: 8px; overflow: hidden; - background: var(--element-bg-subtle, var(--color-bg-secondary)); + background: var(--element-bg-subtle); border-radius: 0 0 5px 5px; } @@ -237,12 +237,12 @@ font-size: 9px; font-weight: 600; color: var(--color-text-primary); - background: var(--group-color, var(--color-accent-500, var(--color-accent-700))); + background: var(--group-color, var(--color-accent-500)); border-radius: 3px; z-index: 1; - box-shadow: 0 1px 3px var(--color-shadow, var(--color-overlay-black-30)); + box-shadow: 0 1px 3px var(--color-overlay-black-30); backdrop-filter: blur(4px); - text-shadow: 0 1px 2px var(--color-shadow, var(--color-overlay-black-20)); + text-shadow: 0 1px 2px var(--color-overlay-black-20); &--primary { background: var(--group-color, var(--color-accent-500)); diff --git a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/TabOverflowMenu.scss b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/TabOverflowMenu.scss index 126748beda..1cad464477 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/TabOverflowMenu.scss +++ b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/TabOverflowMenu.scss @@ -80,7 +80,7 @@ background: var(--color-bg-elevated); border: 1px solid var(--border-base); border-radius: 6px; - box-shadow: 0 8px 24px var(--color-shadow, var(--color-overlay-black-50)); + box-shadow: 0 8px 24px var(--color-overlay-black-50); min-width: 200px; max-width: 280px; max-height: 360px; @@ -111,7 +111,7 @@ &:hover { // Use accent-200 for clear hover in light/dark themes - background: var(--color-accent-200, var(--color-accent-100, var(--element-bg-medium))); + background: var(--color-accent-200); } span { @@ -157,7 +157,7 @@ } &::-webkit-scrollbar-thumb { - background: var(--color-scrollbar, var(--color-overlay-white-15)); + background: var(--color-scrollbar); border-radius: 3px; } } @@ -183,7 +183,7 @@ &.is-active { // Use accent-100 as active background - background: var(--color-accent-100, var(--color-accent-200, var(--element-bg-base))); + background: var(--color-accent-100); .canvas-tab-overflow-menu__item-title { color: var(--color-primary); diff --git a/src/web-ui/src/app/layout/WorkspaceBody.scss b/src/web-ui/src/app/layout/WorkspaceBody.scss index c8a5e71392..246ef1a5b0 100644 --- a/src/web-ui/src/app/layout/WorkspaceBody.scss +++ b/src/web-ui/src/app/layout/WorkspaceBody.scss @@ -14,6 +14,8 @@ $_nav-collapsed-width: 80px; // ── Root: left-right split ─────────────────────────────── .bitfun-workspace-body { + --nav-width: #{$_nav-width}; + display: flex; flex-direction: row; flex: 1; @@ -33,7 +35,7 @@ $_nav-collapsed-width: 80px; .bitfun-workspace-body__nav-area { display: flex; flex-direction: column; - width: var(--nav-width, $_nav-width); + width: var(--nav-width); flex-shrink: 0; height: 100%; overflow: visible; @@ -74,7 +76,7 @@ $_nav-collapsed-width: 80px; .bitfun-workspace-body__nav-divider { position: absolute; top: 0; - left: calc(#{$size-gap-2} + var(--nav-width, #{$_nav-width})); + left: calc(#{$size-gap-2} + var(--nav-width)); width: 8px; height: 100%; cursor: ew-resize; @@ -150,14 +152,14 @@ $_nav-collapsed-width: 80px; opacity: 0; } to { - width: var(--nav-width, $_nav-width); + width: var(--nav-width); opacity: 1; } } @keyframes wb-nav-slide-out { from { - width: var(--nav-width, $_nav-width); + width: var(--nav-width); opacity: 1; } to { diff --git a/src/web-ui/src/app/scenes/agents/components/AgentCard.scss b/src/web-ui/src/app/scenes/agents/components/AgentCard.scss index af7abb08f6..c177a44dc5 100644 --- a/src/web-ui/src/app/scenes/agents/components/AgentCard.scss +++ b/src/web-ui/src/app/scenes/agents/components/AgentCard.scss @@ -365,7 +365,7 @@ &.is-active { border-color: color-mix(in srgb, var(--border-subtle) 88%, transparent); - border-bottom-color: var(--color-surface-base, var(--surface-base, transparent)); + border-bottom-color: transparent; background: color-mix(in srgb, var(--element-bg-subtle) 96%, transparent); color: var(--color-accent-500); box-shadow: diff --git a/src/web-ui/src/app/scenes/agents/components/CoreAgentCard.scss b/src/web-ui/src/app/scenes/agents/components/CoreAgentCard.scss index f949debfe3..3ef4900f9a 100644 --- a/src/web-ui/src/app/scenes/agents/components/CoreAgentCard.scss +++ b/src/web-ui/src/app/scenes/agents/components/CoreAgentCard.scss @@ -2,9 +2,11 @@ @use './AgentSurfaceCard' as surface; .core-agent-card { + --core-accent: var(--color-accent-500); + @include surface.agent-surface-card( var(--core-card-gradient), - var(--core-accent, var(--color-accent-500)) + var(--core-accent) ); // ── Header ──────────────────────────────────────────────────────── @@ -29,7 +31,7 @@ display: flex; align-items: center; justify-content: center; - color: var(--core-accent, var(--color-accent-500)); + color: var(--core-accent); transition: transform $motion-fast $easing-decelerate; } @@ -58,7 +60,7 @@ gap: 4px; font-size: var(--font-size-xs); font-weight: $font-weight-medium; - color: var(--core-accent, var(--color-accent-500)); + color: var(--core-accent); opacity: 0.9; } @@ -110,7 +112,7 @@ strong { font-weight: $font-weight-semibold; - color: var(--core-accent, var(--color-accent-500)); + color: var(--core-accent); opacity: 0.9; } } diff --git a/src/web-ui/src/app/scenes/agents/components/CreateAgentPage.scss b/src/web-ui/src/app/scenes/agents/components/CreateAgentPage.scss index 2be2b310a9..df5b715340 100644 --- a/src/web-ui/src/app/scenes/agents/components/CreateAgentPage.scss +++ b/src/web-ui/src/app/scenes/agents/components/CreateAgentPage.scss @@ -126,7 +126,7 @@ &.is-active { border-color: var(--color-primary); - background: rgba(var(--color-primary-rgb, 99 102 241) / 0.08); + background: rgba(var(--color-primary-rgb) / 0.08); color: var(--color-text-primary); font-weight: $font-weight-semibold; } @@ -166,7 +166,7 @@ &.is-on { border: 1px solid var(--color-primary); - background: rgba(var(--color-primary-rgb, 99 102 241) / 0.08); + background: rgba(var(--color-primary-rgb) / 0.08); color: var(--color-text-primary); } } diff --git a/src/web-ui/src/app/scenes/git/GitScene.scss b/src/web-ui/src/app/scenes/git/GitScene.scss index 29968f1aa8..cc70a89e93 100644 --- a/src/web-ui/src/app/scenes/git/GitScene.scss +++ b/src/web-ui/src/app/scenes/git/GitScene.scss @@ -91,9 +91,9 @@ align-items: center; gap: 6px; padding: 8px 16px; - border: 1px solid var(--btn-primary-border, var(--border-medium)); + border: 1px solid var(--btn-primary-border); border-radius: 8px; - background: var(--btn-primary-bg, var(--element-bg-medium)); + background: var(--btn-primary-bg); color: var(--btn-primary-color); box-shadow: var(--btn-primary-shadow); font-size: 12px; diff --git a/src/web-ui/src/app/scenes/miniapps/MiniAppScene.scss b/src/web-ui/src/app/scenes/miniapps/MiniAppScene.scss index d947f3b388..6e729da3bc 100644 --- a/src/web-ui/src/app/scenes/miniapps/MiniAppScene.scss +++ b/src/web-ui/src/app/scenes/miniapps/MiniAppScene.scss @@ -74,7 +74,7 @@ overflow: hidden; border: 1px solid var(--border-subtle); border-radius: $size-radius-lg; - background: var(--color-bg-elevated, var(--color-bg-primary)); + background: var(--color-bg-elevated); box-shadow: 0 24px 70px rgba(0, 0, 0, 0.34), 0 0 0 1px color-mix(in srgb, var(--color-static-white) 4%, transparent); @@ -175,7 +175,7 @@ gap: $size-gap-2; max-width: min(520px, calc(100% - #{$size-gap-6})); padding: $size-gap-2 $size-gap-3; - border: 1px solid var(--color-success-border, var(--border-subtle)); + border: 1px solid var(--color-success-border); border-radius: $size-radius-base; background: var(--color-bg-surface); color: var(--color-text-primary); @@ -202,7 +202,7 @@ overflow: hidden; border: 1px solid var(--border-subtle); border-radius: $size-radius-lg; - background: var(--color-bg-elevated, var(--color-bg-primary)); + background: var(--color-bg-elevated); color: var(--color-text-primary); box-shadow: 0 24px 70px rgba(0, 0, 0, 0.36), @@ -248,7 +248,7 @@ gap: $size-gap-2; padding: $size-gap-3; border-bottom: 1px solid var(--border-subtle); - background: var(--color-warning-bg, var(--color-bg-subtle)); + background: var(--color-warning-bg); svg { flex: 0 0 auto; @@ -308,7 +308,7 @@ line-height: $line-height-relaxed; &:focus { - outline: 2px solid var(--color-primary-border, var(--color-primary)); + outline: 2px solid var(--color-primary); outline-offset: 1px; } } @@ -335,15 +335,15 @@ } &__error { - border: 1px solid var(--color-danger-border, var(--border-subtle)); - background: var(--color-danger-bg, var(--color-bg-subtle)); + border: 1px solid var(--color-danger-border); + background: var(--color-danger-bg); color: var(--color-danger); } &__status { gap: $size-gap-2; - border: 1px solid var(--color-success-border, var(--border-subtle)); - background: var(--color-success-bg, var(--color-bg-subtle)); + border: 1px solid var(--color-success-border); + background: var(--color-success-bg); color: var(--color-text-secondary); svg { @@ -359,12 +359,12 @@ overflow: hidden; border: 1px solid var(--border-subtle); border-radius: $size-radius-base; - background: var(--color-bg-flowchat, var(--color-bg-primary)); + background: var(--color-bg-flowchat); .btw-session-panel { min-height: 0; width: 100%; - background: var(--color-bg-flowchat, var(--color-bg-primary)); + background: var(--color-bg-flowchat); } .btw-session-panel__header-right { diff --git a/src/web-ui/src/app/scenes/my-agent/InsightsScene.scss b/src/web-ui/src/app/scenes/my-agent/InsightsScene.scss index 8685afdd63..bcc0a7f210 100644 --- a/src/web-ui/src/app/scenes/my-agent/InsightsScene.scss +++ b/src/web-ui/src/app/scenes/my-agent/InsightsScene.scss @@ -173,7 +173,7 @@ $ins-label: 12px; @extend %ins-btn; background: $ins-red-bg; color: $ins-red; - &:hover { background: var(--color-error-border, $ins-red-bg); } + &:hover { background: var(--color-error-border); } } // Error / progress @@ -1405,7 +1405,7 @@ $ins-label: 12px; &:hover { border-color: color-mix(in srgb, $ins-blue 45%, transparent); - background: var(--color-accent-200, $ins-blue-bg); + background: var(--color-accent-200); box-shadow: var(--shadow-sm); } } diff --git a/src/web-ui/src/app/scenes/profile/views/AssistantQuickInput.scss b/src/web-ui/src/app/scenes/profile/views/AssistantQuickInput.scss index 3ea41a465e..d50679f687 100644 --- a/src/web-ui/src/app/scenes/profile/views/AssistantQuickInput.scss +++ b/src/web-ui/src/app/scenes/profile/views/AssistantQuickInput.scss @@ -19,7 +19,7 @@ &:hover:not(:has(.bitfun-textarea__field:disabled)) { transform: translateY(-2px); - border-color: var(--border-strong, var(--border-medium)); + border-color: var(--border-strong); background: color-mix(in srgb, var(--element-bg-base) 90%, transparent); box-shadow: 0 20px 48px -14px color-mix(in srgb, var(--color-text-primary) 14%, transparent), @@ -28,7 +28,7 @@ &:focus-within { transform: translateY(-2px); - border-color: var(--border-focus, var(--color-accent-500)); + border-color: var(--border-focus); background: var(--element-bg-soft); box-shadow: 0 22px 52px -12px color-mix(in srgb, var(--color-accent-500) 18%, transparent), diff --git a/src/web-ui/src/app/scenes/profile/views/NurseryView.scss b/src/web-ui/src/app/scenes/profile/views/NurseryView.scss index 642d354b32..a25a5a68b7 100644 --- a/src/web-ui/src/app/scenes/profile/views/NurseryView.scss +++ b/src/web-ui/src/app/scenes/profile/views/NurseryView.scss @@ -572,7 +572,7 @@ $nursery-scene-gutter: clamp(40px, 6vw, 80px); .nursery-delete-dialog { pointer-events: auto; - background: var(--color-bg-elevated, var(--color-bg-secondary)); + background: var(--color-bg-elevated); border: 1px solid var(--border-subtle); border-radius: 14px; padding: 24px; diff --git a/src/web-ui/src/app/scenes/settings/components/KeyboardShortcutsTab.scss b/src/web-ui/src/app/scenes/settings/components/KeyboardShortcutsTab.scss index aaad80917e..626274e07c 100644 --- a/src/web-ui/src/app/scenes/settings/components/KeyboardShortcutsTab.scss +++ b/src/web-ui/src/app/scenes/settings/components/KeyboardShortcutsTab.scss @@ -19,7 +19,7 @@ border-radius: 6px; /* Match Search defaults; --color-border alone can be undefined and drop the border */ border: 1px solid var(--border-base); - background: var(--color-bg-input, var(--color-surface)); + background: var(--input-bg); transition: border-color 0.15s ease, box-shadow 0.15s ease; } @@ -53,7 +53,7 @@ padding: 8px 12px; margin-bottom: 16px; border-radius: 6px; - background: var(--color-error-surface, var(--color-error-bg)); + background: var(--color-error-bg); color: var(--color-error); font-size: 13px; } @@ -147,7 +147,7 @@ padding: 3px 8px; border-radius: 4px; border: 1px solid var(--color-border); - background: var(--color-surface); + background: var(--element-bg-subtle); color: var(--color-text-secondary); font-family: ui-monospace, 'Cascadia Code', 'Fira Code', monospace; font-size: 12px; diff --git a/src/web-ui/src/app/scenes/shell/ShellNav.scss b/src/web-ui/src/app/scenes/shell/ShellNav.scss index e79b1dc6a4..3ef8e4f254 100644 --- a/src/web-ui/src/app/scenes/shell/ShellNav.scss +++ b/src/web-ui/src/app/scenes/shell/ShellNav.scss @@ -97,7 +97,7 @@ padding: 6px; border: 1px solid var(--border-subtle); border-radius: 10px; - background: var(--panel-bg, var(--color-bg-elevated)); + background: var(--panel-bg); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.24); z-index: 8; } @@ -209,7 +209,7 @@ padding: 6px; border: 1px solid var(--border-subtle); border-radius: 10px; - background: var(--panel-bg, var(--color-bg-elevated)); + background: var(--panel-bg); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.24); z-index: 8; } diff --git a/src/web-ui/src/app/scenes/skills/SkillsScene.scss b/src/web-ui/src/app/scenes/skills/SkillsScene.scss index cd003eae79..09fc4b19bf 100644 --- a/src/web-ui/src/app/scenes/skills/SkillsScene.scss +++ b/src/web-ui/src/app/scenes/skills/SkillsScene.scss @@ -303,7 +303,7 @@ border: 1px solid color-mix(in srgb, var(--color-accent-500) 20%, var(--border-subtle)); border-radius: $size-radius-full; background: color-mix(in srgb, var(--color-accent-500) 8%, transparent); - color: var(--color-accent-600, var(--color-text-secondary)); + color: var(--color-accent-600); font-size: var(--font-size-xs); font-weight: $font-weight-medium; cursor: pointer; @@ -316,7 +316,7 @@ &:hover { background: color-mix(in srgb, var(--color-accent-500) 14%, transparent); border-color: color-mix(in srgb, var(--color-accent-500) 35%, var(--border-medium)); - color: var(--color-accent-600, var(--color-text-primary)); + color: var(--color-accent-600); } &:focus-visible { diff --git a/src/web-ui/src/app/scenes/welcome/WelcomeScene.scss b/src/web-ui/src/app/scenes/welcome/WelcomeScene.scss index 36a08ed340..7c43662f2a 100644 --- a/src/web-ui/src/app/scenes/welcome/WelcomeScene.scss +++ b/src/web-ui/src/app/scenes/welcome/WelcomeScene.scss @@ -151,7 +151,7 @@ } &:hover .welcome-scene__recent-time-btn:hover { - background: var(--element-bg-muted); + background: var(--element-bg-subtle); .welcome-scene__recent-time-btn__icon svg { transform: scale(1.12); @@ -227,7 +227,7 @@ &:focus-visible { color: var(--color-text-primary); - outline: 2px solid var(--focus-ring-color, var(--color-accent)); + outline: 2px solid var(--color-accent); outline-offset: -2px; z-index: 1; } diff --git a/src/web-ui/src/app/styles/global.scss b/src/web-ui/src/app/styles/global.scss index 87c23ff536..9679f7c04b 100644 --- a/src/web-ui/src/app/styles/global.scss +++ b/src/web-ui/src/app/styles/global.scss @@ -33,8 +33,8 @@ html { } body { - font-family: var(--font-family-sans, #{$font-family-sans}); - font-size: var(--font-size-base, #{$font-size-base}); + font-family: var(--font-family-sans); + font-size: var(--font-size-base); line-height: $line-height-base; color: var(--color-text-primary); background: var(--color-bg-primary); diff --git a/src/web-ui/src/component-library/components/Badge/Badge.scss b/src/web-ui/src/component-library/components/Badge/Badge.scss index 8f93dd61e5..a68adb957f 100644 --- a/src/web-ui/src/component-library/components/Badge/Badge.scss +++ b/src/web-ui/src/component-library/components/Badge/Badge.scss @@ -11,7 +11,7 @@ gap: 3px; padding: tokens.$badge-padding-y tokens.$badge-padding-x; border-radius: tokens.$badge-border-radius; - font-size: var(--badge-font-size, #{tokens.$badge-font-size}); + font-size: var(--badge-font-size); font-weight: tokens.$badge-font-weight; line-height: 1; border: none; diff --git a/src/web-ui/src/component-library/components/Button/Button.scss b/src/web-ui/src/component-library/components/Button/Button.scss index 6c6af2708c..6e5431035c 100644 --- a/src/web-ui/src/component-library/components/Button/Button.scss +++ b/src/web-ui/src/component-library/components/Button/Button.scss @@ -27,21 +27,21 @@ } &-sm { - height: var(--button-height-sm, 32px); + height: var(--button-height-sm); padding: 0 var(--size-gap-3); font-size: var(--font-size-sm); } &-base { - height: var(--button-height-base, 40px); + height: var(--button-height-base); padding: 0 var(--size-gap-4); - font-size: var(--font-size-base, 15px); + font-size: var(--font-size-base); } &-lg { - height: var(--button-height-lg, 48px); + height: var(--button-height-lg); padding: 0 var(--size-gap-6); - font-size: var(--font-size-lg, 16px); + font-size: var(--font-size-lg); } &:hover:not(:disabled) { @@ -74,7 +74,7 @@ &:focus-visible { outline-color: color-mix( in srgb, - var(--btn-primary-color, var(--color-accent-500)) 55%, + var(--btn-primary-color) 55%, transparent ); } @@ -128,15 +128,15 @@ &-icon-only { padding: 0; - width: var(--button-height-base, 40px); + width: var(--button-height-base); aspect-ratio: 1; &.btn-sm { - width: var(--button-height-sm, 32px); + width: var(--button-height-sm); } &.btn-lg { - width: var(--button-height-lg, 48px); + width: var(--button-height-lg); } } diff --git a/src/web-ui/src/component-library/components/Card/Card.scss b/src/web-ui/src/component-library/components/Card/Card.scss index a0dd3352ce..9514784ac4 100644 --- a/src/web-ui/src/component-library/components/Card/Card.scss +++ b/src/web-ui/src/component-library/components/Card/Card.scss @@ -7,7 +7,7 @@ background: var(--card-bg-default); border: none; - border-radius: var(--size-radius-base, 8px); + border-radius: var(--size-radius-base); color: var(--color-text-secondary); transition: all var(--motion-base) var(--easing-standard); @@ -93,11 +93,11 @@ } &--radius-medium { - border-radius: var(--size-radius-base, 8px); + border-radius: var(--size-radius-base); } &--radius-large { - border-radius: var(--size-radius-lg, 12px); + border-radius: var(--size-radius-lg); } @@ -121,14 +121,14 @@ font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); - line-height: var(--line-height-tight, 1.2); + line-height: var(--line-height-tight); margin-bottom: var(--size-gap-1); } &__subtitle { font-size: var(--font-size-xs); color: var(--color-text-muted); - line-height: var(--line-height-base, 1.5); + line-height: var(--line-height-base); } &__extra { @@ -143,7 +143,7 @@ flex: 1; color: var(--color-text-secondary); font-size: var(--font-size-sm); - line-height: var(--line-height-relaxed, 1.6); + line-height: var(--line-height-relaxed); .v-card-header + & { margin-top: var(--size-gap-3); diff --git a/src/web-ui/src/component-library/components/DotMatrixLoader/DotMatrixLoader.scss b/src/web-ui/src/component-library/components/DotMatrixLoader/DotMatrixLoader.scss index d15bb8bd14..306bb651cd 100644 --- a/src/web-ui/src/component-library/components/DotMatrixLoader/DotMatrixLoader.scss +++ b/src/web-ui/src/component-library/components/DotMatrixLoader/DotMatrixLoader.scss @@ -34,13 +34,16 @@ $matrix-step: math.div(100%, $matrix-phase-count); $matrix-epsilon: 0.0001%; .dot-matrix-loader { + --dm-cell: 6px; + --dm-gap: 2px; + flex-shrink: 0; display: grid; - grid-template-columns: repeat(3, var(--dm-cell, 6px)); - grid-template-rows: repeat(3, var(--dm-cell, 6px)); - gap: var(--dm-gap, 2px); - width: calc(3 * var(--dm-cell, 6px) + 2 * var(--dm-gap, 2px)); - height: calc(3 * var(--dm-cell, 6px) + 2 * var(--dm-gap, 2px)); + grid-template-columns: repeat(3, var(--dm-cell)); + grid-template-rows: repeat(3, var(--dm-cell)); + gap: var(--dm-gap); + width: calc(3 * var(--dm-cell) + 2 * var(--dm-gap)); + height: calc(3 * var(--dm-cell) + 2 * var(--dm-gap)); color: var(--text-secondary); &--small { @@ -60,8 +63,8 @@ $matrix-epsilon: 0.0001%; } .dot-matrix-loader__dot { - width: var(--dm-cell, 6px); - height: var(--dm-cell, 6px); + width: var(--dm-cell); + height: var(--dm-cell); border-radius: 1px; background-color: currentColor; animation-duration: $matrix-period; diff --git a/src/web-ui/src/component-library/components/FilterPill/FilterPill.scss b/src/web-ui/src/component-library/components/FilterPill/FilterPill.scss index 8138335520..d278ab18b2 100644 --- a/src/web-ui/src/component-library/components/FilterPill/FilterPill.scss +++ b/src/web-ui/src/component-library/components/FilterPill/FilterPill.scss @@ -31,17 +31,17 @@ } &--active { - background: var(--git-color-branch-bg, #{$git-color-branch-bg}); + background: var(--git-color-branch-bg); border-color: transparent; - color: var(--git-color-branch, #{$git-color-branch}); + color: var(--git-color-branch); .v-filter-pill__count { - background: color-mix(in srgb, var(--git-color-branch, #{$git-color-branch}) 22%, transparent); - color: var(--git-color-branch, #{$git-color-branch}); + background: color-mix(in srgb, var(--git-color-branch) 22%, transparent); + color: var(--git-color-branch); } &:hover:not(:disabled) { - background: var(--git-color-branch-bg-hover, #{$git-color-branch-bg-hover}); + background: var(--git-color-branch-bg-hover); } } diff --git a/src/web-ui/src/component-library/components/Markdown/Markdown.scss b/src/web-ui/src/component-library/components/Markdown/Markdown.scss index 67b1418190..0665f2d0ba 100644 --- a/src/web-ui/src/component-library/components/Markdown/Markdown.scss +++ b/src/web-ui/src/component-library/components/Markdown/Markdown.scss @@ -2,10 +2,9 @@ .markdown-renderer { --markdown-font-sans: var(--font-family-sans); - --markdown-font-mono: var(--font-family-mono, "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace); --markdown-font-heading: var(--markdown-font-sans); --markdown-font-size: var(--flowchat-font-size-base); - --markdown-font-size-sm: var(--flowchat-font-size-sm, var(--font-size-sm, 13px)); + --markdown-font-size-sm: var(--flowchat-font-size-sm); --markdown-code-font-size: var(--flowchat-code-font-size); --markdown-line-height: var(--flowchat-text-line-height); --markdown-support-line-height: var(--flowchat-support-line-height); @@ -33,6 +32,7 @@ --markdown-table-header-bg: var(--flowchat-md-table-th-bg, var(--color-overlay-white-03)); --markdown-table-row-stripe: var(--flowchat-md-table-row-stripe, var(--color-overlay-white-02)); --markdown-table-row-hover: var(--flowchat-md-table-hover, var(--color-overlay-white-04)); + --markdown-primary-color: var(--primary-color, var(--color-accent-600)); --markdown-link-color: var(--flowchat-link-color); --markdown-link-hover-color: var(--flowchat-link-hover-color); @@ -49,7 +49,7 @@ word-wrap: break-word; cursor: text; user-select: text; - animation: fadeIn var(--motion-normal, 0.2s) ease-out; + animation: fadeIn var(--motion-normal) ease-out; } .markdown-renderer > * { @@ -232,7 +232,7 @@ } .markdown-renderer pre:hover { - border-color: color-mix(in srgb, var(--markdown-pre-border) 72%, var(--primary-color, var(--color-accent-600)) 28%); + border-color: color-mix(in srgb, var(--markdown-pre-border) 72%, var(--markdown-primary-color) 28%); } .markdown-renderer pre code, @@ -272,7 +272,7 @@ } .markdown-renderer .code-block-wrapper:hover { - border-color: color-mix(in srgb, var(--markdown-pre-border) 72%, var(--primary-color, var(--color-accent-600)) 28%); + border-color: color-mix(in srgb, var(--markdown-pre-border) 72%, var(--markdown-primary-color) 28%); } .markdown-renderer .code-block-toolbar { @@ -349,7 +349,7 @@ .markdown-renderer .copy-button:hover { background: var(--color-overlay-white-06); - color: var(--primary-color, var(--color-accent-600)); + color: var(--markdown-primary-color); } .markdown-renderer .copy-button:active { diff --git a/src/web-ui/src/component-library/components/Markdown/Markdown.tsx b/src/web-ui/src/component-library/components/Markdown/Markdown.tsx index e64d86f551..ef8c5d4116 100644 --- a/src/web-ui/src/component-library/components/Markdown/Markdown.tsx +++ b/src/web-ui/src/component-library/components/Markdown/Markdown.tsx @@ -1061,7 +1061,7 @@ export const Markdown = React.memo(({ lineHeight: '1.55', }; const codeTagStyle: React.CSSProperties = { - fontFamily: 'var(--markdown-font-mono, "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace)', + fontFamily: 'var(--markdown-font-mono)', }; return ( diff --git a/src/web-ui/src/component-library/components/Markdown/MermaidBlock.scss b/src/web-ui/src/component-library/components/Markdown/MermaidBlock.scss index 685edd275e..4c40334fd8 100644 --- a/src/web-ui/src/component-library/components/Markdown/MermaidBlock.scss +++ b/src/web-ui/src/component-library/components/Markdown/MermaidBlock.scss @@ -175,8 +175,8 @@ background: transparent; border: none; border-radius: 0; - font-family: var(--font-family-mono, 'Consolas', 'Monaco', 'Courier New', monospace); - font-size: var(--flowchat-code-font-size, 0.92em); + font-family: var(--font-family-mono); + font-size: var(--flowchat-code-font-size); line-height: var(--flowchat-code-line-height); color: var(--color-text-primary); overflow-x: auto; diff --git a/src/web-ui/src/component-library/components/Markdown/markdownPrismTheme.ts b/src/web-ui/src/component-library/components/Markdown/markdownPrismTheme.ts index 542d53c90e..7205e35995 100644 --- a/src/web-ui/src/component-library/components/Markdown/markdownPrismTheme.ts +++ b/src/web-ui/src/component-library/components/Markdown/markdownPrismTheme.ts @@ -16,14 +16,14 @@ export function buildMarkdownPrismStyle(isLight: boolean): Record void; className?: string; + style?: React.CSSProperties; rounded?: boolean; } @@ -24,6 +25,7 @@ export const Tag: React.FC = ({ closable = false, onClose, className = '', + style, rounded = false, }) => { const classNames = [ @@ -37,7 +39,7 @@ export const Tag: React.FC = ({ .join(' '); return ( - + {children} {closable && ( - `,i.querySelector(".outline-card").addEventListener("click",()=>{o?.id&&t.selectSlide(o.id)}),r.append(i)}))}function Wn(e,t){let r=Lt("slideThumbs");if(r){if(r.innerHTML="",!e.slides.length){let n=document.createElement("div");n.className="thumbs-empty",n.textContent=U("slidesEmptyHint"),r.append(n);return}e.slides.forEach((n,a)=>{let i=n.html?Di(n.html):null,o=n.theme||{},l=i||o.background||"var(--studio-slide-chrome)",c=document.createElement("button");c.className=`thumb${n.id===e.activeSlideId?" is-active":""}`,c.type="button",c.style.setProperty("--thumb-bg",l),c.style.setProperty("--thumb-primary",o.primary||"var(--studio-accent)");let s=document.createElement("div");if(s.className="thumb-preview",s.style.background=l,n.html)s.appendChild(Vh(n.html));else{let h=document.createElement("div");h.className="thumb-preview-slide",h.innerHTML=un(n),s.appendChild(h)}c.appendChild(s);let d=document.createElement("div");d.className="thumb-copy",d.innerHTML=` - ${Le(n.kicker||"")} - ${Le(n.title)} - `,c.appendChild(d);let u=document.createElement("span");u.className="thumb-number",u.textContent=String(a+1),c.appendChild(u),c.addEventListener("click",()=>t.selectSlide(n.id)),r.append(c)}),requestAnimationFrame(()=>{Ts(),Wi()})}}var Ii=960,Mi=540;function Vh(e){let{host:t}=Fs({hostClass:"thumb-preview-html",frameClass:"thumb-preview-frame",html:e,onReady:()=>vr(t)});return t}function Xh(e,t){let r=e?.closest?.(`.${mr}`)||t?.querySelector?.(`.${mr}`);if(r){vr(r);return}if(!e||!t)return;let{width:n,height:a}=Ps(t);if(!n||!a)return;let i=Math.min(n/Ii,a/Mi);e.style.width=`${Ii}px`,e.style.height=`${Mi}px`,e.style.transform=`scale(${i})`,e.style.transformOrigin="top left"}function Hh(e){let t=e.querySelector(".thumb-preview-frame");if(t){Xh(t,e);return}let r=e.querySelector(".thumb-preview-html, .thumb-preview-slide");if(!r)return;let n=Dh(e);if(!n)return;let a=n/Ii,i=Mi*a;r.style.width=`${Ii}px`,r.style.height=`${Mi}px`,r.style.transform=`scale(${a})`,r.style.transformOrigin="top left",e.style.height=`${i}px`}function Ts(){let e=Lt("slideThumbs");e&&(e.querySelectorAll(`.${mr}`).forEach(t=>{vr(t)}),e.querySelectorAll(".thumb-preview").forEach(t=>{t.querySelector(`.${mr}`)||Hh(t)}))}var Ba=null,La=null;function Qc(){if(typeof ResizeObserver>"u")return;La||(La=new ResizeObserver(()=>{document.querySelectorAll(`.${mr}`).forEach(t=>{vr(t)})})),La.disconnect(),document.querySelectorAll(`.${mr}`).forEach(t=>{La.observe(t)});let e=Lt("slideCanvas");e&&La.observe(e)}function Wi(){let e=Lt("slideThumbs");!e||typeof ResizeObserver>"u"||(Ba||(Ba=new ResizeObserver(()=>Ts())),Ba.disconnect(),Ba.observe(e),e.querySelectorAll(".thumb-preview").forEach(t=>{Ba.observe(t)}),Qc())}function _h(e){if(!String(e.brief?.topic||"").trim()){let n=String(e.title||"").trim();if(n===U("blankDeckTitle")||n===U("defaultDeckTitle")||n===U("newSlideTitle"))return!0}if(!e.slides?.length)return!0;let t=String(e.title||"").trim();return e.slides.length===1&&e.outline.length===1&&e.outline[0]===U("newSlideTitle")&&(t===U("blankDeckTitle")||t===U("newSlideTitle"))}function Li(e,t){if(!e)return;if(!t){e.style.background="";return}let r=t.theme||{},a=(t.html?Di(t.html):null)||r.background||"";e.style.background=a||""}function dn(e,t){let r=Lt("slideCanvas");if(!r)return;let n=_t(e),a=!!(e.generation?.active||e.generation?.steps?.some(o=>o.status==="running"));if(!n){r.classList.remove("is-html-slide"),r.classList.add("is-empty"),r.innerHTML=a?`
${Le(U("generationAgentWorking"))}

${Le(U("agentWorkingDetail"))}

`:`

${Le(U("welcomeTitle"))}

${Le(U("welcomeSubcopy"))}

`,zc(r),Li(r,null),ea();return}if(_h(e)&&!n.html&&!a){r.classList.remove("is-html-slide"),r.classList.add("is-empty"),r.innerHTML=` + `,i.querySelector(".outline-card").addEventListener("click",()=>{o?.id&&t.selectSlide(o.id)}),r.append(i)}))}function Ln(e,t){let r=Et("slideThumbs");if(r){if(r.innerHTML="",!e.slides.length){let n=document.createElement("div");n.className="thumbs-empty",n.textContent=V("slidesEmptyHint"),r.append(n);return}e.slides.forEach((n,a)=>{let i=n.html?Ci(n.html):null,o=n.theme||{},l=i||o.background||"var(--studio-slide-chrome)",c=document.createElement("button");c.className=`thumb${n.id===e.activeSlideId?" is-active":""}`,c.type="button",c.style.setProperty("--thumb-bg",l),c.style.setProperty("--thumb-primary",o.primary||"var(--studio-accent)");let s=document.createElement("div");if(s.className="thumb-preview",s.style.background=l,n.html)s.appendChild(Fh(n.html));else{let f=document.createElement("div");f.className="thumb-preview-slide",f.innerHTML=dn(n),s.appendChild(f)}c.appendChild(s);let d=document.createElement("div");d.className="thumb-copy",d.innerHTML=` + ${Be(n.kicker||"")} + ${Be(n.title)} + `,c.appendChild(d);let u=document.createElement("span");u.className="thumb-number",u.textContent=String(a+1),c.appendChild(u),c.addEventListener("click",()=>t.selectSlide(n.id)),r.append(c)}),requestAnimationFrame(()=>{ys(),Ii()})}}var Bi=960,Ei=540;function Fh(e){let{host:t}=ms({hostClass:"thumb-preview-html",frameClass:"thumb-preview-frame",html:e,onReady:()=>gr(t)});return t}function Th(e,t){let r=e?.closest?.(`.${Ar}`)||t?.querySelector?.(`.${Ar}`);if(r){gr(r);return}if(!e||!t)return;let{width:n,height:a}=vs(t);if(!n||!a)return;let i=Math.min(n/Bi,a/Ei);e.style.width=`${Bi}px`,e.style.height=`${Ei}px`,e.style.transform=`scale(${i})`,e.style.transformOrigin="top left"}function Dh(e){let t=e.querySelector(".thumb-preview-frame");if(t){Th(t,e);return}let r=e.querySelector(".thumb-preview-html, .thumb-preview-slide");if(!r)return;let n=uh(e);if(!n)return;let a=n/Bi,i=Ei*a;r.style.width=`${Bi}px`,r.style.height=`${Ei}px`,r.style.transform=`scale(${a})`,r.style.transformOrigin="top left",e.style.height=`${i}px`}function ys(){let e=Et("slideThumbs");e&&(e.querySelectorAll(`.${Ar}`).forEach(t=>{gr(t)}),e.querySelectorAll(".thumb-preview").forEach(t=>{t.querySelector(`.${Ar}`)||Dh(t)}))}var Pa=null,Fa=null;function zc(){if(typeof ResizeObserver>"u")return;Fa||(Fa=new ResizeObserver(()=>{document.querySelectorAll(`.${Ar}`).forEach(t=>{gr(t)})})),Fa.disconnect(),document.querySelectorAll(`.${Ar}`).forEach(t=>{Fa.observe(t)});let e=Et("slideCanvas");e&&Fa.observe(e)}function Ii(){let e=Et("slideThumbs");!e||typeof ResizeObserver>"u"||(Pa||(Pa=new ResizeObserver(()=>ys())),Pa.disconnect(),Pa.observe(e),e.querySelectorAll(".thumb-preview").forEach(t=>{Pa.observe(t)}),zc())}function Bh(e){if(!String(e.brief?.topic||"").trim()){let n=String(e.title||"").trim();if(n===V("blankDeckTitle")||n===V("defaultDeckTitle")||n===V("newSlideTitle"))return!0}if(!e.slides?.length)return!0;let t=String(e.title||"").trim();return e.slides.length===1&&e.outline.length===1&&e.outline[0]===V("newSlideTitle")&&(t===V("blankDeckTitle")||t===V("newSlideTitle"))}function Ti(e,t){if(!e)return;if(!t){e.style.background="";return}let r=t.theme||{},a=(t.html?Ci(t.html):null)||r.background||"";e.style.background=a||""}function cn(e,t){let r=Et("slideCanvas");if(!r)return;let n=Vt(e),a=!!(e.generation?.active||e.generation?.steps?.some(o=>o.status==="running"));if(!n){r.classList.remove("is-html-slide"),r.classList.add("is-empty"),r.innerHTML=a?`
${Be(V("generationAgentWorking"))}

${Be(V("agentWorkingDetail"))}

`:`

${Be(V("welcomeTitle"))}

${Be(V("welcomeSubcopy"))}

`,Cc(r),Ti(r,null),Kn();return}if(Bh(e)&&!n.html&&!a){r.classList.remove("is-html-slide"),r.classList.add("is-empty"),r.innerHTML=`
-

${Le(U("welcomeTitle"))}

-

${Le(U("welcomeSubcopy"))}

+

${Be(V("welcomeTitle"))}

+

${Be(V("welcomeSubcopy"))}

- - - + + +
- `,zc(r),Li(r,null),ea();return}if(r.classList.remove("is-empty"),n?.html){r.innerHTML="",r.classList.add("is-html-slide");let{host:o,frame:l}=Fs({frameClass:"html-slide-frame",html:n.html,interactive:!0,onReady:c=>{Zh(c,n.id,t),ea(),vr(o),requestAnimationFrame(()=>vr(o))}});r.append(o),Li(r,n),r.classList.remove("is-entering"),r.offsetWidth,r.classList.add("is-entering"),ea();return}r.classList.remove("is-html-slide"),r.innerHTML=n?un(n,{selectedElementId:e.selectedElementId,editable:!0}):"",r.querySelectorAll(".slide-element").forEach(o=>{let l=o.dataset.elementId;o.addEventListener("click",c=>{c.stopPropagation(),t.selectElement(l)}),o.addEventListener("pointerdown",c=>{c.target?.isContentEditable&&!c.target.classList.contains("resize-handle")||t.beginDrag(c,l)})}),r.querySelectorAll("[data-edit-text]").forEach(o=>{o.addEventListener("blur",()=>{t.updateElementTextDirect(o.dataset.editText,o.textContent||"")}),o.addEventListener("keydown",l=>{(l.metaKey||l.ctrlKey)&&l.key==="Enter"&&o.blur()})}),r.querySelectorAll("[data-edit-list]").forEach(o=>{o.addEventListener("blur",()=>{t.updateElementListItemDirect(o.dataset.editList,Number(o.dataset.itemIndex),o.textContent||"")}),o.addEventListener("keydown",l=>{(l.metaKey||l.ctrlKey)&&l.key==="Enter"&&o.blur()})}),r.classList.remove("is-entering"),r.offsetWidth,r.classList.add("is-entering");let i=On(e);if(i)Eh(i);else{let o=Lt("floatingToolbar");o&&o.classList.remove("is-visible")}Li(r,n),ea()}function Gi(e,t){let r=Lt("elementInspector"),n=On(e),a=_t(e);if(!(!r||!a)){if(r.hidden){r.innerHTML="";return}if(!n){r.innerHTML=`${Ic(a)}

${U("noSelection")}

`,Mc(r,t),r.querySelector("#slideNotesInput")?.addEventListener("input",i=>t.updateSlideNotes(i.target.value));return}r.innerHTML=` - ${Ic(a)} - - - `,Sc(r,t),r.querySelector("#slideNotesInput")?.addEventListener("input",i=>t.updateSlideNotes(i.target.value));return}r.innerHTML=` + ${xc(a)} + + + -
- - - - + + + +
- - + +
- - `,["elementTextInput","elementItemsInput","elementDataInput","elementXInput","elementYInput","elementWInput","elementHInput","elementFontInput","elementWeightInput","elementColorInput","elementBgInput","slideNotesInput"].forEach(i=>r.querySelector(`#${i}`)?.addEventListener("input",()=>t.updateElementFromInspector())),Mc(r,t)}}function Ic(e){return` + + `,["elementTextInput","elementItemsInput","elementDataInput","elementXInput","elementYInput","elementWInput","elementHInput","elementFontInput","elementWeightInput","elementColorInput","elementBgInput","slideNotesInput"].forEach(i=>r.querySelector(`#${i}`)?.addEventListener("input",()=>t.updateElementFromInspector())),Sc(r,t)}}function xc(e){return`
- - - - - + + + + +
- ${Kh(e)} - `}function Kh(e){let t=Array.isArray(e.quality?.issues)?e.quality.issues:[];return t.length?` + ${Eh(e)} + `}function Eh(e){let t=Array.isArray(e.quality?.issues)?e.quality.issues:[];return t.length?`
- ${Le(U("qualityReportTitle"))}: ${Math.round(Number(e.quality?.score??100))}/100 -
    ${t.map(r=>`
  • ${Le(r.message)}
  • `).join("")}
+ ${Be(V("qualityReportTitle"))}: ${Math.round(Number(e.quality?.score??100))}/100 +
    ${t.map(r=>`
  • ${Be(r.message)}
  • `).join("")}
- `:""}function Mc(e,t){["slideKickerInput","slideClaimInput","slideProofInput","slideSupportInput","slideSourceInput"].forEach(r=>{e.querySelector(`#${r}`)?.addEventListener("input",()=>t.updateSlideMethodology())})}function un(e,t={}){if(e?.html)return``;let r=!!t.editable,n=t.selectedElementId||"",a=[`--slide-bg:${e.theme.background}`,`--slide-ink:${e.theme.ink}`,`--slide-muted:${e.theme.muted}`,`--slide-primary:${e.theme.primary}`,`--slide-accent:${e.theme.accent}`,`--slide-panel:${e.theme.panel||"#ffffff"}`].join(";");return`
- ${e.kicker?`
${Le(e.kicker)}
`:""} - ${e.proofObject?`
${Le(e.proofObject)}
`:""} - ${Qh(e)} - ${(e.elements||[]).map(i=>Jh(i,e.theme,r,n)).join("")} - ${e.sourceNote?`
${Le(e.sourceNote)}
`:""} -
`}function Qh(e){let t=Array.isArray(e.quality?.issues)?e.quality.issues:[];if(!t.length)return"";let r=t.filter(a=>a.severity==="high").length,n=r?U("qualityNeedsReview"):U("qualityHasWarnings");return`
${Le(n)}
`}function zc(e){e.querySelectorAll("[data-welcome-prompt]").forEach(t=>{t.addEventListener("click",()=>{let r=Lt("topicInput");r&&(r.value=t.dataset.welcomePrompt||t.textContent||"",r.focus())})})}function Zh(e,t,r){if(!r?.updateSlideHtmlDirect)return;let n=e?.shadowRoot,a=n?.querySelector(`.${Ss}`);if(!n||!a)return;if(!n.querySelector(`style[${Lc}]`)){let c=document.createElement("style");c.setAttribute(Lc,"true"),c.textContent=[`[${ta}] { cursor: text; -webkit-user-select: text !important; user-select: text !important; }`,`[${ta}]:hover { outline: 1.5px dashed rgba(37, 99, 235, 0.55); outline-offset: 1px; }`,`[${ta}]:focus { outline: 2px solid rgba(37, 99, 235, 0.85); outline-offset: 1px; }`].join(` -`),n.appendChild(c)}n.addEventListener("click",c=>{c.target?.closest?.("a[href]")&&c.preventDefault()},!0);let i=()=>{let c=Yh(e,a);c&&r.updateSlideHtmlDirect(t,c)},o=a.querySelectorAll("h1,h2,h3,h4,h5,h6,p,li,span,strong,em,b,i,u,small,code,a,label,blockquote,td,th,dt,dd,figcaption,div"),l=0;o.forEach(c=>{Array.from(c.childNodes).some(d=>d.nodeType===Node.TEXT_NODE&&String(d.textContent||"").trim())&&(l+=1,c.setAttribute("contenteditable","true"),c.setAttribute("spellcheck","false"),c.setAttribute(ta,"true"),c.addEventListener("blur",i),c.addEventListener("keydown",d=>{((d.metaKey||d.ctrlKey)&&d.key==="Enter"||d.key==="Escape")&&(d.preventDefault(),c.blur())}))}),l||console.warn("[ppt-live] no editable nodes bound for slide",{slideId:t,candidates:o.length})}function Yh(e,t){let r=String(e?._pptLiveSourceHtml||"");if(!r)return"";let n=new DOMParser().parseFromString(zr(r),"text/html");if(!n.body)return"";let a=t.cloneNode(!0);a.querySelectorAll(`[${ta}]`).forEach(o=>{o.removeAttribute("contenteditable"),o.removeAttribute("spellcheck"),o.removeAttribute(ta)}),n.body.innerHTML=a.innerHTML;let i=` -${n.documentElement.outerHTML}`;return e._pptLiveSourceHtml=i,i}function zr(e){let t=String(e||"").trim();return t?/]/i.test(t)?t:`${t}`:''}function Jh(e,t,r,n){let a=r&&n===e.id,i=[`left:${e.x}%`,`top:${e.y}%`,`width:${e.w}%`,`height:${e.h}%`,`font-size:${ep(e.style.fontSize)}`,`font-weight:${e.style.fontWeight}`,`color:${Oc(e.style.color,t)}`,`text-align:${e.style.align||"left"}`,`background:${Oc(e.style.background,t)}`,`opacity:${e.style.opacity}`,`border-radius:${e.style.borderRadius}px`].join(";"),o="";if(e.type==="list")o=`
    ${(e.items||[]).map((l,c)=>r?`
  • ${Le(l)}
  • `:`
  • ${Le(l)}
  • `).join("")}
`;else if(e.type==="metric")o=`${Le(e.text)}${Le(e.label)}`;else if(e.type==="chart"){let l=Math.max(1,...(e.data||[]).map(c=>Number(c.value)||0));o=`${Le(e.text)}
${(e.data||[]).map(c=>`${Le(c.label)}`).join("")}
`}else e.type==="media"?o=`${Le(e.text||U("mediaPlaceholder"))}`:o=r?`${Le(e.text||"")}`:Le(e.text||"");return`
${o}${a?'':""}
`}function Oc(e,t){return!e||e==="transparent"?"transparent":e==="ink"?t.ink:e==="muted"?t.muted:e==="primary"?t.primary:e==="accent"?t.accent:e==="panel"?t.panel||"#ffffff":e==="soft"?$h(t.primary,.1):e==="background"?t.background:e}function $h(e,t){let r=String(e||"#0f766e").replace("#",""),n=parseInt(r.length===3?r.split("").map(l=>l+l).join(""):r,16),a=n>>16&255,i=n>>8&255,o=n&255;return`rgba(${a}, ${i}, ${o}, ${t})`}function ep(e){let t=Math.max(8,Number(e)||24);return`clamp(8px, ${Math.round(t/10.2*1e3)/1e3}cqw, ${t}px)`}function Lt(e){return document.getElementById(e)}function tp(e,t){let r=Lt(e);r&&document.activeElement!==r&&(r.value=t??"")}function rp(e){return Lt(e)?.value||""}function xs(e,t){let r=Lt(e);r&&(r.textContent=String(t??""))}function Ni(e){return Math.round(Number(e)*10)/10}function Zc(e=document,t=!1){let r=e,n=r.defaultView||window,a=new Set(["SCRIPT","STYLE","PRE","CODE","SVG","TEXTAREA"]),i="strong,b,em,i,u,span,a,small,mark,sub,sup,code",o="p,h1,h2,h3,h4,h5,h6,li";function l(B){let D=String(B.className||"").toLowerCase(),T=String(B.getAttribute?.("role")||"").toLowerCase();return/h1|title|headline|hero/.test(D)||T==="heading"?"h1":/h2|subtitle|subhead|section-title/.test(D)?"h2":/h3|kicker|eyebrow|label|caption/.test(D)?"h3":"p"}function c(B){return!B||B==="transparent"||B==="rgba(0, 0, 0, 0)"}function s(){let B=r.body;if(!B)return;let D=n.getComputedStyle(B),T=parseFloat(D.width),G=parseFloat(D.height);B.style.width=T>0?`${T}px`:"1280px",B.style.height=G>0?`${G}px`:"720px",B.style.margin="0",B.style.padding=D.padding||"0",B.style.overflow="hidden",B.style.position=D.position==="static"?"relative":D.position,c(D.backgroundColor)||(B.style.backgroundColor=D.backgroundColor),D.color&&(B.style.color=D.color),r.documentElement.style.margin="0",r.documentElement.style.padding="0";let j=n.getComputedStyle(r.documentElement).backgroundColor;!c(j)&&c(D.backgroundColor)&&(B.style.backgroundColor=j)}function d(B){B.querySelectorAll("div").forEach(D=>{a.has(D.tagName)||[...D.childNodes].forEach(T=>{if(T.nodeType!==Node.TEXT_NODE)return;let G=T.textContent.replace(/\s+/g," ").trim();if(!G){T.remove();return}let j=r.createElement(l(D));j.textContent=G,D.replaceChild(j,T)})})}function u(B){B.querySelectorAll("span").forEach(D=>{let T=n.getComputedStyle(D),G=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",j=f(T);if(!G&&!j)return;let S=r.createElement("p");D.className&&(S.className=D.className),D.getAttribute("style")&&S.setAttribute("style",D.getAttribute("style")),S.textContent=D.textContent,D.replaceWith(S)})}function h(B){B.querySelectorAll("div").forEach(D=>{let T=[...D.children].length>0&&[...D.children].every(S=>S.tagName==="SPAN"||S.tagName==="BR"),G=D.textContent.replace(/\s+/g," ").trim();if(!T||!G||D.querySelector("ul,ol,p,h1,h2,h3,h4,h5,h6"))return;let j=G.split(/\s*[•·▪-]\s+/).map(S=>S.trim()).filter(Boolean);if(j.length>=2){let S=r.createElement("ul");j.forEach(V=>{let v=r.createElement("li");v.textContent=V,S.appendChild(v)}),D.replaceChildren(S)}})}function f(B){return["Top","Right","Bottom","Left"].some(D=>parseFloat(B[`border${D}Width`]||0)>0)}function p(B){B.querySelectorAll(o).forEach(D=>{let T=n.getComputedStyle(D),G=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",j=T.backgroundImage&&T.backgroundImage!=="none",S=f(T),V=T.boxShadow&&T.boxShadow!=="none";if(!G&&!j&&!S&&!V)return;let v=r.createElement("div");(G||j)&&(v.style.background=T.background,v.style.backgroundColor=T.backgroundColor),j&&!String(T.backgroundImage||"").includes("gradient")&&(v.style.backgroundImage="none"),S&&(v.style.border=T.border),T.borderRadius&&(v.style.borderRadius=T.borderRadius),V&&(v.style.boxShadow=T.boxShadow),T.padding&&(v.style.padding=T.padding),D.style.background="transparent",D.style.backgroundColor="transparent",D.style.backgroundImage="none",D.style.border="none",D.style.boxShadow="none",D.style.padding="0",D.parentNode.insertBefore(v,D),v.appendChild(D)})}function A(B){B.querySelectorAll("*").forEach(D=>{let T=n.getComputedStyle(D),G=T.backgroundImage||"";if(!G.includes("gradient"))return;let j=G.match(/#[0-9a-f]{3,8}|rgba?\([^)]+\)/i);D.style.backgroundImage="none",j?D.style.backgroundColor=j[0]:T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)"&&(D.style.backgroundColor=T.backgroundColor)})}function y(B){B.querySelectorAll("div").forEach(D=>{let T=n.getComputedStyle(D),G=T.backgroundImage;!G||G==="none"||(D.style.backgroundImage="none",T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)"&&(D.style.backgroundColor=T.backgroundColor))})}function m(B){B.querySelectorAll(i).forEach(D=>{D.style.setProperty("margin","0","important"),D.style.setProperty("padding","0","important"),D.style.setProperty("border","none","important"),D.style.setProperty("box-shadow","none","important"),D.style.setProperty("background","transparent","important"),D.style.setProperty("background-color","transparent","important"),D.style.setProperty("background-image","none","important"),n.getComputedStyle(D).display==="block"&&D.style.setProperty("display","inline","important")})}function b(B){B.querySelectorAll(i).forEach(D=>{D.removeAttribute("class"),D.removeAttribute("style")})}function x(B){B.querySelectorAll('link[rel="stylesheet"], style').forEach(D=>{D.id!=="ppt-live-export-safe-styles"&&D.remove()})}function C(B){B.querySelectorAll(i).forEach(D=>{let T=n.getComputedStyle(D),G=["marginTop","marginRight","marginBottom","marginLeft"].some(_=>parseFloat(T[_])>0),j=["paddingTop","paddingRight","paddingBottom","paddingLeft"].some(_=>parseFloat(T[_])>0),S=f(T),V=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",v=T.backgroundImage&&T.backgroundImage!=="none";if(!G&&!j&&!S&&!V&&!v)return;let z=D.tagName.toLowerCase(),ae=r.createElement(z);ae.textContent=D.textContent,D.replaceWith(ae)})}function F(B){let D=B.body||B.querySelector(".ppt-export-body");(D?[D,...D.querySelectorAll("*")]:[...B.querySelectorAll("body, body *")]).forEach(G=>{if(a.has(G.tagName))return;let j=n.getComputedStyle(G),S=G.style;j.position&&j.position!=="static"&&(S.position=j.position),j.display&&j.display!=="inline"&&(S.display=j.display),["left","top","right","bottom","width","height","maxWidth","maxHeight"].forEach(v=>{let z=j[v];z&&z!=="auto"&&z!=="none"&&z!=="0px"&&(S[v]=z)}),j.zIndex&&j.zIndex!=="auto"&&(S.zIndex=j.zIndex),j.color&&(S.color=j.color),j.fontSize&&(S.fontSize=j.fontSize),j.fontWeight&&(S.fontWeight=j.fontWeight),j.fontFamily&&(S.fontFamily=j.fontFamily),j.lineHeight&&j.lineHeight!=="normal"&&(S.lineHeight=j.lineHeight),j.textAlign&&(S.textAlign=j.textAlign);let V=j.backgroundColor;V&&V!=="rgba(0, 0, 0, 0)"&&(S.backgroundColor=V),j.border&&j.border!=="none"&&f(j)&&(S.border=j.border),j.borderRadius&&j.borderRadius!=="0px"&&(S.borderRadius=j.borderRadius),j.padding&&j.padding!=="0px"&&(S.padding=j.padding),j.gap&&j.gap!=="normal"&&(S.gap=j.gap),j.flexDirection&&j.flexDirection!=="row"&&(S.flexDirection=j.flexDirection),j.alignItems&&j.alignItems!=="normal"&&(S.alignItems=j.alignItems),j.justifyContent&&j.justifyContent!=="normal"&&(S.justifyContent=j.justifyContent)})}function I(B){let D="ppt-live-export-safe-styles";B.getElementById(D)?.remove();let T=r.createElement("style");T.id=D,T.textContent=` + `:""}function Sc(e,t){["slideKickerInput","slideClaimInput","slideProofInput","slideSupportInput","slideSourceInput"].forEach(r=>{e.querySelector(`#${r}`)?.addEventListener("input",()=>t.updateSlideMethodology())})}function dn(e,t={}){if(e?.html)return``;let r=!!t.editable,n=t.selectedElementId||"",a=[`--slide-bg:${e.theme.background}`,`--slide-ink:${e.theme.ink}`,`--slide-muted:${e.theme.muted}`,`--slide-primary:${e.theme.primary}`,`--slide-accent:${e.theme.accent}`,`--slide-panel:${e.theme.panel||"#ffffff"}`].join(";");return`
+ ${e.kicker?`
${Be(e.kicker)}
`:""} + ${e.proofObject?`
${Be(e.proofObject)}
`:""} + ${Rh(e)} + ${(e.elements||[]).map(i=>Ih(i,e.theme,r,n)).join("")} + ${e.sourceNote?`
${Be(e.sourceNote)}
`:""} +
`}function Rh(e){let t=Array.isArray(e.quality?.issues)?e.quality.issues:[];if(!t.length)return"";let r=t.filter(a=>a.severity==="high").length,n=r?V("qualityNeedsReview"):V("qualityHasWarnings");return`
${Be(n)}
`}function Cc(e){e.querySelectorAll("[data-welcome-prompt]").forEach(t=>{t.addEventListener("click",()=>{let r=Et("topicInput");r&&(r.value=t.dataset.welcomePrompt||t.textContent||"",r.focus())})})}function Lh(e,t,r){if(!r?.updateSlideHtmlDirect)return;let n=e?.shadowRoot,a=n?.querySelector(`.${ps}`);if(!n||!a)return;if(!n.querySelector(`style[${bc}]`)){let c=document.createElement("style");c.setAttribute(bc,"true"),c.textContent=[`[${Qn}] { cursor: text; -webkit-user-select: text !important; user-select: text !important; }`,`[${Qn}]:hover { outline: 1.5px dashed rgba(37, 99, 235, 0.55); outline-offset: 1px; }`,`[${Qn}]:focus { outline: 2px solid rgba(37, 99, 235, 0.85); outline-offset: 1px; }`].join(` +`),n.appendChild(c)}n.addEventListener("click",c=>{c.target?.closest?.("a[href]")&&c.preventDefault()},!0);let i=()=>{let c=Nh(e,a);c&&r.updateSlideHtmlDirect(t,c)},o=a.querySelectorAll("h1,h2,h3,h4,h5,h6,p,li,span,strong,em,b,i,u,small,code,a,label,blockquote,td,th,dt,dd,figcaption,div"),l=0;o.forEach(c=>{Array.from(c.childNodes).some(d=>d.nodeType===Node.TEXT_NODE&&String(d.textContent||"").trim())&&(l+=1,c.setAttribute("contenteditable","true"),c.setAttribute("spellcheck","false"),c.setAttribute(Qn,"true"),c.addEventListener("blur",i),c.addEventListener("keydown",d=>{((d.metaKey||d.ctrlKey)&&d.key==="Enter"||d.key==="Escape")&&(d.preventDefault(),c.blur())}))}),l||console.warn("[ppt-live] no editable nodes bound for slide",{slideId:t,candidates:o.length})}function Nh(e,t){let r=String(e?._pptLiveSourceHtml||"");if(!r)return"";let n=new DOMParser().parseFromString(Mr(r),"text/html");if(!n.body)return"";let a=t.cloneNode(!0);a.querySelectorAll(`[${Qn}]`).forEach(o=>{o.removeAttribute("contenteditable"),o.removeAttribute("spellcheck"),o.removeAttribute(Qn)}),n.body.innerHTML=a.innerHTML;let i=` +${n.documentElement.outerHTML}`;return e._pptLiveSourceHtml=i,i}function Mr(e){let t=String(e||"").trim();return t?/]/i.test(t)?t:`${t}`:''}function Ih(e,t,r,n){let a=r&&n===e.id,i=[`left:${e.x}%`,`top:${e.y}%`,`width:${e.w}%`,`height:${e.h}%`,`font-size:${zh(e.style.fontSize)}`,`font-weight:${e.style.fontWeight}`,`color:${kc(e.style.color,t)}`,`text-align:${e.style.align||"left"}`,`background:${kc(e.style.background,t)}`,`opacity:${e.style.opacity}`,`border-radius:${e.style.borderRadius}px`].join(";"),o="";if(e.type==="list")o=`
    ${(e.items||[]).map((l,c)=>r?`
  • ${Be(l)}
  • `:`
  • ${Be(l)}
  • `).join("")}
`;else if(e.type==="metric")o=`${Be(e.text)}${Be(e.label)}`;else if(e.type==="chart"){let l=Math.max(1,...(e.data||[]).map(c=>Number(c.value)||0));o=`${Be(e.text)}
${(e.data||[]).map(c=>`${Be(c.label)}`).join("")}
`}else e.type==="media"?o=`${Be(e.text||V("mediaPlaceholder"))}`:o=r?`${Be(e.text||"")}`:Be(e.text||"");return`
${o}${a?'':""}
`}function kc(e,t){return!e||e==="transparent"?"transparent":e==="ink"?t.ink:e==="muted"?t.muted:e==="primary"?t.primary:e==="accent"?t.accent:e==="panel"?t.panel||"#ffffff":e==="soft"?Mh(t.primary,.1):e==="background"?t.background:e}function Mh(e,t){let r=String(e||"#0f766e").replace("#",""),n=parseInt(r.length===3?r.split("").map(l=>l+l).join(""):r,16),a=n>>16&255,i=n>>8&255,o=n&255;return`rgba(${a}, ${i}, ${o}, ${t})`}function zh(e){let t=Math.max(8,Number(e)||24);return`clamp(8px, ${Math.round(t/10.2*1e3)/1e3}cqw, ${t}px)`}function Et(e){return document.getElementById(e)}function Oh(e,t){let r=Et(e);r&&document.activeElement!==r&&(r.value=t??"")}function Uh(e){return Et(e)?.value||""}function hs(e,t){let r=Et(e);r&&(r.textContent=String(t??""))}function Di(e){return Math.round(Number(e)*10)/10}function Oc(e=document,t=!1){let r=e,n=r.defaultView||window,a=new Set(["SCRIPT","STYLE","PRE","CODE","SVG","TEXTAREA"]),i="strong,b,em,i,u,span,a,small,mark,sub,sup,code",o="p,h1,h2,h3,h4,h5,h6,li";function l(R){let E=String(R.className||"").toLowerCase(),T=String(R.getAttribute?.("role")||"").toLowerCase();return/h1|title|headline|hero/.test(E)||T==="heading"?"h1":/h2|subtitle|subhead|section-title/.test(E)?"h2":/h3|kicker|eyebrow|label|caption/.test(E)?"h3":"p"}function c(R){return!R||R==="transparent"||R==="rgba(0, 0, 0, 0)"}function s(){let R=r.body;if(!R)return;let E=n.getComputedStyle(R),T=parseFloat(E.width),W=parseFloat(E.height);R.style.width=T>0?`${T}px`:"1280px",R.style.height=W>0?`${W}px`:"720px",R.style.margin="0",R.style.padding=E.padding||"0",R.style.overflow="hidden",R.style.position=E.position==="static"?"relative":E.position,c(E.backgroundColor)||(R.style.backgroundColor=E.backgroundColor),E.color&&(R.style.color=E.color),r.documentElement.style.margin="0",r.documentElement.style.padding="0";let q=n.getComputedStyle(r.documentElement).backgroundColor;!c(q)&&c(E.backgroundColor)&&(R.style.backgroundColor=q)}function d(R){R.querySelectorAll("div").forEach(E=>{a.has(E.tagName)||[...E.childNodes].forEach(T=>{if(T.nodeType!==Node.TEXT_NODE)return;let W=T.textContent.replace(/\s+/g," ").trim();if(!W){T.remove();return}let q=r.createElement(l(E));q.textContent=W,E.replaceChild(q,T)})})}function u(R){R.querySelectorAll("span").forEach(E=>{let T=n.getComputedStyle(E),W=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",q=h(T);if(!W&&!q)return;let C=r.createElement("p");E.className&&(C.className=E.className),E.getAttribute("style")&&C.setAttribute("style",E.getAttribute("style")),C.textContent=E.textContent,E.replaceWith(C)})}function f(R){R.querySelectorAll("div").forEach(E=>{let T=[...E.children].length>0&&[...E.children].every(C=>C.tagName==="SPAN"||C.tagName==="BR"),W=E.textContent.replace(/\s+/g," ").trim();if(!T||!W||E.querySelector("ul,ol,p,h1,h2,h3,h4,h5,h6"))return;let q=W.split(/\s*[•·▪-]\s+/).map(C=>C.trim()).filter(Boolean);if(q.length>=2){let C=r.createElement("ul");q.forEach(j=>{let v=r.createElement("li");v.textContent=j,C.appendChild(v)}),E.replaceChildren(C)}})}function h(R){return["Top","Right","Bottom","Left"].some(E=>parseFloat(R[`border${E}Width`]||0)>0)}function p(R){R.querySelectorAll(o).forEach(E=>{let T=n.getComputedStyle(E),W=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",q=T.backgroundImage&&T.backgroundImage!=="none",C=h(T),j=T.boxShadow&&T.boxShadow!=="none";if(!W&&!q&&!C&&!j)return;let v=r.createElement("div");(W||q)&&(v.style.background=T.background,v.style.backgroundColor=T.backgroundColor),q&&!String(T.backgroundImage||"").includes("gradient")&&(v.style.backgroundImage="none"),C&&(v.style.border=T.border),T.borderRadius&&(v.style.borderRadius=T.borderRadius),j&&(v.style.boxShadow=T.boxShadow),T.padding&&(v.style.padding=T.padding),E.style.background="transparent",E.style.backgroundColor="transparent",E.style.backgroundImage="none",E.style.border="none",E.style.boxShadow="none",E.style.padding="0",E.parentNode.insertBefore(v,E),v.appendChild(E)})}function A(R){R.querySelectorAll("*").forEach(E=>{let T=n.getComputedStyle(E),W=T.backgroundImage||"";if(!W.includes("gradient"))return;let q=W.match(/#[0-9a-f]{3,8}|rgba?\([^)]+\)/i);E.style.backgroundImage="none",q?E.style.backgroundColor=q[0]:T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)"&&(E.style.backgroundColor=T.backgroundColor)})}function y(R){R.querySelectorAll("div").forEach(E=>{let T=n.getComputedStyle(E),W=T.backgroundImage;!W||W==="none"||(E.style.backgroundImage="none",T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)"&&(E.style.backgroundColor=T.backgroundColor))})}function m(R){R.querySelectorAll(i).forEach(E=>{E.style.setProperty("margin","0","important"),E.style.setProperty("padding","0","important"),E.style.setProperty("border","none","important"),E.style.setProperty("box-shadow","none","important"),E.style.setProperty("background","transparent","important"),E.style.setProperty("background-color","transparent","important"),E.style.setProperty("background-image","none","important"),n.getComputedStyle(E).display==="block"&&E.style.setProperty("display","inline","important")})}function b(R){R.querySelectorAll(i).forEach(E=>{E.removeAttribute("class"),E.removeAttribute("style")})}function x(R){R.querySelectorAll('link[rel="stylesheet"], style').forEach(E=>{E.id!=="ppt-live-export-safe-styles"&&E.remove()})}function S(R){R.querySelectorAll(i).forEach(E=>{let T=n.getComputedStyle(E),W=["marginTop","marginRight","marginBottom","marginLeft"].some(_=>parseFloat(T[_])>0),q=["paddingTop","paddingRight","paddingBottom","paddingLeft"].some(_=>parseFloat(T[_])>0),C=h(T),j=T.backgroundColor&&T.backgroundColor!=="rgba(0, 0, 0, 0)",v=T.backgroundImage&&T.backgroundImage!=="none";if(!W&&!q&&!C&&!j&&!v)return;let z=E.tagName.toLowerCase(),ne=r.createElement(z);ne.textContent=E.textContent,E.replaceWith(ne)})}function F(R){let E=R.body||R.querySelector(".ppt-export-body");(E?[E,...E.querySelectorAll("*")]:[...R.querySelectorAll("body, body *")]).forEach(W=>{if(a.has(W.tagName))return;let q=n.getComputedStyle(W),C=W.style;q.position&&q.position!=="static"&&(C.position=q.position),q.display&&q.display!=="inline"&&(C.display=q.display),["left","top","right","bottom","width","height","maxWidth","maxHeight"].forEach(v=>{let z=q[v];z&&z!=="auto"&&z!=="none"&&z!=="0px"&&(C[v]=z)}),q.zIndex&&q.zIndex!=="auto"&&(C.zIndex=q.zIndex),q.color&&(C.color=q.color),q.fontSize&&(C.fontSize=q.fontSize),q.fontWeight&&(C.fontWeight=q.fontWeight),q.fontFamily&&(C.fontFamily=q.fontFamily),q.lineHeight&&q.lineHeight!=="normal"&&(C.lineHeight=q.lineHeight),q.textAlign&&(C.textAlign=q.textAlign);let j=q.backgroundColor;j&&j!=="rgba(0, 0, 0, 0)"&&(C.backgroundColor=j),q.border&&q.border!=="none"&&h(q)&&(C.border=q.border),q.borderRadius&&q.borderRadius!=="0px"&&(C.borderRadius=q.borderRadius),q.padding&&q.padding!=="0px"&&(C.padding=q.padding),q.gap&&q.gap!=="normal"&&(C.gap=q.gap),q.flexDirection&&q.flexDirection!=="row"&&(C.flexDirection=q.flexDirection),q.alignItems&&q.alignItems!=="normal"&&(C.alignItems=q.alignItems),q.justifyContent&&q.justifyContent!=="normal"&&(C.justifyContent=q.justifyContent)})}function L(R){let E="ppt-live-export-safe-styles";R.getElementById(E)?.remove();let T=r.createElement("style");T.id=E,T.textContent=` ${i}, [class] ${i.split(",").join(", [class] ")} { margin: 0 !important; padding: 0 !important; @@ -86,19 +86,19 @@ ${n.documentElement.outerHTML}`;return e._pptLiveSourceHtml=i,i}function zr(e){l p, h1, h2, h3, h4, h5, h6, li { box-shadow: none !important; } - `,(B.head||B.documentElement).appendChild(T)}s(),d(r),u(r),h(r),A(r),y(r),t?(p(r),m(r),C(r),I(r),C(r),F(r),r.querySelectorAll("[class]").forEach(B=>B.removeAttribute("class")),x(r),b(r),m(r),C(r),I(r)):F(r)}function Yc(e=document){let t=e.defaultView||window,r=e.body,n=t.getComputedStyle(r),a={width:parseFloat(n.width),height:parseFloat(n.height),scrollWidth:r.scrollWidth,scrollHeight:r.scrollHeight},i=[],o=Math.max(0,a.scrollWidth-a.width-1),l=Math.max(0,a.scrollHeight-a.height-1),c=o*.75,s=l*.75;if(c>0||s>0){let d=[];c>0&&d.push(`${c.toFixed(1)}pt horizontally`),s>0&&d.push(`${s.toFixed(1)}pt vertically`);let u=s>0?' (Remember: leave 0.5" margin at bottom of slide)':"";i.push(`HTML content overflows body by ${d.join(" and ")}${u}`)}return{...a,errors:i}}function Jc(e=document){let t=e,r=t.defaultView||window,n=.75,a=96,i=["impact"],o=q=>{if(!q)return!1;let ee=q.toLowerCase().replace(/['"]/g,"").split(",")[0].trim();return i.includes(ee)},l=q=>q/a,c=q=>parseFloat(q)*n,s=q=>{if(q==="rgba(0, 0, 0, 0)"||q==="transparent")return"FFFFFF";let ee=q.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return ee?ee.slice(1).map(Y=>parseInt(Y).toString(16).padStart(2,"0")).join(""):"FFFFFF"},d=q=>{let ee=q.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(!ee||!ee[4])return null;let Y=parseFloat(ee[4]);return Math.round((1-Y)*100)},u=q=>{let ee=String(q||"").match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);return ee?{r:parseInt(ee[1],10),g:parseInt(ee[2],10),b:parseInt(ee[3],10),a:ee[4]!=null?parseFloat(ee[4]):1}:null},h=q=>{let ee=String(q||"0E0E12").replace("#","");return{r:parseInt(ee.slice(0,2),16),g:parseInt(ee.slice(2,4),16),b:parseInt(ee.slice(4,6),16)}},f=(q,ee="0E0E12")=>{let Y=u(q);if(!Y)return{fill:s(q),transparency:d(q)};if(Y.a>=.98)return{fill:s(q),transparency:null};let Ce=h(ee),be=Math.round(Ce.r*(1-Y.a)+Y.r*Y.a),oe=Math.round(Ce.g*(1-Y.a)+Y.g*Y.a),ye=Math.round(Ce.b*(1-Y.a)+Y.b*Y.a);return{fill:[be,oe,ye].map(Me=>Me.toString(16).padStart(2,"0")).join("").toUpperCase(),transparency:null}},p=(q,ee)=>ee==="uppercase"?q.toUpperCase():ee==="lowercase"?q.toLowerCase():ee==="capitalize"?q.replace(/\b\w/g,Y=>Y.toUpperCase()):q,A=(q,ee)=>{let Y=0;if(ee==="vertical-rl"?Y=90:ee==="vertical-lr"&&(Y=270),q&&q!=="none"){let Ce=q.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/);if(Ce)Y+=parseFloat(Ce[1]);else{let be=q.match(/matrix\(([^)]+)\)/);if(be){let oe=be[1].split(",").map(parseFloat),ye=Math.atan2(oe[1],oe[0])*(180/Math.PI);Y+=Math.round(ye)}}}return Y=Y%360,Y<0&&(Y+=360),Y===0?null:Y},y=(q,ee,Y)=>{if(Y===null)return{x:ee.left,y:ee.top,w:ee.width,h:ee.height};if(Y===90||Y===270){let ye=ee.left+ee.width/2,Me=ee.top+ee.height/2;return{x:ye-ee.height/2,y:Me-ee.width/2,w:ee.height,h:ee.width}}let be=ee.left+ee.width/2,oe=ee.top+ee.height/2;return{x:be-q.offsetWidth/2,y:oe-q.offsetHeight/2,w:q.offsetWidth,h:q.offsetHeight}},m=q=>{if(!q||q==="none"||q.match(/inset/))return null;let Y=q.match(/rgba?\([^)]+\)/),Ce=q.match(/([-\d.]+)(px|pt)/g);if(!Ce||Ce.length<2)return null;let be=parseFloat(Ce[0]),oe=parseFloat(Ce[1]),ye=Ce.length>2?parseFloat(Ce[2]):0,Me=0;(be!==0||oe!==0)&&(Me=Math.atan2(oe,be)*(180/Math.PI),Me<0&&(Me+=360));let we=Math.sqrt(be*be+oe*oe)*n,Ie=.5;if(Y){let Ee=Y[0].match(/[\d.]+\)$/);Ee&&(Ie=parseFloat(Ee[0].replace(")","")))}return{type:"outer",angle:Math.round(Me),blur:ye*.75,color:Y?s(Y[0]):"000000",offset:we,opacity:Ie}},b=(q,ee={},Y=[],Ce=be=>be)=>{let be=!1;return q.childNodes.forEach(oe=>{let ye=Ce,Me=oe.nodeType===Node.TEXT_NODE||oe.tagName==="BR";if(Me){let we=oe.tagName==="BR"?` -`:ye(oe.textContent.replace(/\s+/g," ")),Ie=Y[Y.length-1];be&&Ie?Ie.text+=we:Y.push({text:we,options:{...ee}})}else if(oe.nodeType===Node.ELEMENT_NODE&&oe.textContent.trim()){let we={...ee},Ie=r.getComputedStyle(oe);if(oe.tagName==="SPAN"||oe.tagName==="B"||oe.tagName==="STRONG"||oe.tagName==="I"||oe.tagName==="EM"||oe.tagName==="U"){if((Ie.fontWeight==="bold"||parseInt(Ie.fontWeight)>=600)&&!o(Ie.fontFamily)&&(we.bold=!0),Ie.fontStyle==="italic"&&(we.italic=!0),Ie.textDecoration&&Ie.textDecoration.includes("underline")&&(we.underline=!0),Ie.color&&Ie.color!=="rgb(0, 0, 0)"){we.color=s(Ie.color);let w=d(Ie.color);w!==null&&(we.transparency=w)}if(Ie.fontSize&&(we.fontSize=c(Ie.fontSize)),Ie.textTransform&&Ie.textTransform!=="none"){let w=Ie.textTransform;ye=de=>p(de,w)}Ie.marginLeft&&parseFloat(Ie.marginLeft)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginRight&&parseFloat(Ie.marginRight)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginTop&&parseFloat(Ie.marginTop)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginBottom&&parseFloat(Ie.marginBottom)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`),b(oe,we,Y,ye)}}be=Me}),Y.length>0&&(Y[0].text=Y[0].text.replace(/^\s+/,""),Y[Y.length-1].text=Y[Y.length-1].text.replace(/\s+$/,"")),Y.filter(oe=>oe.text.length>0)},x=q=>!q||q==="transparent"||q==="rgba(0, 0, 0, 0)",C=q=>{let ee=[q,t.documentElement,q?.querySelector?.(":scope > section, :scope > div, :scope > main")].filter(Boolean);for(let Y of ee){let Ce=r.getComputedStyle(Y),be=Ce.backgroundImage||"";if(be.includes("linear-gradient")||be.includes("radial-gradient"))return{gradient:!0};if(be&&be!=="none"){let ye=be.match(/url\(["']?([^"')]+)["']?\)/);if(ye)return{type:"image",path:ye[1]}}let oe=Ce.backgroundColor;if(!x(oe))return{type:"color",value:s(oe)}}return{type:"color",value:"FFFFFF"}},F=t.body,I=F.getBoundingClientRect(),B=q=>({left:q.left-I.left,top:q.top-I.top,width:q.width,height:q.height}),D=q=>B(q.getBoundingClientRect()),T=(q,ee)=>{let Y=u(q.color);return!Y||Y.a>=.2||!ee.textContent.trim()?s(q.color):"E8E8E8"},G=(q,ee,Y)=>{let{x:Ce,y:be,w:oe,h:ye}=y(q,ee,Y),Me=I.width,we=I.height,Ie=Math.max(8,Me-Ce-4),Ee=q.scrollHeight||0,w=/^H[1-6]$/.test(q.tagName),de=w?Math.min(Math.max(8,oe*.05),32):Math.min(Math.max(2,oe*.02),12);w?oe=Math.max(oe+de,Me*.92-Ce):oe=Math.min(oe+de,Ie),oe=Math.min(oe,Ie);let re=Math.max(6,ye*(w?.18:.12)),O=Math.max(8,we-be-4);return Ee>ye+2?ye=Math.min(Ee+re,O):ye=Math.min(ye+re,O),{x:Ce,y:be,w:oe,h:ye}},j=q=>{let ee=r.getComputedStyle(q).zIndex;if(!ee||ee==="auto")return 0;let Y=parseInt(ee,10);return Number.isFinite(Y)?Y:0},S=(q,ee)=>{ee&&(q.zIndex=j(ee)),ce.push(q)},V=(q,ee,Y)=>{let be=r.getComputedStyle(q).listStyleColor;if(be&&be!=="rgba(0, 0, 0, 0)"){let oe=s(be);if(oe&&oe!==Y)return oe}for(let oe of ee)try{let ye=r.getComputedStyle(oe,"::marker");if(ye?.color){let Me=s(ye.color);if(Me&&Me!==Y)return Me}}catch{}return null},v=[],z=C(F);z.gradient&&v.push("CSS gradients are not supported. Use Sharp to rasterize gradients as PNG images first, then reference with background-image: url('gradient.png')");let ae=z.gradient?{type:"color",value:"FFFFFF"}:{type:z.type,...z.path?{path:z.path}:{value:z.value}},_=ae.value||"0E0E12",ce=[],J=[],me=["P","H1","H2","H3","H4","H5","H6","UL","OL","LI"],K=new Set;t.querySelectorAll("*").forEach(q=>{if(K.has(q))return;if(q.tagName==="DIV"&&q.dataset&&q.dataset.pptxMerge==="true"){let P=D(q);if(P.width===0||P.height===0){K.add(q);return}if(q.querySelector('[data-pptx-merge="true"]')){v.push("data-pptx-merge container cannot contain another data-pptx-merge container. Nested merge is not supported."),K.add(q);return}let W=r.getComputedStyle(q);if(W.backgroundImage&&W.backgroundImage!=="none"){v.push("Background images on data-pptx-merge container are not supported. Use solid colors or borders, or layer images via slide.addImage().");return}let le=W.backgroundColor&&W.backgroundColor!=="rgba(0, 0, 0, 0)",he=[W.borderTopWidth,W.borderRightWidth,W.borderBottomWidth,W.borderLeftWidth].map(Re=>parseFloat(Re)||0),pe=he.some(Re=>Re>0)&&he.every(Re=>Re===he[0]);(le||pe)&&ce.push({type:"shape",text:"",position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)},shape:{fill:le?s(W.backgroundColor):null,transparency:le?d(W.backgroundColor):null,line:pe?{color:s(W.borderColor),width:c(W.borderWidth)}:null,rectRadius:(()=>{let Re=W.borderRadius,ot=parseFloat(Re);if(ot===0)return 0;if(Re.includes("%")){if(ot>=50)return 1;let pt=Math.min(P.width,P.height);return ot/100*l(pt)}return Re.includes("pt")?ot/72:ot/a})(),shadow:m(W.boxShadow)}});let ke=Array.from(q.querySelectorAll("p, h1, h2, h3, h4, h5, h6"));if(ke.length===0){v.push("data-pptx-merge container has no

/ children to merge. Remove the data-pptx-merge attribute or add text elements."),K.add(q);return}let fe=r.getComputedStyle(ke[0]),De={fontSize:c(fe.fontSize),fontFace:fe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:s(fe.color),align:fe.textAlign==="start"?"left":fe.textAlign,lineSpacing:fe.lineHeight&&fe.lineHeight!=="normal"?c(fe.lineHeight):null,paraSpaceBefore:0,paraSpaceAfter:0,margin:[c(W.paddingLeft),c(W.paddingRight),c(W.paddingBottom),c(W.paddingTop)]},qe=d(fe.color);qe!==null&&(De.transparency=qe);let ze=[];if(ke.forEach((Re,ot)=>{let pt=ot===ke.length-1,R=r.getComputedStyle(Re),se=R.textTransform,Ae=c(R.fontSize),Fe=R.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),H=s(R.color),ue=R.fontWeight==="bold"||parseInt(R.fontWeight)>=600,k=R.fontStyle==="italic",ge=R.textDecoration.includes("underline"),Oe={};Ae!==De.fontSize&&(Oe.fontSize=Ae),Fe!==De.fontFace&&(Oe.fontFace=Fe),H!==De.color&&(Oe.color=H),ue&&!o(R.fontFamily)&&(Oe.bold=!0),k&&(Oe.italic=!0),ge&&(Oe.underline=!0);let g=Re.querySelector("b, i, u, strong, em, span, br"),te;if(g)te=b(Re,Oe,[],ie=>p(ie,se));else{let ie=p(Re.textContent.trim(),se);if(!ie)return;te=[{text:ie,options:{...Oe}}]}te.length>0&&!pt&&(te[te.length-1].options.breakLine=!0),ze.push(...te),K.add(Re)}),ze.length===0){K.add(q);return}ce.push({type:"merged-text",items:ze,position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)},style:De}),K.add(q);return}if(me.includes(q.tagName)){let P=r.getComputedStyle(q),W=P.backgroundColor&&P.backgroundColor!=="rgba(0, 0, 0, 0)",le=P.borderWidth&&parseFloat(P.borderWidth)>0||P.borderTopWidth&&parseFloat(P.borderTopWidth)>0||P.borderRightWidth&&parseFloat(P.borderRightWidth)>0||P.borderBottomWidth&&parseFloat(P.borderBottomWidth)>0||P.borderLeftWidth&&parseFloat(P.borderLeftWidth)>0,he=P.boxShadow&&P.boxShadow!=="none";if(W||le||he){let Z=D(q);if(Z.width>0&&Z.height>0){let pe=[P.borderTopWidth,P.borderRightWidth,P.borderBottomWidth,P.borderLeftWidth].map(De=>parseFloat(De)||0),ke=pe.some(De=>De>0)&&pe.every(De=>De===pe[0]),fe=W?f(P.backgroundColor,_):{fill:null,transparency:null};if(fe.fill||ke){let De=P.borderRadius,qe=parseFloat(De);S({type:"shape",text:"",position:{x:l(Z.left),y:l(Z.top),w:l(Z.width),h:l(Z.height)},shape:{fill:fe.fill,transparency:fe.transparency,line:ke?{color:s(P.borderColor),width:c(P.borderWidth)}:null,rectRadius:(()=>{if(!qe)return 0;if(De.includes("%")){if(qe>=50)return 1;let ze=Math.min(Z.width,Z.height);return qe/100*l(ze)}return De.includes("pt")?qe/72:qe/a})(),shadow:m(P.boxShadow)}},q)}}}}if(q.className&&q.className.includes("placeholder")){let P=D(q);P.width===0||P.height===0?v.push(`Placeholder "${q.id||"unnamed"}" has ${P.width===0?"width: 0":"height: 0"}. Check the layout CSS.`):J.push({id:q.id||`placeholder-${J.length}`,x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)}),K.add(q);return}if(q.tagName==="IMG"){let P=D(q);if(P.width>0&&P.height>0){ce.push({type:"image",src:q.src,position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)}}),K.add(q);return}}if(new Set(["DIV","SECTION","ARTICLE","ASIDE"]).has(q.tagName)){let P=r.getComputedStyle(q),W=P.backgroundColor&&P.backgroundColor!=="rgba(0, 0, 0, 0)";for(let Re of q.childNodes)if(Re.nodeType===Node.TEXT_NODE){let ot=Re.textContent.trim();ot&&v.push(`DIV element contains unwrapped text "${ot.substring(0,50)}${ot.length>50?"...":""}". All text must be wrapped in

,

-

,
    , or
      tags to appear in PowerPoint.`)}let le=P.backgroundImage;if(le&&le!=="none"){v.push("Background images on DIV elements are not supported. Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images.");return}let he=P.borderTopWidth,Z=P.borderRightWidth,pe=P.borderBottomWidth,ke=P.borderLeftWidth,fe=[he,Z,pe,ke].map(Re=>parseFloat(Re)||0),De=fe.some(Re=>Re>0),qe=De&&fe.every(Re=>Re===fe[0]),ze=[];if(De&&!qe){let Re=D(q),ot=l(Re.left),pt=l(Re.top),R=l(Re.width),se=l(Re.height);if(parseFloat(he)>0){let Ae=c(he),Fe=Ae/72/2;ze.push({type:"line",x1:ot,y1:pt+Fe,x2:ot+R,y2:pt+Fe,width:Ae,color:s(P.borderTopColor)})}if(parseFloat(Z)>0){let Ae=c(Z),Fe=Ae/72/2;ze.push({type:"line",x1:ot+R-Fe,y1:pt,x2:ot+R-Fe,y2:pt+se,width:Ae,color:s(P.borderRightColor)})}if(parseFloat(pe)>0){let Ae=c(pe),Fe=Ae/72/2;ze.push({type:"line",x1:ot,y1:pt+se-Fe,x2:ot+R,y2:pt+se-Fe,width:Ae,color:s(P.borderBottomColor)})}if(parseFloat(ke)>0){let Ae=c(ke),Fe=Ae/72/2;ze.push({type:"line",x1:ot+Fe,y1:pt,x2:ot+Fe,y2:pt+se,width:Ae,color:s(P.borderLeftColor)})}}if(W||De){let Re=D(q);if(Re.width>=I.width*.97&&Re.height>=I.height*.97&&W){K.add(q);return}if(Re.width>0&&Re.height>0){let pt=m(P.boxShadow);if(W||qe){let R=W?f(P.backgroundColor,_):{fill:null,transparency:null},se=R.fill,Ae=R.transparency;!se&&qe&&(se=s(P.borderColor)||"2A2A30",Ae=Ae??88),S({type:"shape",text:"",position:{x:l(Re.left),y:l(Re.top),w:l(Re.width),h:l(Re.height)},shape:{fill:se,transparency:Ae,line:qe?{color:s(P.borderColor),width:c(P.borderWidth)}:null,rectRadius:(()=>{let Fe=P.borderRadius,H=parseFloat(Fe);if(H===0)return 0;if(Fe.includes("%")){if(H>=50)return 1;let ue=Math.min(Re.width,Re.height);return H/100*l(ue)}return Fe.includes("pt")?H/72:H/a})(),shadow:pt}},q)}ze.forEach(R=>S(R,q)),K.add(q);return}}}if(q.tagName==="UL"||q.tagName==="OL"){let P=D(q);if(P.width===0||P.height===0)return;let W=Array.from(q.querySelectorAll("li")),le=[],he=r.getComputedStyle(q),Z=c(he.paddingLeft),pe=Z*.5,ke=Z*.5,fe=r.getComputedStyle(W[0]||q),De=s(fe.color),qe=V(q,W,De);W.forEach((Re,ot)=>{let pt=ot===W.length-1,R=b(Re,{breakLine:!1});R.length>0&&(R[0].text=R[0].text.replace(/^[•\-\*▪▸]\s*/,""),R[0].options.bullet={indent:ke}),R.length>0&&qe&&qe!==De&&R.unshift({text:"\u200B",options:{bullet:{indent:ke},color:qe,fontSize:R[0]?.options?.fontSize||c(fe.fontSize),breakLine:!1}}),R.length>0&&!pt&&(R[R.length-1].options.breakLine=!0),le.push(...R)});let ze=G(q,P,null);S({type:"list",items:le,position:{x:l(ze.x),y:l(ze.y),w:l(ze.w),h:l(ze.h)},style:{fontSize:c(fe.fontSize),fontFace:fe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:De,bulletColor:qe,transparency:d(fe.color),align:fe.textAlign==="start"?"left":fe.textAlign,lineSpacing:fe.lineHeight&&fe.lineHeight!=="normal"?c(fe.lineHeight):null,paraSpaceBefore:0,paraSpaceAfter:c(fe.marginBottom),margin:[pe,0,0,0]}},q),W.forEach(Re=>K.add(Re)),K.add(q);return}if(!me.includes(q.tagName))return;let Ce=D(q),be=q.textContent.trim();if(Ce.width===0||Ce.height===0||!be)return;if(q.tagName!=="LI"&&/^[•\-\*▪▸○●◆◇■□]\s/.test(be.trimStart())){v.push(`Text element <${q.tagName.toLowerCase()}> starts with bullet symbol "${be.substring(0,20)}...". Use
        or
          lists instead of manual bullet symbols.`);return}let oe=r.getComputedStyle(q),ye=A(oe.transform,oe.writingMode),{x:Me,y:we,w:Ie,h:Ee}=G(q,Ce,ye),w=oe.fontWeight==="bold"||parseInt(oe.fontWeight,10)>=600,de={fontSize:c(oe.fontSize),fontFace:oe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:T(oe,q),align:oe.textAlign==="start"?"left":oe.textAlign,lineSpacing:c(oe.lineHeight),paraSpaceBefore:c(oe.marginTop),paraSpaceAfter:c(oe.marginBottom),margin:[c(oe.paddingLeft),c(oe.paddingRight),c(oe.paddingBottom),c(oe.paddingTop)]},re=d(oe.color);if(re!==null&&(de.transparency=re),ye!==null&&(de.rotate=ye),q.querySelector("b, i, u, strong, em, span, br")){let P=oe.textTransform,W={};w&&!o(oe.fontFamily)&&(W.bold=!0);let le=b(q,W,[],pe=>p(pe,P));!le.map(pe=>pe.text).join("").trim()&&be&&(le=[{text:p(be,P),options:{...W}}]);let Z={...de};if(Z.lineSpacing){let pe=Math.max(Z.fontSize,...le.map(ke=>ke.options?.fontSize||0));if(pe>Z.fontSize){let ke=Z.lineSpacing/Z.fontSize;Z.lineSpacing=pe*ke}}S({type:q.tagName.toLowerCase(),text:le,position:{x:l(Me),y:l(we),w:l(Ie),h:l(Ee)},style:Z},q)}else{let P=oe.textTransform,W=p(be,P);S({type:q.tagName.toLowerCase(),text:W,position:{x:l(Me),y:l(we),w:l(Ie),h:l(Ee)},style:{...de,bold:w&&!o(oe.fontFamily),italic:oe.fontStyle==="italic",underline:oe.textDecoration.includes("underline")}},q)}K.add(q)});let Q=q=>q==="shape"?0:q==="line"?1:q==="image"?2:3;return ce.sort((q,ee)=>{let Y=(q.zIndex??0)-(ee.zIndex??0);return Y!==0?Y:Q(q.type)-Q(ee.type)}),{background:ae,elements:ce,placeholders:J,errors:v}}var or={width:1280,height:720},np={p:["p"],h1:["h1"],h2:["h2"],h3:["h3"],h4:["h4"],h5:["h5"],h6:["h6"],list:["li"],"merged-text":["span","em","strong","b","i","p","h1","h2","h3","h4","h5","h6"]};function ed(e){return(e?.elements||[]).filter(t=>td.has(t.type)).length}function ap(e){let t=new Set;for(let n of e?.elements||[])if(td.has(n.type))for(let a of np[n.type]||[n.type])t.add(a);return t.size?[...t].map(n=>`body[data-pptx-raster="1"] ${n} { + `,(R.head||R.documentElement).appendChild(T)}s(),d(r),u(r),f(r),A(r),y(r),t?(p(r),m(r),S(r),L(r),S(r),F(r),r.querySelectorAll("[class]").forEach(R=>R.removeAttribute("class")),x(r),b(r),m(r),S(r),L(r)):F(r)}function Uc(e=document){let t=e.defaultView||window,r=e.body,n=t.getComputedStyle(r),a={width:parseFloat(n.width),height:parseFloat(n.height),scrollWidth:r.scrollWidth,scrollHeight:r.scrollHeight},i=[],o=Math.max(0,a.scrollWidth-a.width-1),l=Math.max(0,a.scrollHeight-a.height-1),c=o*.75,s=l*.75;if(c>0||s>0){let d=[];c>0&&d.push(`${c.toFixed(1)}pt horizontally`),s>0&&d.push(`${s.toFixed(1)}pt vertically`);let u=s>0?' (Remember: leave 0.5" margin at bottom of slide)':"";i.push(`HTML content overflows body by ${d.join(" and ")}${u}`)}return{...a,errors:i}}function Wc(e=document){let t=e,r=t.defaultView||window,n=.75,a=96,i=["impact"],o=G=>{if(!G)return!1;let ee=G.toLowerCase().replace(/['"]/g,"").split(",")[0].trim();return i.includes(ee)},l=G=>G/a,c=G=>parseFloat(G)*n,s=G=>{if(G==="rgba(0, 0, 0, 0)"||G==="transparent")return"FFFFFF";let ee=G.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return ee?ee.slice(1).map(Y=>parseInt(Y).toString(16).padStart(2,"0")).join(""):"FFFFFF"},d=G=>{let ee=G.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(!ee||!ee[4])return null;let Y=parseFloat(ee[4]);return Math.round((1-Y)*100)},u=G=>{let ee=String(G||"").match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);return ee?{r:parseInt(ee[1],10),g:parseInt(ee[2],10),b:parseInt(ee[3],10),a:ee[4]!=null?parseFloat(ee[4]):1}:null},f=G=>{let ee=String(G||"0E0E12").replace("#","");return{r:parseInt(ee.slice(0,2),16),g:parseInt(ee.slice(2,4),16),b:parseInt(ee.slice(4,6),16)}},h=(G,ee="0E0E12")=>{let Y=u(G);if(!Y)return{fill:s(G),transparency:d(G)};if(Y.a>=.98)return{fill:s(G),transparency:null};let Ce=f(ee),be=Math.round(Ce.r*(1-Y.a)+Y.r*Y.a),oe=Math.round(Ce.g*(1-Y.a)+Y.g*Y.a),ye=Math.round(Ce.b*(1-Y.a)+Y.b*Y.a);return{fill:[be,oe,ye].map(Me=>Me.toString(16).padStart(2,"0")).join("").toUpperCase(),transparency:null}},p=(G,ee)=>ee==="uppercase"?G.toUpperCase():ee==="lowercase"?G.toLowerCase():ee==="capitalize"?G.replace(/\b\w/g,Y=>Y.toUpperCase()):G,A=(G,ee)=>{let Y=0;if(ee==="vertical-rl"?Y=90:ee==="vertical-lr"&&(Y=270),G&&G!=="none"){let Ce=G.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/);if(Ce)Y+=parseFloat(Ce[1]);else{let be=G.match(/matrix\(([^)]+)\)/);if(be){let oe=be[1].split(",").map(parseFloat),ye=Math.atan2(oe[1],oe[0])*(180/Math.PI);Y+=Math.round(ye)}}}return Y=Y%360,Y<0&&(Y+=360),Y===0?null:Y},y=(G,ee,Y)=>{if(Y===null)return{x:ee.left,y:ee.top,w:ee.width,h:ee.height};if(Y===90||Y===270){let ye=ee.left+ee.width/2,Me=ee.top+ee.height/2;return{x:ye-ee.height/2,y:Me-ee.width/2,w:ee.height,h:ee.width}}let be=ee.left+ee.width/2,oe=ee.top+ee.height/2;return{x:be-G.offsetWidth/2,y:oe-G.offsetHeight/2,w:G.offsetWidth,h:G.offsetHeight}},m=G=>{if(!G||G==="none"||G.match(/inset/))return null;let Y=G.match(/rgba?\([^)]+\)/),Ce=G.match(/([-\d.]+)(px|pt)/g);if(!Ce||Ce.length<2)return null;let be=parseFloat(Ce[0]),oe=parseFloat(Ce[1]),ye=Ce.length>2?parseFloat(Ce[2]):0,Me=0;(be!==0||oe!==0)&&(Me=Math.atan2(oe,be)*(180/Math.PI),Me<0&&(Me+=360));let we=Math.sqrt(be*be+oe*oe)*n,Ie=.5;if(Y){let Ee=Y[0].match(/[\d.]+\)$/);Ee&&(Ie=parseFloat(Ee[0].replace(")","")))}return{type:"outer",angle:Math.round(Me),blur:ye*.75,color:Y?s(Y[0]):"000000",offset:we,opacity:Ie}},b=(G,ee={},Y=[],Ce=be=>be)=>{let be=!1;return G.childNodes.forEach(oe=>{let ye=Ce,Me=oe.nodeType===Node.TEXT_NODE||oe.tagName==="BR";if(Me){let we=oe.tagName==="BR"?` +`:ye(oe.textContent.replace(/\s+/g," ")),Ie=Y[Y.length-1];be&&Ie?Ie.text+=we:Y.push({text:we,options:{...ee}})}else if(oe.nodeType===Node.ELEMENT_NODE&&oe.textContent.trim()){let we={...ee},Ie=r.getComputedStyle(oe);if(oe.tagName==="SPAN"||oe.tagName==="B"||oe.tagName==="STRONG"||oe.tagName==="I"||oe.tagName==="EM"||oe.tagName==="U"){if((Ie.fontWeight==="bold"||parseInt(Ie.fontWeight)>=600)&&!o(Ie.fontFamily)&&(we.bold=!0),Ie.fontStyle==="italic"&&(we.italic=!0),Ie.textDecoration&&Ie.textDecoration.includes("underline")&&(we.underline=!0),Ie.color&&Ie.color!=="rgb(0, 0, 0)"){we.color=s(Ie.color);let w=d(Ie.color);w!==null&&(we.transparency=w)}if(Ie.fontSize&&(we.fontSize=c(Ie.fontSize)),Ie.textTransform&&Ie.textTransform!=="none"){let w=Ie.textTransform;ye=de=>p(de,w)}Ie.marginLeft&&parseFloat(Ie.marginLeft)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginRight&&parseFloat(Ie.marginRight)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginTop&&parseFloat(Ie.marginTop)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`),Ie.marginBottom&&parseFloat(Ie.marginBottom)>0&&v.push(`Inline element <${oe.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`),b(oe,we,Y,ye)}}be=Me}),Y.length>0&&(Y[0].text=Y[0].text.replace(/^\s+/,""),Y[Y.length-1].text=Y[Y.length-1].text.replace(/\s+$/,"")),Y.filter(oe=>oe.text.length>0)},x=G=>!G||G==="transparent"||G==="rgba(0, 0, 0, 0)",S=G=>{let ee=[G,t.documentElement,G?.querySelector?.(":scope > section, :scope > div, :scope > main")].filter(Boolean);for(let Y of ee){let Ce=r.getComputedStyle(Y),be=Ce.backgroundImage||"";if(be.includes("linear-gradient")||be.includes("radial-gradient"))return{gradient:!0};if(be&&be!=="none"){let ye=be.match(/url\(["']?([^"')]+)["']?\)/);if(ye)return{type:"image",path:ye[1]}}let oe=Ce.backgroundColor;if(!x(oe))return{type:"color",value:s(oe)}}return{type:"color",value:"FFFFFF"}},F=t.body,L=F.getBoundingClientRect(),R=G=>({left:G.left-L.left,top:G.top-L.top,width:G.width,height:G.height}),E=G=>R(G.getBoundingClientRect()),T=(G,ee)=>{let Y=u(G.color);return!Y||Y.a>=.2||!ee.textContent.trim()?s(G.color):"E8E8E8"},W=(G,ee,Y)=>{let{x:Ce,y:be,w:oe,h:ye}=y(G,ee,Y),Me=L.width,we=L.height,Ie=Math.max(8,Me-Ce-4),Ee=G.scrollHeight||0,w=/^H[1-6]$/.test(G.tagName),de=w?Math.min(Math.max(8,oe*.05),32):Math.min(Math.max(2,oe*.02),12);w?oe=Math.max(oe+de,Me*.92-Ce):oe=Math.min(oe+de,Ie),oe=Math.min(oe,Ie);let re=Math.max(6,ye*(w?.18:.12)),O=Math.max(8,we-be-4);return Ee>ye+2?ye=Math.min(Ee+re,O):ye=Math.min(ye+re,O),{x:Ce,y:be,w:oe,h:ye}},q=G=>{let ee=r.getComputedStyle(G).zIndex;if(!ee||ee==="auto")return 0;let Y=parseInt(ee,10);return Number.isFinite(Y)?Y:0},C=(G,ee)=>{ee&&(G.zIndex=q(ee)),ce.push(G)},j=(G,ee,Y)=>{let be=r.getComputedStyle(G).listStyleColor;if(be&&be!=="rgba(0, 0, 0, 0)"){let oe=s(be);if(oe&&oe!==Y)return oe}for(let oe of ee)try{let ye=r.getComputedStyle(oe,"::marker");if(ye?.color){let Me=s(ye.color);if(Me&&Me!==Y)return Me}}catch{}return null},v=[],z=S(F);z.gradient&&v.push("CSS gradients are not supported. Use Sharp to rasterize gradients as PNG images first, then reference with background-image: url('gradient.png')");let ne=z.gradient?{type:"color",value:"FFFFFF"}:{type:z.type,...z.path?{path:z.path}:{value:z.value}},_=ne.value||"0E0E12",ce=[],J=[],ge=["P","H1","H2","H3","H4","H5","H6","UL","OL","LI"],K=new Set;t.querySelectorAll("*").forEach(G=>{if(K.has(G))return;if(G.tagName==="DIV"&&G.dataset&&G.dataset.pptxMerge==="true"){let P=E(G);if(P.width===0||P.height===0){K.add(G);return}if(G.querySelector('[data-pptx-merge="true"]')){v.push("data-pptx-merge container cannot contain another data-pptx-merge container. Nested merge is not supported."),K.add(G);return}let U=r.getComputedStyle(G);if(U.backgroundImage&&U.backgroundImage!=="none"){v.push("Background images on data-pptx-merge container are not supported. Use solid colors or borders, or layer images via slide.addImage().");return}let le=U.backgroundColor&&U.backgroundColor!=="rgba(0, 0, 0, 0)",he=[U.borderTopWidth,U.borderRightWidth,U.borderBottomWidth,U.borderLeftWidth].map(Re=>parseFloat(Re)||0),pe=he.some(Re=>Re>0)&&he.every(Re=>Re===he[0]);(le||pe)&&ce.push({type:"shape",text:"",position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)},shape:{fill:le?s(U.backgroundColor):null,transparency:le?d(U.backgroundColor):null,line:pe?{color:s(U.borderColor),width:c(U.borderWidth)}:null,rectRadius:(()=>{let Re=U.borderRadius,it=parseFloat(Re);if(it===0)return 0;if(Re.includes("%")){if(it>=50)return 1;let pt=Math.min(P.width,P.height);return it/100*l(pt)}return Re.includes("pt")?it/72:it/a})(),shadow:m(U.boxShadow)}});let ke=Array.from(G.querySelectorAll("p, h1, h2, h3, h4, h5, h6"));if(ke.length===0){v.push("data-pptx-merge container has no

          / children to merge. Remove the data-pptx-merge attribute or add text elements."),K.add(G);return}let fe=r.getComputedStyle(ke[0]),De={fontSize:c(fe.fontSize),fontFace:fe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:s(fe.color),align:fe.textAlign==="start"?"left":fe.textAlign,lineSpacing:fe.lineHeight&&fe.lineHeight!=="normal"?c(fe.lineHeight):null,paraSpaceBefore:0,paraSpaceAfter:0,margin:[c(U.paddingLeft),c(U.paddingRight),c(U.paddingBottom),c(U.paddingTop)]},qe=d(fe.color);qe!==null&&(De.transparency=qe);let ze=[];if(ke.forEach((Re,it)=>{let pt=it===ke.length-1,B=r.getComputedStyle(Re),se=B.textTransform,Ae=c(B.fontSize),Fe=B.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),H=s(B.color),ue=B.fontWeight==="bold"||parseInt(B.fontWeight)>=600,k=B.fontStyle==="italic",ve=B.textDecoration.includes("underline"),Oe={};Ae!==De.fontSize&&(Oe.fontSize=Ae),Fe!==De.fontFace&&(Oe.fontFace=Fe),H!==De.color&&(Oe.color=H),ue&&!o(B.fontFamily)&&(Oe.bold=!0),k&&(Oe.italic=!0),ve&&(Oe.underline=!0);let g=Re.querySelector("b, i, u, strong, em, span, br"),te;if(g)te=b(Re,Oe,[],ie=>p(ie,se));else{let ie=p(Re.textContent.trim(),se);if(!ie)return;te=[{text:ie,options:{...Oe}}]}te.length>0&&!pt&&(te[te.length-1].options.breakLine=!0),ze.push(...te),K.add(Re)}),ze.length===0){K.add(G);return}ce.push({type:"merged-text",items:ze,position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)},style:De}),K.add(G);return}if(ge.includes(G.tagName)){let P=r.getComputedStyle(G),U=P.backgroundColor&&P.backgroundColor!=="rgba(0, 0, 0, 0)",le=P.borderWidth&&parseFloat(P.borderWidth)>0||P.borderTopWidth&&parseFloat(P.borderTopWidth)>0||P.borderRightWidth&&parseFloat(P.borderRightWidth)>0||P.borderBottomWidth&&parseFloat(P.borderBottomWidth)>0||P.borderLeftWidth&&parseFloat(P.borderLeftWidth)>0,he=P.boxShadow&&P.boxShadow!=="none";if(U||le||he){let Z=E(G);if(Z.width>0&&Z.height>0){let pe=[P.borderTopWidth,P.borderRightWidth,P.borderBottomWidth,P.borderLeftWidth].map(De=>parseFloat(De)||0),ke=pe.some(De=>De>0)&&pe.every(De=>De===pe[0]),fe=U?h(P.backgroundColor,_):{fill:null,transparency:null};if(fe.fill||ke){let De=P.borderRadius,qe=parseFloat(De);C({type:"shape",text:"",position:{x:l(Z.left),y:l(Z.top),w:l(Z.width),h:l(Z.height)},shape:{fill:fe.fill,transparency:fe.transparency,line:ke?{color:s(P.borderColor),width:c(P.borderWidth)}:null,rectRadius:(()=>{if(!qe)return 0;if(De.includes("%")){if(qe>=50)return 1;let ze=Math.min(Z.width,Z.height);return qe/100*l(ze)}return De.includes("pt")?qe/72:qe/a})(),shadow:m(P.boxShadow)}},G)}}}}if(G.className&&G.className.includes("placeholder")){let P=E(G);P.width===0||P.height===0?v.push(`Placeholder "${G.id||"unnamed"}" has ${P.width===0?"width: 0":"height: 0"}. Check the layout CSS.`):J.push({id:G.id||`placeholder-${J.length}`,x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)}),K.add(G);return}if(G.tagName==="IMG"){let P=E(G);if(P.width>0&&P.height>0){ce.push({type:"image",src:G.src,position:{x:l(P.left),y:l(P.top),w:l(P.width),h:l(P.height)}}),K.add(G);return}}if(new Set(["DIV","SECTION","ARTICLE","ASIDE"]).has(G.tagName)){let P=r.getComputedStyle(G),U=P.backgroundColor&&P.backgroundColor!=="rgba(0, 0, 0, 0)";for(let Re of G.childNodes)if(Re.nodeType===Node.TEXT_NODE){let it=Re.textContent.trim();it&&v.push(`DIV element contains unwrapped text "${it.substring(0,50)}${it.length>50?"...":""}". All text must be wrapped in

          ,

          -

          ,
            , or
              tags to appear in PowerPoint.`)}let le=P.backgroundImage;if(le&&le!=="none"){v.push("Background images on DIV elements are not supported. Use solid colors or borders for shapes, or use slide.addImage() in PptxGenJS to layer images.");return}let he=P.borderTopWidth,Z=P.borderRightWidth,pe=P.borderBottomWidth,ke=P.borderLeftWidth,fe=[he,Z,pe,ke].map(Re=>parseFloat(Re)||0),De=fe.some(Re=>Re>0),qe=De&&fe.every(Re=>Re===fe[0]),ze=[];if(De&&!qe){let Re=E(G),it=l(Re.left),pt=l(Re.top),B=l(Re.width),se=l(Re.height);if(parseFloat(he)>0){let Ae=c(he),Fe=Ae/72/2;ze.push({type:"line",x1:it,y1:pt+Fe,x2:it+B,y2:pt+Fe,width:Ae,color:s(P.borderTopColor)})}if(parseFloat(Z)>0){let Ae=c(Z),Fe=Ae/72/2;ze.push({type:"line",x1:it+B-Fe,y1:pt,x2:it+B-Fe,y2:pt+se,width:Ae,color:s(P.borderRightColor)})}if(parseFloat(pe)>0){let Ae=c(pe),Fe=Ae/72/2;ze.push({type:"line",x1:it,y1:pt+se-Fe,x2:it+B,y2:pt+se-Fe,width:Ae,color:s(P.borderBottomColor)})}if(parseFloat(ke)>0){let Ae=c(ke),Fe=Ae/72/2;ze.push({type:"line",x1:it+Fe,y1:pt,x2:it+Fe,y2:pt+se,width:Ae,color:s(P.borderLeftColor)})}}if(U||De){let Re=E(G);if(Re.width>=L.width*.97&&Re.height>=L.height*.97&&U){K.add(G);return}if(Re.width>0&&Re.height>0){let pt=m(P.boxShadow);if(U||qe){let B=U?h(P.backgroundColor,_):{fill:null,transparency:null},se=B.fill,Ae=B.transparency;!se&&qe&&(se=s(P.borderColor)||"2A2A30",Ae=Ae??88),C({type:"shape",text:"",position:{x:l(Re.left),y:l(Re.top),w:l(Re.width),h:l(Re.height)},shape:{fill:se,transparency:Ae,line:qe?{color:s(P.borderColor),width:c(P.borderWidth)}:null,rectRadius:(()=>{let Fe=P.borderRadius,H=parseFloat(Fe);if(H===0)return 0;if(Fe.includes("%")){if(H>=50)return 1;let ue=Math.min(Re.width,Re.height);return H/100*l(ue)}return Fe.includes("pt")?H/72:H/a})(),shadow:pt}},G)}ze.forEach(B=>C(B,G)),K.add(G);return}}}if(G.tagName==="UL"||G.tagName==="OL"){let P=E(G);if(P.width===0||P.height===0)return;let U=Array.from(G.querySelectorAll("li")),le=[],he=r.getComputedStyle(G),Z=c(he.paddingLeft),pe=Z*.5,ke=Z*.5,fe=r.getComputedStyle(U[0]||G),De=s(fe.color),qe=j(G,U,De);U.forEach((Re,it)=>{let pt=it===U.length-1,B=b(Re,{breakLine:!1});B.length>0&&(B[0].text=B[0].text.replace(/^[•\-\*▪▸]\s*/,""),B[0].options.bullet={indent:ke}),B.length>0&&qe&&qe!==De&&B.unshift({text:"\u200B",options:{bullet:{indent:ke},color:qe,fontSize:B[0]?.options?.fontSize||c(fe.fontSize),breakLine:!1}}),B.length>0&&!pt&&(B[B.length-1].options.breakLine=!0),le.push(...B)});let ze=W(G,P,null);C({type:"list",items:le,position:{x:l(ze.x),y:l(ze.y),w:l(ze.w),h:l(ze.h)},style:{fontSize:c(fe.fontSize),fontFace:fe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:De,bulletColor:qe,transparency:d(fe.color),align:fe.textAlign==="start"?"left":fe.textAlign,lineSpacing:fe.lineHeight&&fe.lineHeight!=="normal"?c(fe.lineHeight):null,paraSpaceBefore:0,paraSpaceAfter:c(fe.marginBottom),margin:[pe,0,0,0]}},G),U.forEach(Re=>K.add(Re)),K.add(G);return}if(!ge.includes(G.tagName))return;let Ce=E(G),be=G.textContent.trim();if(Ce.width===0||Ce.height===0||!be)return;if(G.tagName!=="LI"&&/^[•\-\*▪▸○●◆◇■□]\s/.test(be.trimStart())){v.push(`Text element <${G.tagName.toLowerCase()}> starts with bullet symbol "${be.substring(0,20)}...". Use
                or
                  lists instead of manual bullet symbols.`);return}let oe=r.getComputedStyle(G),ye=A(oe.transform,oe.writingMode),{x:Me,y:we,w:Ie,h:Ee}=W(G,Ce,ye),w=oe.fontWeight==="bold"||parseInt(oe.fontWeight,10)>=600,de={fontSize:c(oe.fontSize),fontFace:oe.fontFamily.split(",")[0].replace(/['"]/g,"").trim(),color:T(oe,G),align:oe.textAlign==="start"?"left":oe.textAlign,lineSpacing:c(oe.lineHeight),paraSpaceBefore:c(oe.marginTop),paraSpaceAfter:c(oe.marginBottom),margin:[c(oe.paddingLeft),c(oe.paddingRight),c(oe.paddingBottom),c(oe.paddingTop)]},re=d(oe.color);if(re!==null&&(de.transparency=re),ye!==null&&(de.rotate=ye),G.querySelector("b, i, u, strong, em, span, br")){let P=oe.textTransform,U={};w&&!o(oe.fontFamily)&&(U.bold=!0);let le=b(G,U,[],pe=>p(pe,P));!le.map(pe=>pe.text).join("").trim()&&be&&(le=[{text:p(be,P),options:{...U}}]);let Z={...de};if(Z.lineSpacing){let pe=Math.max(Z.fontSize,...le.map(ke=>ke.options?.fontSize||0));if(pe>Z.fontSize){let ke=Z.lineSpacing/Z.fontSize;Z.lineSpacing=pe*ke}}C({type:G.tagName.toLowerCase(),text:le,position:{x:l(Me),y:l(we),w:l(Ie),h:l(Ee)},style:Z},G)}else{let P=oe.textTransform,U=p(be,P);C({type:G.tagName.toLowerCase(),text:U,position:{x:l(Me),y:l(we),w:l(Ie),h:l(Ee)},style:{...de,bold:w&&!o(oe.fontFamily),italic:oe.fontStyle==="italic",underline:oe.textDecoration.includes("underline")}},G)}K.add(G)});let Q=G=>G==="shape"?0:G==="line"?1:G==="image"?2:3;return ce.sort((G,ee)=>{let Y=(G.zIndex??0)-(ee.zIndex??0);return Y!==0?Y:Q(G.type)-Q(ee.type)}),{background:ne,elements:ce,placeholders:J,errors:v}}var ir={width:1280,height:720},Wh={p:["p"],h1:["h1"],h2:["h2"],h3:["h3"],h4:["h4"],h5:["h5"],h6:["h6"],list:["li"],"merged-text":["span","em","strong","b","i","p","h1","h2","h3","h4","h5","h6"]};function qc(e){return(e?.elements||[]).filter(t=>jc.has(t.type)).length}function Gh(e){let t=new Set;for(let n of e?.elements||[])if(jc.has(n.type))for(let a of Wh[n.type]||[n.type])t.add(a);return t.size?[...t].map(n=>`body[data-pptx-raster="1"] ${n} { color: transparent !important; -webkit-text-fill-color: transparent !important; text-shadow: none !important; }`).join(` -`):""}function ip(e,t=null){let r=zr(e);if(!t||ed(t)===0)return r;let n=ap(t);if(!n||r.includes('data-pptx-raster="1"')&&r.includes("pptx-raster-hide-text"))return r;let a=``;return/<\/head>/i.test(r)?r.replace(/<\/head>/i,`${a}`).replace(/e.querySelectorAll(r),createElement:r=>document.createElement(r),getElementById:r=>e.querySelector(`#${r}`),head:e.querySelector("style")?.parentElement||e,_exportRoot:e}}function dp(){let e=document.createElement("div");e.className="ppt-export-root-host",e.setAttribute("aria-hidden","true"),e.style.cssText=[`width:${or.width}px`,`height:${or.height}px`,"overflow:hidden"].join(";"),op().appendChild(e);let t=e.attachShadow({mode:"open"}),r=document.createElement("div");return r.className="ppt-export-root",r.style.cssText=[`width:${or.width}px`,`height:${or.height}px`,"overflow:hidden"].join(";"),t.appendChild(r),r._exportHost=e,r}function up(e){let t=e?._exportHost||e;t?.isConnected&&t.remove()}async function rd(){await new Promise(e=>{requestAnimationFrame(()=>requestAnimationFrame(e))})}function fp(e,t){let r=new DOMParser().parseFromString(t,"text/html");e.replaceChildren(),r.querySelectorAll("style").forEach(a=>{let i=document.createElement("style");i.textContent=lp(a.textContent||""),e.appendChild(i)});let n=document.createElement("div");if(n.className="ppt-export-body",r.body){for(let a of r.body.attributes)a.name==="class"?n.classList.add(...a.value.split(/\s+/).filter(Boolean)):a.name==="style"?n.style.cssText+=`;${a.value}`:n.setAttribute(a.name,a.value);n.innerHTML=r.body.innerHTML}return n.style.boxSizing="border-box",/\bwidth\s*:/i.test(n.style.cssText)||(n.style.width=`${or.width}px`),/\bheight\s*:/i.test(n.style.cssText)||(n.style.height=`${or.height}px`),e.appendChild(n),n}async function hp(e){let t=zr(e),r=dp(),n=fp(r,t);return await rd(),cp(r,n)}async function $c(e,t,r={}){let n=null;try{let a=await hp(e);n=a._exportRoot,Zc(a,t),await rd();let i=Yc(a),o=Jc(a),l=i.errors||[];l.length&&console.warn("[ppt-live-export] slide overflows canvas; exporting anyway:",l.join("; "));let c={...i,errors:[]},s=o.errors||[];return!s.length||r.allowValidationErrors?{slideData:o,bodyDimensions:c,aggressive:t,warnings:l}:{error:new Error(s.join(` -`))}}finally{n&&up(n)}}async function pp(e,t={}){let r=await $c(e,!1,t);if(r?.slideData)return r;let n=await $c(e,!0,t);if(n?.slideData)return n;throw n?.error||r?.error||new Error("PPT Live slide preparation failed")}async function nd(e,t={}){let r=[];try{for(let[n,a]of e.entries()){if(!a?.html)continue;let i=await pp(a.html,t),o=null,c=ed(i.slideData)===0;if(typeof t.renderRaster=="function")try{typeof t.onRasterProgress=="function"&&t.onRasterProgress(n,a);let s=c?Ds(a):ip(a.html,i.slideData);o=await t.renderRaster(s,n)}catch{o=null}r.push({index:n,slideId:a.id,notes:a,...i,rasterBase64:o,rasterOnly:!!(o&&c)})}return r}finally{sp()}}function Ap(e={}){let t=e.theme||{},r=String(e.title||"Slide").replace(/[<>&]/g,l=>({"<":"<",">":">","&":"&"})[l]||l),n=String(e.subtitle||e.claim||"").replace(/[<>&]/g,l=>({"<":"<",">":">","&":"&"})[l]||l),a=t.background||"#ffffff",i=t.ink||"#111111",o=t.muted||"#666666";return` +`):""}function qh(e,t=null){let r=Mr(e);if(!t||qc(t)===0)return r;let n=Gh(t);if(!n||r.includes('data-pptx-raster="1"')&&r.includes("pptx-raster-hide-text"))return r;let a=``;return/<\/head>/i.test(r)?r.replace(/<\/head>/i,`${a}`).replace(/e.querySelectorAll(r),createElement:r=>document.createElement(r),getElementById:r=>e.querySelector(`#${r}`),head:e.querySelector("style")?.parentElement||e,_exportRoot:e}}function _h(){let e=document.createElement("div");e.className="ppt-export-root-host",e.setAttribute("aria-hidden","true"),e.style.cssText=[`width:${ir.width}px`,`height:${ir.height}px`,"overflow:hidden"].join(";"),jh().appendChild(e);let t=e.attachShadow({mode:"open"}),r=document.createElement("div");return r.className="ppt-export-root",r.style.cssText=[`width:${ir.width}px`,`height:${ir.height}px`,"overflow:hidden"].join(";"),t.appendChild(r),r._exportHost=e,r}function Kh(e){let t=e?._exportHost||e;t?.isConnected&&t.remove()}async function Vc(){await new Promise(e=>{requestAnimationFrame(()=>requestAnimationFrame(e))})}function Qh(e,t){let r=new DOMParser().parseFromString(t,"text/html");e.replaceChildren(),r.querySelectorAll("style").forEach(a=>{let i=document.createElement("style");i.textContent=Xh(a.textContent||""),e.appendChild(i)});let n=document.createElement("div");if(n.className="ppt-export-body",r.body){for(let a of r.body.attributes)a.name==="class"?n.classList.add(...a.value.split(/\s+/).filter(Boolean)):a.name==="style"?n.style.cssText+=`;${a.value}`:n.setAttribute(a.name,a.value);n.innerHTML=r.body.innerHTML}return n.style.boxSizing="border-box",/\bwidth\s*:/i.test(n.style.cssText)||(n.style.width=`${ir.width}px`),/\bheight\s*:/i.test(n.style.cssText)||(n.style.height=`${ir.height}px`),e.appendChild(n),n}async function Zh(e){let t=Mr(e),r=_h(),n=Qh(r,t);return await Vc(),Hh(r,n)}async function Gc(e,t,r={}){let n=null;try{let a=await Zh(e);n=a._exportRoot,Oc(a,t),await Vc();let i=Uc(a),o=Wc(a),l=i.errors||[];l.length&&console.warn("[ppt-live-export] slide overflows canvas; exporting anyway:",l.join("; "));let c={...i,errors:[]},s=o.errors||[];return!s.length||r.allowValidationErrors?{slideData:o,bodyDimensions:c,aggressive:t,warnings:l}:{error:new Error(s.join(` +`))}}finally{n&&Kh(n)}}async function Yh(e,t={}){let r=await Gc(e,!1,t);if(r?.slideData)return r;let n=await Gc(e,!0,t);if(n?.slideData)return n;throw n?.error||r?.error||new Error("PPT Live slide preparation failed")}async function Xc(e,t={}){let r=[];try{for(let[n,a]of e.entries()){if(!a?.html)continue;let i=await Yh(a.html,t),o=null,c=qc(i.slideData)===0;if(typeof t.renderRaster=="function")try{typeof t.onRasterProgress=="function"&&t.onRasterProgress(n,a);let s=c?bs(a):qh(a.html,i.slideData);o=await t.renderRaster(s,n)}catch{o=null}r.push({index:n,slideId:a.id,notes:a,...i,rasterBase64:o,rasterOnly:!!(o&&c)})}return r}finally{Vh()}}function Jh(e={}){let t=e.theme||{},r=String(e.title||"Slide").replace(/[<>&]/g,l=>({"<":"<",">":">","&":"&"})[l]||l),n=String(e.subtitle||e.claim||"").replace(/[<>&]/g,l=>({"<":"<",">":">","&":"&"})[l]||l),a=t.background||"#ffffff",i=t.ink||"#111111",o=t.muted||"#666666";return`
                  ${t}
                  -`}function ky(e){let t=(e.slides||[]).map((r,n)=>`
                  - +`}function ly(e){let t=(e.slides||[]).map((r,n)=>`
                  +
                  `).join(` `);return` - + -${Le(e.title||"PPT Live")} +${Be(e.title||"PPT Live")} ..." - }`;function Xo(e){try{return JSON.stringify(e??{},null,2)}catch{return"{}"}}function Dy(e){return Array.isArray(e?.currentDeck?.slides)&&e.currentDeck.slides.length>0}function Ey(e){let t=e?.operation||"auto";return Dy(e)?` - -## Current operation - -- Operation: ${t} -- \`currentDeck\` is provided. Treat the user instruction as an incremental editing request for the existing deck unless the instruction explicitly asks for a completely new deck. -- \`currentDeck.slides[].slideIndex\` is zero-based. \`currentDeck.slides[].slideNumber\` is one-based and matches what users usually say. -- Use \`currentDeck.activeSlideIndex\` when the instruction says "current slide", "this page", "\u672C\u9875", "\u5F53\u524D\u9875", or similar. -- Decide the affected slide or slides yourself from the instruction, \`currentDeck.targetHints\`, slide titles, claims, notes, and visible text. Do not ask the user which pages to edit. -- Preserve unchanged slides exactly by returning a patch instead of regenerating them. -- Prefer \`deckPatch\` for revision, insertion, and deletion. Return a full \`slides\` array only when the user asks for a whole-deck rewrite or the requested change naturally affects most slides. - -For incremental edits, return this optional patch shape instead of \`slides\`: -{ - "title": "existing or updated deck title", - "language": "zh-CN or en-US", - "outline": ["updated slide title list, optional"], - "researchReport": { - "summary": "what changed", - "verifiedFacts": [], - "assumptions": [], - "warnings": [] - }, - "design": { "stylePhilosophy": "pentagram|muller-brockmann|build|kenya-hara|takram", "theme": "light|dark", "palette": {}, "layoutPrinciples": [] }, - "deckPatch": { - "rationale": "why these slides were selected", - "changedSlideIndexes": [0], - "changes": [ - { - "op": "replace_slide|insert_slide|delete_slide", - "slideId": "existing slide id for replace/delete", - "slideIndex": 0, - "slideNumber": 1, - "afterSlideId": "existing slide id for insert, optional", - "slide": { - "id": "reuse the existing id for replace; create a stable id only for insert", - "role": "cover|content|data|transition|closing", - "narrativeStage": "hook|progression|climax|landing", - "title": "concrete slide title", - "kicker": "short page type", - "claim": "one core message", - "proofObject": "source-backed proof or visual direction", - "supportNote": "source fact, assumption, or verification note", - "sourceNote": "source URL/name or verification note", - "facts": ["verified fact or clearly marked assumption"], - "bullets": ["short visible bullet"], - "metric": { "value": "", "label": "" }, - "chartData": [], - "notes": "speaker notes", - "layout": "cover|brief|evidence|process|comparison|quote|data|closing", - "visualTreatment": "typographic|grid|editorial|white-space|soft-tech|data|process|comparison", - "html": "..." - } - } - ] - } -} - -Patch rules: -- \`replace_slide\`: include a complete replacement \`slide\` with mandatory \`html\`; reuse the original slide id. -- \`insert_slide\`: include a complete new \`slide\` with mandatory \`html\`; place it with \`afterSlideId\`, \`beforeSlideId\`, \`slideIndex\`, or \`slideNumber\`. -- \`delete_slide\`: do not include \`slide\`; target by \`slideId\` plus index/number when available. -- Never return an empty patch. If no change is needed, still make the smallest useful improvement requested by the user. -- If you return a full \`slides\` array during an edit, it must include every final slide in order. Missing unchanged slides will be treated as deleted. -`:` - -## Current operation - -- Operation: ${t} -- No current deck was provided. This is a first-pass deck generation run. Return a complete \`slides\` array. -`}function Ho(e){let t=e?.style||{},r=t.fontFamily||"sans",n=t.density||"standard",a=n==="loose"?"spacious":n,i=t.colorMode||t.theme||"light",o=t.stylePreset||"",l=t.palette,c=r==="serif"?'serif \u2014 use serif typography in every slide HTML (for example Georgia, "Songti SC", "Times New Roman", Cambria). Avoid sans-serif body copy.':'sans-serif \u2014 use clean sans-serif typography in every slide HTML (for example system-ui, "PingFang SC", "Microsoft YaHei", Arial, Helvetica). Avoid serif body copy.',s;a==="compact"?s="compact \u2014 information-forward: body padding 24-32px, line-height 1.2-1.28, and 4-6 concise bullets, metrics, or a two-column grid when the content supports it. Prefer readable tightness over decorative whitespace; never overflow the slide.":a==="spacious"?s="spacious \u2014 the loosest tier, still content-rich: body padding 44-52px, line-height 1.32-1.4, and 2-4 concise bullets or 2-3 short content blocks per slide. Keep clear hierarchy without leaving large empty regions.":s="standard \u2014 balanced professional density: body padding 34-42px, line-height 1.26-1.34, and 3-5 bullets, metrics, or paired columns when useful. Use whitespace to separate sections, not to leave half the slide blank.";let u=` - -## Presentation style preferences (must follow in slides[].html) - -- Font family: ${c} -- Information density: ${s} -- Slide color mode: ${i==="dark"?"dark \u2014 use dark slide backgrounds with light text, high-contrast panels, and a keynote-style atmosphere. Set design.theme to dark and reflect it in every slides[].html background, text, and panel colors.":"light \u2014 use light slide backgrounds with dark text, clean readable contrast, and a professional presentation look. Set design.theme to light and reflect it in every slides[].html background, text, and panel colors."} - -## Hard layout rules (apply to every slides[].html, any style) - -- Zero overflow, enforced by budget: before writing each slide, budget the vertical space \u2014 title block 70-95pt + footer 20-25pt + a mandatory >=36pt (0.5in) bottom safety margin leaves only ~390-420pt for body content. Estimate every block as \`lines x font-size x line-height + paddings\` (tables as \`rows x row-height\`); if the sum exceeds the body budget, cut rows, merge columns, or split the slide. Never shrink fonts below 10px to force-fit content. -- Structural clipping fallback: set \`body { overflow: hidden; }\`, make the root a \`display:flex; flex-direction:column; height:540pt;\` container, and give the stretchable content area \`flex:1; min-height:0; overflow:hidden;\` so a misestimate clips inside its container instead of overflowing the canvas. Every text box larger than 12px must end >=0.5in above the canvas bottom. -- Choose the representation by content shape, judged per slide by which form communicates fastest: comparisons -> tables/matrices, rankings -> CSS horizontal bar charts, trends -> CSS column charts, composition -> \`conic-gradient\` pie/donut, strategy -> SWOT/2x2 grids, processes -> flow diagrams with CSS arrows, milestones -> timelines, single KPIs -> big-number callouts; qualitative reasoning or narrative stays as structured text. Do not write paragraphs where a visual is clearly faster, and do not force decorative charts onto purely qualitative content. Pure HTML/CSS only, label every bar/segment with its value, and pair each visual with a one-line takeaway. -`;if(o){if(u+=` -- Style preset: \`${o}\`. After loading the ppt-design skill, \`Read\` its \`references/style-presets/${o}.md\` (the path is relative to the skill directory reported by the Skill tool) and apply that file in full to every slides[].html: visual identity (palette, typography mood, decorative language, recommended layouts) plus any information-density, language, and page-structure rules the preset defines. When the preset's density or structure rules conflict with the generic density preference above, the preset wins. -`,l)try{u+=`- Style palette (matches the preset; use these exact colors for backgrounds, text, accents, and panels in every slide HTML): ${JSON.stringify(l)} -`}catch{}u+=`- The preset does not suspend the ppt-design core rules: assertion-led titles, one core message per slide, anti-AI-slop rules, the 960pt x 540pt canvas, editable-PPTX constraints, and zero content overflow all still apply. -- Pick the closest of the skill's five design philosophies as the structural grammar for layout, then skin it with the preset. If the preset file cannot be read, keep the palette above and fall back to that philosophy. -`}return u}var Ry=`Your previous response in this session was interrupted before it finished. Continue the task now. If your deliverable is a project file, first inspect what you already wrote (Read it) and rewrite that file completely if it could be incomplete. If your deliverable is a final JSON message, re-emit the complete JSON from scratch \u2014 do not assume any part of the interrupted output was received. - -`,By=` -## MiniApp headless generation rules (mandatory) - -- The user only sees the PPT Live UI. They cannot answer AskUserQuestion prompts, approve tool confirmations, or interact with ControlHub/browser/desktop tools during this run. -- Never call AskUserQuestion, ControlHub, GenerativeUI, ComputerUse*, ReviewPlatform, SessionControl, Playbook, Cron, or MiniappInit. -- If the prompt is ambiguous, choose the best interpretation yourself and record assumptions in project files instead of asking the user. -- Prefer WebSearch/WebFetch for straightforward research. Use Task with an Explore subagent only when multi-source deep research is truly required. -- When you call Task, write a concrete subtask description so progress can be inferred. -`;function Ly(e){let t=Array.isArray(e?.issues)?e.issues.map(String).filter(Boolean):[],r=String(e?.previousFailure||"").trim();return`AUTOMATIC COMPLETION CONTINUATION ${e?.attempt||1}/${e?.maxAttempts||1}. - -The host has verified that the "${e?.stage||"current"}" stage is still incomplete after its normal retry budget. Continue the unfinished task in this user turn; do not restart completed work and do not merely explain what remains. -- Inspect the durable project files first and reuse every valid artifact already present. -- Perform the missing tool calls or rewrites now. -- Do not claim completion until the required artifact exists and satisfies the host checks. -${r?`- Previous verified failure: ${r} -`:""}${t.length?`- Host verification issues: - - ${t.join(` - - `)} -`:""} -`}function Ny(e){let t=e?.style?.palette||{};return JSON.stringify(t,null,2).split(` -`).map(r=>` ${r}`).join(` -`)}function Iy(e){let t=e?.style?.stylePreset||"",r=e?.style?.colorMode||e?.style?.theme||"light",n=t?`references/style-presets/${t}.md`:"",a=Array.isArray(e?.complianceIssues)&&e.complianceIssues.length?`The host rejected the previous plan because required Skill evidence was missing. Complete these exact missing actions before accepting or rewriting project.json: -- ${e.complianceIssues.join(` -- `)} - -`:"",i=`Plan a PPT Live deck. This is the PLANNING phase of a staged pipeline running in a dedicated deck project directory: the workspace root of this session is the deck's \`{{ppt_project_dir}}\`. Research the topic, lock the narrative, design the visual system, write a per-slide brief, and SAVE the complete plan to \`project.json\` at the workspace root. Later turns run in THIS SAME session, but must be able to recover from context compression using the self-contained generation contract in that file. - -1. Call \`Skill('${pr}')\`. This exact stable key is mandatory. Check the tool result reports \`skill_key="${pr}"\`; if it does not, stop with an error. Never invoke or substitute a different PPT/presentation skill. -2. From the skill directory returned by Skill, \`Read\` all mandatory references: \`${Sa.join("`, `")}\`. -${n?`3. \`Read\` the selected style preset \`${n}\` and apply its visual rules when planning.`:"3. No named style preset was supplied; derive one coherent visual system from the user intent and skill."} -4. If the deck is data-heavy, analytical, explanatory, or structurally complex, \`Read\` \`references/data-information-visualization.md\` before drafting slide plans. -5. Use any BitFun research tools needed by the user's prompt. All external research happens NOW; render runs must not re-research. -6. When the plan is final, \`Write\` it to \`project.json\` as one strict JSON object matching the schema below. Do not generate slide HTML in this phase. -7. For a deck with at least 5 pages, choose two visually different \`showcaseSlideNumbers\` that later render first and establish the grammar for the remaining pages. -8. End with one short status line such as "PLAN READY: N slides". Do not paste the JSON into the reply. - -\`project.json\` schema: -{ - "title": "deck title", - "language": "zh-CN or en-US", - "outline": [ - { "id": "slide-01", "title": "slide title", "bullets": [], "slide_id": "slide-01" } - ], - "slide_order": ["slide-01"], - "style": { - "stylePreset": "${t}", - "fontFamily": "exact input value", - "density": "exact input value", - "colorMode": "exact input value", - "palette": {} - }, - "assumptions": ["one-shot assumptions"], - "generationContract": { - "version": 1, - "skillKey": "${pr}", - "skillName": "ppt-design", - "requiredReferences": [ - ${Sa.map(o=>`"${o}"`).join(`, - `)}${n?`, - "${n}"`:""} - ], - "deliveryTarget": "editable-pptx", - "userPrompt": "copy input.instruction exactly", - "userBrief": {}, - "userStyle": {}, - "hardRules": ["compact exact rules distilled from editable-pptx.md"], - "visualGrammar": { - "designRead": "one-line audience, task, visual language, structural grammar", - "visualThesis": "why this brief should look this way; not generic adjectives", - "signatureMove": "one theme-specific device used sparingly across the deck", - "typography": "hierarchy via weight, size, spacing \u2014 not decoration", - "paletteRoles": "semantic role of every supplied color", - "composition": "grid, margins, edge discipline, and dominant visual masses", - "layoutFamilies": ["deck-specific families with different silhouettes"], - "objectStyles": "tables, charts, diagrams, images, quotes, annotations, and sources", - "surfaces": "lines and whitespace first; cards and shadow only when hierarchy requires", - "dataAndDiagrams": "semantics over template shapes", - "densityCurve": "where the deck opens, builds, peaks, releases, and closes", - "copyRegister": "language tone, sentence style, label discipline, and evidence standard", - "antiDefaults": ["brief-specific defaults that would make this deck generic"], - "pageRhythm": "showcase grammar, adjacent-page contrast, and accent discipline" - } - }, - "showcaseSlideNumbers": [1, 3], - "researchReport": { - "summary": "short internal summary safe to show as a product status detail", - "verifiedFacts": ["fact with source note when available"], - "assumptions": ["clearly marked assumption"], - "warnings": ["source or verification warning"] - }, - "design": { - "stylePhilosophy": "pentagram|muller-brockmann|build|kenya-hara|takram", - "theme": "${r==="dark"?"dark":"light"}", - "palette": { -${Ny(e)} - }, - "layoutPrinciples": ["specific visual rules every slide of this deck must share"] - }, - "slidePlans": [ - { - "slideNumber": 1, - "role": "cover|content|data|transition|closing", - "narrativeStage": "hook|progression|climax|landing", - "title": "concrete slide title", - "kicker": "short page type", - "claim": "one core message", - "proofObject": "source-backed proof or visual direction", - "supportNote": "source fact, assumption, or verification note", - "sourceNote": "source URL/name or verification note", - "facts": ["verified fact or clearly marked assumption"], - "bullets": ["short visible bullet"], - "metric": { "value": "", "label": "" }, - "chartData": [], - "notes": "speaker notes", - "layout": "cover|brief|evidence|process|comparison|quote|data|closing", - "visualTreatment": "typographic|grid|editorial|white-space|soft-tech|data|process|comparison", - "contentBrief": "everything the render run needs to build this slide without asking questions: the exact copy or copy direction, the data values to visualize and the recommended visual form (table/bar/column/pie/SWOT/flow/timeline/big-number/structured text), and the layout intent" - } - ] -} - -Plan rules: -- \`slidePlans\` must cover the full deck in final order; \`slideNumber\` is one-based and contiguous. -- \`outline[].slide_id\`, \`slide_order[]\`, and \`slides/slide-XX.html\` numbering must agree. -- Copy the user's original instruction, brief, and all style settings into \`generationContract\`; do not paraphrase away explicit requirements. -- Every \`contentBrief\` must be concrete enough that a render run with no research access can produce an audience-ready slide from it. Put real numbers, names, and source notes into the briefs, not vague directions. -- \`design.layoutPrinciples\` and \`design.palette\` are the consistency contract across render runs \u2014 make them specific. -- Distill the skill, mandatory references, selected preset, and user style into \`generationContract.hardRules\` and \`generationContract.visualGrammar\` so later turns can recover after context compression. -- The editable PPTX rules below are non-negotiable and must appear compactly in \`generationContract.hardRules\`: -${Vo} - -Plan density (quality bar, not a protocol limit): -- Write dense, telegraphic notes, never prose paragraphs. Pack facts, numbers, and names; drop filler words. -- \`contentBrief\`: at most ~400 characters per slide. -- \`facts\`: at most 4 items; \`bullets\`: at most 4 items; each item one short line. -- \`proofObject\`, \`supportNote\`, \`sourceNote\`, \`notes\`: one short sentence each. -- \`researchReport.summary\`: at most ~600 characters; \`verifiedFacts\`/\`assumptions\`/\`warnings\`: at most 12 short items combined. - -Input JSON: -\`\`\`json -${Xo(e)} -\`\`\``;return a+i+Ho(e)}function bf(e){return`slides/slide-${String(e).padStart(2,"0")}.html`}function My(e){let t=(e?.assignedSlides||[])[0]||{},r=t?.slideNumber??"?",n=bf(t?.slideNumber||0);return`Render PPT Live slide ${r}. This turn runs in the SAME deck Agent Session as planning, but you must rely on the durable project contract rather than assuming old context is still present. - -1. \`Read\` \`project.json\` on every page turn. Verify \`generationContract.skillKey\` is exactly \`${pr}\`, then follow its userPrompt, userBrief, userStyle, hardRules, visualGrammar, design system, and assigned slide plan together. -2. If \`showcaseSlideNumbers\` contains already-rendered pages, \`Read\` those slide files before rendering a non-showcase page and reuse their visual grammar without copying their layout mechanically. -3. Do not re-run research or change the planned title, claim, layout, or narrative role. Apply \`plan.design\` and the assigned slide plan together. -4. \`Write\` exactly one complete document to \`${n}\`. Do not modify any other slide. -5. End with "SLIDE ${r} READY"; do not paste HTML into the reply. - -Hard HTML/PPTX constraints: -- The file must be a complete self-contained 960pt \xD7 540pt document with inline CSS only and no remote assets. -- \`body { overflow: hidden; }\`, flex-column root with \`height: 540pt\`, stretchable areas \`flex:1; min-height:0; overflow:hidden;\`. -- Budget the vertical space before writing; body text >= 10px; keep a >=36pt bottom safety margin; never overflow the canvas. -${Vo} -- Slide copy must be audience-ready, never placeholder instructions. -${Ho(e)} - -Current page contract: -\`\`\`json -${Xo({generationContract:e?.generationContract||{},design:e?.design||{},style:e?.style||{},showcaseSlideNumbers:e?.showcaseSlideNumbers||[],assignedSlide:t})} -\`\`\``}function zy(e){let t=(e?.assignedSlides||[])[0]||{},r=t?.slideNumber??"?",n=bf(t?.slideNumber||0);return`Render one PPT Live slide. This is the RENDER phase of a staged pipeline running in a dedicated deck project directory: the workspace root of this session is the deck's \`{{ppt_project_dir}}\`. The plan (research, outline, design system, render guide, and per-slide briefs) is already final and is provided in the input JSON as \`plan\`. This turn must render ONLY slide ${r}. - -1. Call \`Skill('${pr}')\`; verify the result reports the exact stable key. Never substitute another skill. -2. \`Read\` every path in \`plan.generationContract.requiredReferences\`, including mandatory references \`${Sa.join("`, `")}\`, plus the selected style preset when listed. -3. \`Read\` \`project.json\`, then follow \`generationContract\`, \`plan.design\`, and the assigned slide together. Do not re-research or change the planned title, claim, layout, or narrative role. -4. \`Write\` exactly one complete document to \`${n}\`: self-contained 960pt \xD7 540pt HTML with inline CSS and audience-ready copy. -5. End with "SLIDE ${r} READY"; do not paste HTML into the reply. - -Render rules: -- Apply every explicit user style setting from \`generationContract.userStyle\`. -- Keep the HTML compact: no HTML comments, no unused CSS rules, minimal whitespace and indentation. Density of CONTENT is good; padding of MARKUP is not. -${Vo} - -Input JSON: -\`\`\`json -${Xo(e)} -\`\`\``+Ho(e)}function Oy(e){return`Generate or revise a PPT Live deck. The user only sees the PPT Live app UI. - -1. Call \`Skill('${pr}')\` and verify the returned stable key. Never substitute a user or project skill. -2. \`Read\` every mandatory reference \`${Sa.join("`, `")}\` and the selected style preset reference. If this deck has a project file, \`Read\` \`project.json\` and preserve its generationContract. -3. Use research tools only when the edit introduces factual claims not already grounded by the deck. -4. Finish with only one strict JSON object. - -Every slide must include complete \`slides[].html\`: self-contained 960pt \xD7 540pt HTML with inline CSS (ppt-design editable PPTX rules). Slide copy must be audience-ready, never placeholder instructions. -${Vo} +`}var Oo='xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"',af={pptx:``,pdf:`PDF`,html:``,png:``};function of(e){return af[e]||af.html}function sf(e){return{pptx:"#d44726",pdf:"#e01e3c",html:"#0d9488",png:"#2563eb"}[e]||"#475569"}var Ml=new Set,lf="user::bitfun-system::ppt-design";function dy(e){Ml.forEach(t=>{try{t(e)}catch{}})}function uy(e){try{return JSON.stringify(e??{},null,2)}catch{return"{}"}}function fy(e){return Array.isArray(e?.currentDeck?.slides)&&e.currentDeck.slides.length>0}function hy(e={}){let t=[],r=e.fontFamily;r==="serif"?t.push("\u886C\u7EBF\u5B57\u4F53"):r==="sans"&&t.push("\u975E\u886C\u7EBF\u5B57\u4F53");let n=e.density==="loose"?"spacious":e.density;return n==="compact"?t.push("\u7D27\u51D1\u4FE1\u606F\u5BC6\u5EA6"):n==="spacious"&&t.push("\u5BBD\u677E\u7559\u767D"),(e.colorMode||e.theme)==="dark"&&t.push("\u6DF1\u8272\u4E3B\u9898"),e.stylePreset&&t.push(`\u98CE\u683C\u9884\u8BBE: ${e.stylePreset}`),t.length?t.join("\u3001"):""}function py(e){let t=fy(e),r=hy(e?.style),n=e?.instruction||e?.userInput||"",a=t?`\u4F7F\u7528 PPT-Design skill \u7F16\u8F91\u73B0\u6709 PPT\u3002\u7F16\u8F91\u6307\u4EE4\uFF1A${n||"\uFF08\u89C1 currentDeck \u4E0A\u4E0B\u6587\uFF09"}\u3002`:`\u4F7F\u7528 PPT-Design skill \u751F\u6210 PPT\u3002\u7528\u6237\u9700\u6C42\uFF1A${n||"\uFF08\u89C1 input JSON\uFF09"}\u3002`;return r&&(a+=` +\u6837\u5F0F\u504F\u597D\uFF1A${r}\u3002`),a+=` -Return JSON matching this shape: -{ - "title": "deck title", - "language": "zh-CN or en-US", - "outline": ["slide title"], - "researchReport": { - "summary": "short internal summary safe to show as a product status detail", - "verifiedFacts": ["fact with source note when available"], - "assumptions": ["clearly marked assumption"], - "warnings": ["source or verification warning"] - }, - "design": { - "stylePhilosophy": "pentagram|muller-brockmann|build|kenya-hara|takram", - "theme": "light|dark", - "palette": { - "background": "#FAFAF7", - "ink": "#1A1A1A", - "muted": "#666666", - "primary": "#111111", - "accent": "#C84B31", - "panel": "#FFFFFF" - }, - "layoutPrinciples": ["specific visual rules used for this deck"] - }, - "slides": [ - ${Ty} - ] -} +## \u7EA6\u675F +- \u7528\u6237\u53EA\u80FD\u770B\u5230 PPT Live UI\uFF0C\u65E0\u6CD5\u56DE\u7B54\u63D0\u95EE\u3002\u5982\u6709\u6B67\u4E49\u81EA\u884C\u5224\u65AD\u6700\u4F18\u65B9\u6848\u5E76\u8BB0\u5F55\u5047\u8BBE\u3002 +- \u4E0D\u8981\u8C03\u7528 AskUserQuestion\u3001ControlHub\u3001GenerativeUI\u3001ComputerUse \u7B49\u4EA4\u4E92\u5DE5\u5177\u3002 +- \u7814\u7A76\u7528 WebSearch / WebFetch \u5373\u53EF\u3002 +`,t&&(a+="\n## \u7F16\u8F91\u4E0A\u4E0B\u6587\n\n- `currentDeck` \u5DF2\u63D0\u4F9B\u3002\u5C06\u7528\u6237\u6307\u4EE4\u89C6\u4E3A\u5BF9\u73B0\u6709 deck \u7684\u589E\u91CF\u7F16\u8F91\uFF0C\u9664\u975E\u6307\u4EE4\u660E\u786E\u8981\u6C42\u5168\u65B0\u751F\u6210\u3002\n- `currentDeck.slides[].slideNumber` \u662F\u4ECE 1 \u5F00\u59CB\u7684\u9875\u7801\uFF0C\u4E0E\u7528\u6237\u53E3\u8BED\u4E00\u81F4\u3002\n- \u7F16\u8F91\u65F6\u53EA\u91CD\u5199\u53D8\u66F4\u7684 `slides/slide-NN.html` \u6587\u4EF6\uFF0C\u4E0D\u52A8\u5176\u4ED6\u9875\u3002\n"),a+=` Input JSON: \`\`\`json -${Xo(e)} -\`\`\``+Ey(e)+Ho(e)}function Uy(e){let t;return e?.phase==="plan"?t=Iy(e):e?.phase==="slides"?t=e?.inSession?My(e):zy(e):t=Oy(e),e?.completionRecovery?t=Ly(e.completionRecovery)+t:e?.continueAfterInterruption&&(t=Ry+t),By+t}function Wy(e){let t=!1,r=()=>{t||(t=!0,e.agent.onEvent(n=>{!n||typeof n!="object"||Fy(n)}))};e.backend={protocol:"files",async call(n,a,i={}){if(n!=="ppt.generate")throw new Error(`Unsupported PPT Live action: ${n}`);r();let o=Uy(a),l=await e.agent.run(o,{runId:i.idempotencyKey,sessionName:"PPT Live",sessionId:i.sessionId,appDataWorkspace:i.appDataWorkspace});if(!l?.sessionId||!l?.turnId)throw new Error("PPT Live agent backend did not return sessionId/turnId");return{sessionId:l.sessionId,turnId:l.turnId,actionRunId:l.actionRunId||l.turnId}},onEvent(n){Xl.add(n)},offEvent(n){Xl.delete(n)},async cancel(n,a){await e.agent.cancel(n,a)},async turnText(n,a){return{text:(await e.agent.turnText(n,a))?.text||""}},async cancelStaleRuns(){await e.agent.cancelStaleRuns()}}}function wf(e=window.app){!e||e.backend?.call||e.agent?.run&&Wy(e)}var L=zn(),Fn=!1,Tr=null,Dn=[],Ln=0,Ko=!1,mi=!1,Yn=[],Hl=0,Se=e=>document.getElementById(e),it=()=>window.app||{};wf(it());var Wf=2500,es=new Map;function _l(e){try{return localStorage.getItem(e)}catch{return es.has(e)?es.get(e):null}}function Kl(e,t){try{localStorage.setItem(e,t)}catch{es.set(e,t)}}var vi={get:async e=>JSON.parse(_l(e)||"null"),set:async(e,t)=>Kl(e,JSON.stringify(t))};function Gf(){let e=it();return e.storage?e.storage:vi}async function qf(e){let t=Gf();if(t===vi||!it().storage)return t.get(e);try{return await Promise.race([t.get(e),new Promise((r,n)=>setTimeout(()=>n(new Error("storage-timeout")),Wf))])}catch(r){return it().log?.warn?.("Host storage read timed out, using local fallback",{key:e,error:String(r)}),vi.get(e)}}async function rs(e,t){let r=Gf();if(r===vi||!it().storage){await r.set(e,t);return}try{await Promise.race([r.set(e,t),new Promise((n,a)=>setTimeout(()=>a(new Error("storage-timeout")),Wf))])}catch(n){it().log?.warn?.("Host storage write timed out, using local fallback",{key:e,error:String(n)}),await vi.set(e,t)}}async function Gy(){try{Yn=await qy();let e=await qf(Da);if(e){L=cn(e),Jl(L)&&(L=zn(),await rs(Da,{...L,updatedAt:Date.now()}));return}L=zn(),await Ct(!0)}catch(e){it().log?.warn?.("Failed to load PPT Live state",{error:String(e)}),L=zn()}}async function Ct(e=!1){L=cn(L),await rs(Da,{...L,updatedAt:Date.now()}),await ns(e?"autosave":"manual"),e||It(U("saved"))}async function qy(){try{let e=await qf(fs);return Array.isArray(e)?e.map(jf).filter(Boolean).slice(0,40):[]}catch(e){return it().log?.warn?.("Failed to load PPT Live history",{error:String(e)}),[]}}async function ns(e="autosave"){if(!L?.slides?.length||Jl(L))return;let t=Date.now();if(e==="autosave"&&Hl&&t-Hl<15e3)return;Hl=t;let r=jf({id:L.sessionId||gr("deck"),title:L.title||U("blankDeckTitle"),updatedAt:t,slideCount:L.slides.length,reason:e,prompt:L.promptDraft||L.brief?.topic||"",state:qr({...L,generation:{...L.generation,active:!1}})});r&&(Yn=[r,...Yn.filter(n=>n.id!==r.id)].slice(0,40),await rs(fs,Yn),Vf())}function Jl(e){let t=Array.isArray(e?.slides)?e.slides:[];return t.length===1&&!t[0]?.html&&String(t[0]?.id||"").startsWith("agent-working-slide")&&String(e?.title||"")===U("agentWorkingTitle")&&!e?.generation?.active}function jf(e){return!e?.id||!e?.state?null:{id:String(e.id),title:String(e.title||e.state?.title||U("blankDeckTitle")),updatedAt:Number(e.updatedAt||Date.now()),slideCount:Number(e.slideCount||e.state?.slides?.length||0),reason:String(e.reason||"autosave"),prompt:String(e.prompt||e.state?.brief?.topic||""),state:e.state}}function Vf(){let e=Se("historyList");if(e){if(e.innerHTML="",!Yn.length){let t=document.createElement("div");t.className="history-empty",t.textContent=U("historyEmpty"),e.append(t);return}Yn.slice(0,12).forEach(t=>{let r=document.createElement("button");r.type="button",r.className=`history-card${t.id===L.sessionId?" is-active":""}`,r.innerHTML=` - ${xf(t.title)} - ${U("historyMeta",{count:t.slideCount,time:Vy(t.updatedAt)})} - ${t.prompt?`${xf(t.prompt)}`:""} - `,r.addEventListener("click",()=>{jy(t.id)}),e.append(r)})}}async function jy(e){let t=Yn.find(r=>r.id===e);t&&(Ln+=1,await fc(),L=cn(qr(t.state)),L.generation.active=!1,yi(),jt(),Na(L),It(U("historyRestored")),await rs(Da,{...L,updatedAt:Date.now()}))}function Vy(e){let t=new Date(e);if(Number.isNaN(t.getTime()))return"";let r=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),a=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${r}/${n} ${a}:${i}`}function xf(e){return String(e??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function It(e){let t=Se("statusLine");t&&(t.textContent=e)}function ka(e){let t=Se("exportStatus");t&&(t.textContent=e)}function En(e,t){Fn=e,document.querySelector(".ppt-live")?.classList.toggle("is-busy",Fn),document.querySelectorAll("button, input, select, textarea").forEach(n=>{if(!["closePreview","prevPresent","nextPresent"].includes(n.id)){if(n.id==="cancelGeneration"){n.disabled=!Fn,n.hidden=!Fn;return}n.id!=="newDeck"&&(n.disabled=Fn)}});let r=Se("aiStatusPill");r&&(r.textContent=Fn?U("statusPillBusy"):U("statusPillReady"),r.classList.toggle("is-busy",Fn)),t&&It(t)}function Ft(e,t,r){L.generation.current=e,L.generation.steps=L.generation.steps.map(n=>({...n,status:n.id===e?t:n.status})),L.generation.active=t==="running"||L.generation.steps.some(n=>n.status==="running"),Er(L),Rr(L),r&&It(r)}function yi(){L.generation.active=!1,L.generation.current="idle",L.generation.draftedCount=0,L.generation.slideTarget=0,L.generation.eventSeq=0,L.generation.steps=L.generation.steps.map(e=>({...e,status:"pending"})),L.generation.events=[],Er(L),Rr(L)}function Rt(e,t="",r="info"){L.generation=Fi(L.generation||{});let n=typeof e=="string"?{title:e,detail:t,kind:r}:{...e||{}},a=nn(n.title||n.label||n.message||U("processEventUnknown"),160),i=nn(n.detail??t??"",260),o=String(n.kind||r||"info").toLowerCase().replace(/[^a-z0-9-]/g,"")||"info";if(!a&&!i)return;let l=Array.isArray(L.generation.events)?L.generation.events:[],c=l[l.length-1];if(c&&c.title===a&&c.detail===i&&c.kind===o)c.timestamp=Date.now(),L.generation.events=l;else{let s=l.reduce((u,h)=>Math.max(u,Number(h.seq)||0),0),d=Math.max(Number(L.generation.eventSeq)||0,s)+1;L.generation.eventSeq=d,L.generation.events=[...l,{id:gr("generation-event"),seq:d,title:a||U("processEventUnknown"),detail:i,kind:o,timestamp:Date.now()}].slice(-80)}Er(L),Rr(L)}function jt(){L=cn(L),Wc(L,Jt),Vf()}function an(e={}){Kc(L,e),L=cn(L)}function Xf(){return Se("topicInput")?.value.trim()||""}function $l(){let e=xc().join(` -`);return!L.outline.length||L.outline.join(` -`)===e||L.title===U("defaultDeckTitle")||as()}function as(){let e=String(L.title||"").trim();return L.slides.length===1&&L.outline.length===1&&L.outline[0]===U("newSlideTitle")&&(e===U("blankDeckTitle")||e===U("newSlideTitle"))}function Nn(){return Array.isArray(L.slides)&&L.slides.length>0&&!$l()&&!as()&&!Jl(L)}async function Xy(){await ec()}async function Hy(){await ec()}async function ec(){if(Ko||mi)return;let e=Xf();if(!e){It(U("promptRequired"));return}Ko=!0;let t=Nn();L.promptDraft=e,L.lastSubmittedPrompt=e,an({includeTopic:!t}),t||(L.brief.topic=e);try{await lc("auto",e,{includeTopic:!t,persistBeforeRun:!0});return}catch(r){if(is(r))return;it().log?.warn?.("PPT Live backend generation failed",{error:String(r)}),rc(r),jt(),await Ct(!0)}finally{Ko=!1}}function tc(e=U("deckReady")){L.generation.active=!1,L.generation.draftedCount=L.slides.length,L.generation.slideTarget=0,L.generation.steps=(L.generation.steps||[]).map(t=>({...t,status:t.status==="error"?"error":"done"})),It(e),Er(L),Rr(L)}function _y(e=U("backendGenerationFailed"),t=""){L.generation.active=!1,L.generation.steps=(L.generation.steps||[]).map(r=>({...r,status:r.status==="done"?"done":"error"})),It(e),Rt({title:e,detail:t||U("agentOnlyRetryHint"),kind:"error"}),En(!1),Er(L),Rr(L)}function Ky(e,t=5){let r=[],n=new Set,a=e;for(let i=0;a&&i({kind:n.kind,title:n.title,url:n.url,text:String(n.text||"").slice(0,6e3)}))}:null);let r=Number(L.brief?.slideTarget)||0;return r>0&&(t.slideTarget=r),t}function on({includePreset:e=!0}={}){let t=ls(L.style?.stylePreset),r=L.style?.colorMode==="dark"?"dark":"light",n={fontFamily:L.style?.fontFamily==="serif"?"serif":"sans",density:Mn(L.style?.density),colorMode:r,theme:r,palette:pc(t,r)};return e&&(n.stylePreset=L.style?.stylePreset||sn),n}function Sf(e={}){let t=on();return{...e.style||{},...t}}function Cf(e={}){let t=on();return{...e.design||{},theme:t.colorMode==="dark"?"dark":"light",palette:{...e.design?.palette||{},...t.palette}}}function kf(e={}){let t=on();return{...e.generationContract||{},userStyle:t}}function Qy(e){let t=String(e||"").trim();if(!t)return"";try{let r=new DOMParser().parseFromString(t,"text/html");return r.querySelectorAll("style,script,svg").forEach(n=>n.remove()),nn(r.body?.textContent||r.documentElement?.textContent||"",1800)}catch{return nn(t.replace(/<[^>]+>/g," "),1800)}}function Zy(e){let t=new Set,r=String(e||""),n=$t(L);return/(当前|本页|这一页|此页|current\s+(slide|page)|this\s+(slide|page))/i.test(r)&&t.add(n),[/第\s*(\d{1,2})\s*(页|頁|张|張)/gi,/\b(?:slide|page)\s*(\d{1,2})\b/gi,/\b(\d{1,2})\s*(?:slide|slides|page|pages)\b/gi].forEach(i=>{let o=i.exec(r);for(;o;){let l=Number(o[1])-1;l>=0&&li-o)}function Yy(e){let t=Zy(e),r=$t(L),n=new Set(t.length?t:[r]);return{title:L.title,outline:qr(L.outline||[]),slideCount:L.slides.length,activeSlideIndex:r,activeSlideId:L.slides[r]?.id||"",targetHints:t.map(a=>({slideIndex:a,slideNumber:a+1,slideId:L.slides[a]?.id||"",title:L.slides[a]?.title||""})),slides:L.slides.map((a,i)=>{let o=a.html?Qy(a.html):nn((a.elements||[]).flatMap(c=>[c.text,c.label,...Array.isArray(c.items)?c.items:[]]).filter(Boolean).join(` -`),1800),l={slideIndex:i,slideNumber:i+1,id:a.id,title:a.title,kicker:a.kicker,claim:a.claim,proofObject:a.proofObject,supportNote:a.supportNote,sourceNote:a.sourceNote,notes:a.notes,layout:a.layout,visibleText:o,hasHtml:!!a.html};return n.has(i)&&a.html&&(l.html=String(a.html).slice(0,12e3)),l})}}function Pf(e,t){let r=t.getBoundingClientRect(),n=mt((e-r.left)/r.width,0,1);return Math.round(n*2)}function Pn(e){Ui(Pi(mt(Math.round(Number(e)),0,2)))}function Jy(){_c(L),L=cn(L)}var rr=4,Bn=8,Pa=rr+Bn,$y=750;function ac(e){let t=e>rr;return{continuation:t,continuationAttempt:t?e-rr:0}}function Hf(e,t,r,n=[]){let a=ac(t);return a.continuation?{stage:e,attempt:a.continuationAttempt,maxAttempts:Bn,previousFailure:Rn(r),issues:[...new Set((n||[]).map(String).filter(Boolean))]}:null}function _f(e,t,r=[],n={}){let a=[...new Set(r.map(c=>Rn(c)).filter(Boolean))],o=Rn(t)||a.at(-1)||"The Agent did not produce the required artifact.",l=new Error(`PPT Live recovery exhausted after ${rr} stage attempts and ${Bn} continuation turns. Last verified reason: ${o}`,{cause:t||void 0});return l.pptLiveRecoveryExhausted={stageKey:e,stageVars:n,stepAttempts:rr,continuationAttempts:Bn,reasons:a},l}function ic(e){let t=String(e?.message||e||"");return!(is(e)||/Generation stopped/i.test(t)||/backend is unavailable|did not return sessionId/i.test(t)||/permission|workspacePath is required|unsupported PPT Live action/i.test(t))}function oc(e,t){let r=String(e?.message||e||"");return/rate limit|network|timed? out|connection|temporar|overload|service unavailable|502|503|504/i.test(r)?Math.min(15e3,1e3*2**Math.min(Math.max(0,t-1),4)):$y}function sc(e){return/Unknown MiniApp agent session|session workspace does not match/i.test(String(e?.message||e||""))}function Kf(){let e=it();return e.backend?.protocol==="files"&&!!e.appDataDir&&!!e.fs?.readFile}function Qf(){let e=`deck-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;return{runId:e,workspaceSubdir:`decks/${e}`,dir:`${it().appDataDir}/decks/${e}`}}function eb(){let e=String(L.agentSession?.workspaceSubdir||"");return!e||!it().appDataDir?null:{runId:String(L.agentSession?.runId||e.split("/").pop()||""),workspaceSubdir:e,dir:`${it().appDataDir}/${e}`}}function tb(e){return`slides/slide-${String(e).padStart(2,"0")}.html`}var rb=["All visible text must be inside p, h1-h6, or li; DIV cannot contain direct text and span is inline-only.","CSS gradients are forbidden; use solid fills and discrete shapes.","Backgrounds, borders, and shadows belong on DIV shapes, never on text or inline elements.","DIV background-image is forbidden; use img. Inline text elements cannot carry box spacing or decorative fills.","Use a complete self-contained 960pt x 540pt document with no remote assets and no canvas overflow.","Text larger than 12px must keep at least a 36pt bottom safety margin."];function Zf(e){return typeof e=="string"?e:String(e?.title||"")}function Ff(e){return Array.isArray(e?.outline)?e.outline.map(Zf).filter(Boolean):[]}function Yf(e){let t=[...Sa];return e?.stylePreset&&t.push(`references/style-presets/${e.stylePreset}.md`),t}function nb(e,t){let r=th(e).map((c,s)=>({...c,slideNumber:dc(c,s+1),slideId:String(c?.slideId||`slide-${String(s+1).padStart(2,"0")}`)})),n=on(),a=nc(),i=r.map(c=>({id:c.slideId,title:String(c.title||""),bullets:Array.isArray(c.bullets)?c.bullets.map(String):[],slide_id:c.slideId})),o=Array.isArray(e?.showcaseSlideNumbers)?e.showcaseSlideNumbers.map(Number):[],l=[...new Set(o)].filter(c=>Number.isInteger(c)&&c>=1&&c<=r.length).slice(0,2);if(r.length>=5&&l.length<2){for(let c of[1,Math.min(3,r.length),r.length])if(l.includes(c)||l.push(c),l.length===2)break}return{...e,outline:i,slide_order:i.map(c=>c.slide_id),style:n,design:{...e?.design||{},theme:n.colorMode==="dark"?"dark":"light",palette:{...e?.design?.palette||{},...n.palette}},assumptions:Array.isArray(e?.assumptions)?e.assumptions.map(String):(e?.researchReport?.assumptions||[]).map(String),generationContract:{...e?.generationContract||{},version:1,skillKey:pr,skillName:"ppt-design",requiredReferences:Yf(n),deliveryTarget:"editable-pptx",userPrompt:String(t||""),userBrief:a,userStyle:n,hardRules:rb,visualGrammar:{...e?.generationContract?.visualGrammar||{},...e?.design?.renderGuide||{}}},showcaseSlideNumbers:r.length>=5?l:[],slidePlans:r}}async function ab(e,t){let r=it().fs;!e||!r?.writeFile||await r.writeFile(`${e.dir}/project.json`,`${JSON.stringify(t,null,2)} -`)}async function Jf(e,t){let r=it().fs;if(!r?.readFile)throw new Error("PPT Live fs API is unavailable");return await r.readFile(`${e.dir}/${t}`)}async function ib(e,t){try{let r=String(await Jf(e,t)||"");return r.trim()?Ta(r):null}catch{return null}}async function ob(e){return await ib(e,"project.json")}async function Tf(e,t){try{let r=String(await Jf(e,tb(t))||"").trim();return!r||!/<\/html>\s*$/i.test(r)?null:r}catch{return null}}function Df(e){return e?.params&&typeof e.params=="object"?e.params:{}}function sb(e){let t=String(e||"").trim();return t===pr||t==="ppt-design"}function lb(e){if(!e||String(e.toolName||"").toLowerCase()!=="skill")return!1;let t=e.result||{};return!!(JSON.stringify(t).includes(pr)||t?.success===!0&&String(t?.skill_name||"").toLowerCase()==="ppt-design")}function cb(e,t){let r=(e||[]).filter(s=>s.eventType==="Started"),n=(e||[]).filter(s=>s.eventType==="Completed"),a=new Map(n.filter(s=>s.toolId).map(s=>[s.toolId,s])),i=[],o=r.filter(s=>String(s.toolName||"").toLowerCase()==="skill"&&sb(Df(s).command)),l=o.some(s=>lb(a.get(s.toolId)));(!o.length||!l)&&i.push(`The exact built-in Skill key ${pr} was not successfully loaded.`);let c=r.filter(s=>String(s.toolName||"").toLowerCase()==="read"&&a.has(s.toolId)).map(s=>{let d=Df(s);return String(d.file_path||d.path||"")});return Yf(t).forEach(s=>{c.some(d=>d.endsWith(s))||i.push(`Required reference was not read: ${s}`)}),i}async function $f(e){let t=it().fs;if(!(!t?.readdir||!t?.rm))try{let r=`${it().appDataDir}/decks`,n=await t.readdir(r),a=(Array.isArray(n)?n:[]).map(i=>typeof i=="string"?i:i?.name).filter(i=>typeof i=="string"&&i.startsWith("deck-")&&i!==e);for(let i of a)await t.rm(`${r}/${i}`,{recursive:!0})}catch{}}async function lc(e,t,r={}){if(!it().backend?.call)throw new Error("PPT Live backend is unavailable");if(!mi){mi=!0;try{if(an({includeTopic:r.includeTopic!==!1}),Jy(),r.persistBeforeRun&&await Ct(!0),e==="auto"&&($l()||as())){await Ab(e,t);return}await db(e,t)}finally{mi=!1}}}async function db(e,t){let r=null,n=eb()||(Kf()?Qf():null);n&&!L.agentSession?.workspaceSubdir&&await $f(n.runId);let a={id:L.agentSession?.id||null,project:n};for(let i=1;i<=rr;i+=1)try{await fb(e,t,i,a);return}catch(o){if(r=o,sc(o)?a.id=null:o?.pptLiveSessionId&&(a.id=o.pptLiveSessionId),!ic(o)||i>=rr)throw o;it().log?.warn?.("PPT Live backend attempt failed, retrying",{attempt:i,maxAttempts:rr,continueInSession:!!a.id,error:String(o)}),Rt({title:U("generationRetrying",{attempt:i+1,max:rr}),detail:Rn(o),kind:"error"}),It(U("generationRetrying",{attempt:i+1,max:rr})),await new Promise(l=>setTimeout(l,oc(o,i)))}if(r)throw r}async function cc(e,t={},r={}){let n=it(),a=Ln,i=null,o=null,l="",c="",s=!1,d=0,u=null,h=[],f=new Set,p=[],A=new Set,y=yb(),m={lastEventAt:Date.now()};try{let b=await n.backend.call("ppt.generate",e,{entityId:"deck",idempotencyKey:`ppt-live-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,sessionId:r.sessionId||void 0,appDataWorkspace:r.appDataWorkspace||void 0});if(i=b?.sessionId||null,o=b?.turnId||b?.actionRunId||null,!i||!o)throw new Error("PPT Live backend did not return sessionId/turnId");if(Tb(i,o),Tn(a))throw new Error("Generation stopped");let x=new Promise((G,j)=>{let S=v=>{let z=v.sessionId,ae=z===i,_=String(v.sourceEvent||"");if(_.endsWith("subagent-session-linked")){v.parentSessionId===i&&z&&(A.add(z),Rt({title:U("eventSubagentStarted"),detail:"",kind:"tool"}));return}let ce=A.has(z);if(!(!ae&&!ce)&&!(ae&&v.turnId&&v.turnId!==o)){if(m.lastEventAt=Date.now(),!ae&&z&&A.add(z),_.endsWith("dialog-turn-started"))y.note(U("eventTurnStarted"),"","turn");else if(_.endsWith("model-round-started"))ae?t.onToolPhase?.("round"):y.note(U("eventSubagentWorking"),"","pulse",8e3),y.touch();else if(_.endsWith("model-round-completed"))y.touch();else if(_.endsWith("tool-event")){let J=uc(v.toolEvent||{}),me=J.event_type||J.eventType||"",K=String(J.tool_name||J.toolName||"").trim().toLowerCase();if(ae&&(me==="Started"?p.push({eventType:me,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",params:J.params||{}}):me==="Completed"?p.push({eventType:me,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",result:J.result||{}}):(me==="Failed"||me==="Cancelled")&&p.push({eventType:me,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",error:J.error||J.message||me})),me==="Started"&&K==="task"&&(y.note(U("eventToolTaskStarted"),"","tool"),y.touch()),vb(J,f,{isSubagent:!ae})){let Q=Fb(v,{isSubagent:!ae});Q&&Rt(Q),y.touch()}if(ae&&(me==="EarlyDetected"||me==="Started"))t.onToolPhase?.("detected");else if(ae&&me==="Completed"){let Q=K;t.onToolPhase?.("completed"),Q==="skill"?y.note(U("eventToolSkillReady"),"","phase"):(Q==="websearch"||Q==="webfetch")&&t.onToolPhase?.("research")}}else if(_.endsWith("text-chunk")){let J=String(v.text||"");if(v.contentType==="thinking")c+=J;else{l+=J,y.touch();let K=Date.now();K-d>=500&&(d=K,t.onTextProgress?.(l))}}else if(!_.endsWith("token-usage-updated")){if(_.endsWith("dialog-turn-completed")){if(!ae){y.note(U("eventSubagentDone"),"","tool");return}s=!0,u={success:v.success,finishReason:v.finishReason||v.finish_reason||"",partialRecoveryReason:v.partialRecoveryReason||v.partial_recovery_reason||""},G({answer:l,thinking:c})}else if(_.endsWith("dialog-turn-failed")||_.endsWith("dialog-turn-cancelled")){if(!ae){y.note(U("eventSubagentFailed"),"","error");return}s=!0,l&&t.onTextProgress?.(l);let J=nn(v.error||v.message||"");Rt({title:_.endsWith("dialog-turn-cancelled")?U("eventTurnCancelled"):U("eventTurnFailed"),detail:J,kind:"error"}),j(new Error(J||_))}}}};n.backend.onEvent(S),h.push(()=>n.backend.offEvent?.(S));let V=setInterval(()=>{if(s||Date.now()-y.lastProgressLogAt<12e3)return;let z=(L.generation?.steps||[]).find(ae=>ae.status==="running");y.note(z?.label?`${z.label}\u2026`:U("generationProgressPulse"),z?.detail||"","pulse",0)},12e3);h.push(()=>clearInterval(V))}),C=r.resultKind!=="text",F=await Mb(x,i,o,m,{expectJson:C}),I=typeof F=="string"?F:F?.answer||"",B=typeof F=="string"?"":F?.thinking||"";if(Tn(a))throw new Error("Generation stopped");if(!C)return{payload:null,text:I,sessionId:i,toolTrace:p,completion:u};let D=await zb(i,o,I,B);if(Tn(a))throw new Error("Generation stopped");let T=Ta(D);if(Tn(a))throw new Error("Generation stopped");return{payload:T,sessionId:i,toolTrace:p,completion:u}}catch(b){if(b&&typeof b=="object"&&i&&(b.pptLiveSessionId=i,b.pptLiveToolTrace=p),!s&&i&&o&&n.backend?.cancel)try{await n.backend.cancel(i,o)}catch(x){it().log?.warn?.("PPT Live backend cancel after failure failed",{sessionId:i,turnId:o,error:String(x)})}throw b}finally{h.forEach(b=>b()),i&&o&&Db(i,o)}}function eh(e,t){return{operation:e,instruction:t,locale:Dr(),brief:nc(),style:on()}}function ub(e){return{operation:e,locale:Dr(),brief:nc({includeEvidence:!1}),style:on({includePreset:!1})}}async function fb(e,t,r=1,n={id:null}){let a=Ln;En(!0,U("working")),yi(),Ft("brief","running",U("generationReadingBrief")),Rt({title:U("processEventStarted"),detail:U("processEventWaiting"),kind:"start"}),r>1&&Rt({title:U("generationRetryAttempt",{attempt:r,max:rr}),detail:"",kind:"start"}),rh(e,t);let i=!1,o={value:""},l={touch:()=>{},note:()=>{},lastProgressLogAt:0};try{let{payload:c,sessionId:s}=await cc({...eh(e,t),title:L.title,outline:qr(L.outline),currentSlideIndex:$t(L),currentDeck:Yy(t),...n?.id?{continueAfterInterruption:!0}:{}},{onToolPhase:d=>{d==="detected"?Ft("brief","running",U("generationReadingBrief")):d==="completed"?(Ft("brief","done"),Ft("spine","running",U("generationWritingClaims"))):d==="research"?Ft("proof","running",U("generationChoosingProof")):d==="round"&&Ft("spine","running",U("generationWritingClaims"))},onTextProgress:d=>Pb(d,l,o)},{sessionId:n?.id||void 0,appDataWorkspace:n?.project?.workspaceSubdir});n.id=s||n.id,L.agentSession={id:n.id||"",workspaceSubdir:n?.project?.workspaceSubdir||"",runId:n?.project?.runId||"",skillKey:pr},Rt({title:U("generationParsingDeck"),detail:"",kind:"parsing"}),It(U("generationParsingDeck")),Qo(c),await ns(`agent:${e}`),Rt({title:U("processEventDone"),detail:"",kind:"done"}),Ft("spine","done"),Ft("proof","done"),Ft("design","done"),Ft("compile","done",U("generationCompiled")),tc(U("deckReady")),i=!0,jt(),await Ct(!0)}finally{!Tn(a)&&(L.generation.active&&!i&&(L.generation.active=!1),En(!1)),Er(L),Rr(L)}}function dc(e,t){let r=Number(e?.slideNumber);return Number.isFinite(r)&&r>0?Math.round(r):t}function th(e){return Array.isArray(e?.slidePlans)?e.slidePlans.filter(t=>t&&typeof t=="object"):[]}async function hb(e,t,r=null){let n=null,a=null,i=[],o=[],l=[];for(let c=1;c<=Pa;c+=1)try{let s=ac(c);c>1&&(Rt({title:s.continuation?U("generationRecoveryContinuing",{stage:U("generationStagePlanning"),attempt:s.continuationAttempt,max:Bn}):U("generationPlanRetry",{attempt:c,max:rr}),detail:l.join(" ")||Rn(n),kind:"error"}),It(s.continuation?U("generationRecoveryContinuing",{stage:U("generationStagePlanning"),attempt:s.continuationAttempt,max:Bn}):U("generationPlanRetry",{attempt:c,max:rr})),await new Promise(A=>setTimeout(A,oc(n,c))));let d=Hf("plan",c,n,l),u=await cc({...eh(e,t),phase:"plan",title:L.title,outline:[],...l.length?{complianceIssues:l}:{},...c>1&&!d&&a?{continueAfterInterruption:!0}:{},...d?{completionRecovery:d}:{}},{onToolPhase:A=>{A==="detected"?Ft("brief","running",U("generationReadingBrief")):A==="completed"||A==="round"?(Ft("brief","done"),Ft("spine","running",U("generationWritingClaims"))):A==="research"&&Ft("proof","running",U("generationChoosingProof"))},onTextProgress:A=>{if(!A.includes('"slidePlans"'))return;let y=(A.match(/"slideNumber"/g)||[]).length;y>0?(Ft("proof","running",U("generationPlanProgress",{count:y})),It(U("generationPlanProgress",{count:y}))):Ft("proof","running",U("generationPlanningSlides"))}},{sessionId:a||void 0,appDataWorkspace:r?.workspaceSubdir,resultKind:r?"text":void 0});a=u.sessionId||a,i.push(...u.toolTrace||[]);let h=r?await ob(r):u.payload;if(r&&!h)throw new Error("PPT Live plan turn finished without a valid project.json");if(l=cb(i,on()),l.length){let A=new Error(`PPT Live planning compliance failed: ${l.join(" ")}`);throw A.pptLivePlanCompliance=!0,A.pptLiveSessionId=u.sessionId||a||null,A}let f=nb(h,t),p=th(f);if(!p.length)throw new Error("PPT Live plan phase returned no slidePlans");return await ab(r,f),{payload:f,slidePlans:p,sessionId:u.sessionId||a||null,project:r}}catch(s){if(n=s,o.push(s),Array.isArray(s?.pptLiveToolTrace)&&i.push(...s.pptLiveToolTrace),sc(s)?a=null:s?.pptLiveSessionId&&(a=s.pptLiveSessionId),!ic(s))throw s;if(c>=Pa)throw _f("generationStagePlanning",s,o);it().log?.warn?.("PPT Live plan phase failed, retrying",{attempt:c,maxAttempts:Pa,continueInSession:!!a,error:String(s)})}throw n}async function pb({operation:e,planContext:t,slidePlan:r,slideNumber:n,session:a,project:i}){let o=null,l=[],c=(s,d={score:100,issues:[]})=>({...r,html:s,quality:d,id:`ppt-live-slide-${n}`,slideNumber:n});for(let s=1;s<=Pa;s+=1)try{let d=ac(s);if(s>1){if(i){let b=await Tf(i,n);if(b)return c(b)}Rt({title:d.continuation?U("generationRecoveryContinuing",{stage:U("generationStageSlide",{slide:n}),attempt:d.continuationAttempt,max:Bn}):U("generationSlideRetry",{slide:n,attempt:s,max:rr}),detail:Rn(o),kind:"error"}),It(d.continuation?U("generationRecoveryContinuing",{stage:U("generationStageSlide",{slide:n}),attempt:d.continuationAttempt,max:Bn}):U("generationSlideRetry",{slide:n,attempt:s,max:rr})),await new Promise(b=>setTimeout(b,oc(o,s)))}let u=Hf("slides",s,o,o?[Rn(o)]:[]),f=!!a?.id?{operation:e,locale:Dr(),phase:"slides",inSession:!0,assignedSlides:[r],generationContract:kf(t),design:Cf(t),style:Sf(t),showcaseSlideNumbers:t.showcaseSlideNumbers,...s>1&&!u?{continueAfterInterruption:!0}:{},...u?{completionRecovery:u}:{}}:{...ub(e),phase:"slides",plan:{...t,style:Sf(t),design:Cf(t),generationContract:kf(t)},assignedSlides:[r],...u?{completionRecovery:u}:{}},p=await cc(f,{},{sessionId:a?.id||void 0,appDataWorkspace:i?.workspaceSubdir,resultKind:i?"text":void 0});a&&p.sessionId&&(a.id=p.sessionId);let{payload:A}=p;if(i){let b=await Tf(i,n);if(!b)throw new Error(`PPT Live slide ${n} file is missing or incomplete`);return c(b)}let y=Array.isArray(A?.slides)?A.slides:[],m=y.find(b=>dc(b,NaN)===n)||y[0];if(!m||typeof m!="object"||!String(m.html||"").trim())throw new Error(`PPT Live slide ${n} is missing complete HTML`);return{...m,id:m.id||`ppt-live-slide-${n}`,slideNumber:n}}catch(d){if(o=d,l.push(d),a&&sc(d)&&(a.id=null,it().log?.warn?.("PPT Live planning session lost, falling back to standalone render turns",{slide:n})),!ic(d))throw d;if(s>=Pa)throw _f("generationStageSlide",d,l,{slide:n});it().log?.warn?.("PPT Live slide render failed, retrying page",{slide:n,attempt:s,maxAttempts:Pa,continueInSession:!!a?.id,error:String(d)})}throw o}async function Ab(e,t){let r=Ln;En(!0,U("working")),yi(),Ft("brief","running",U("generationReadingBrief")),Rt({title:U("processEventStarted"),detail:U("processEventWaiting"),kind:"start"}),Rt({title:U("generationPlanPhase"),detail:"",kind:"phase"}),rh(e,t);let n=!1,a=Kf()?Qf():null;a&&await $f(a.runId);try{let{payload:i,slidePlans:o,sessionId:l}=await hb(e,t,a);if(Tn(r))throw new Error("Generation stopped");Ft("brief","done"),Ft("spine","done"),Ft("proof","done"),Ft("design","running",U("generationDesigningLayouts")),L.generation.slideTarget=o.length,L.generation.draftedCount=0,Rt({title:U("generationPlanReady",{count:o.length}),detail:nn(Ff(i).join(" / "),200),kind:"phase"}),i.title&&(L.title=String(i.title),jt());let c={id:l||null};L.agentSession={id:c.id||"",workspaceSubdir:a?.workspaceSubdir||"",runId:a?.runId||"",skillKey:pr};let s={title:i.title||"",language:i.language||"",outline:i.outline||[],researchReport:i.researchReport||"",design:i.design||{},style:i.style||on(),generationContract:i.generationContract||{},showcaseSlideNumbers:i.showcaseSlideNumbers||[],slideOrder:i.slide_order||[]},d=o.map((y,m)=>({...y,slideNumber:dc(y,m+1)})),u=new Set(s.showcaseSlideNumbers),h=[...d.filter(y=>u.has(y.slideNumber)),...d.filter(y=>!u.has(y.slideNumber))];Rt({title:U("generationSlidesPhase",{count:d.length}),detail:"",kind:"phase"});let f=[];for(let y of h){if(Tn(r))throw new Error("Generation stopped");let m=y.slideNumber;Ft("design","running",U("generationRenderingSlide",{slide:m,total:d.length})),It(U("generationRenderingSlide",{slide:m,total:d.length}));try{let x=await pb({operation:e,planContext:s,slidePlan:y,slideNumber:m,session:c,project:a});f.push(x)}catch(x){if(!f.length)throw x;let C=[...f].sort((D,T)=>D.slideNumber-T.slideNumber);Qo({title:i.title,language:i.language,outline:[],researchReport:i.researchReport,design:i.design,slides:C}),L.activeSlideId=L.slides[L.slides.length-1]?.id||L.activeSlideId,L.selectedElementId="",jt(),await Ct(!0);let F=new Set(f.map(D=>D.slideNumber)),I=d.filter(D=>!F.has(D.slideNumber)).map(D=>D.slideNumber),B=new Error(U("generationPartialDeck",{missing:I.join(", ")}));throw B.pptLivePartialDeck=!0,B.cause=x,x?.pptLiveRecoveryExhausted&&(B.pptLiveRecoveryExhausted=x.pptLiveRecoveryExhausted),B}L.generation.draftedCount=f.length,Ft("design","running",U("generationSlideReady",{slide:m,total:d.length})),Rt({title:U("generationSlideReady",{slide:m,total:d.length}),detail:"",kind:"slide"});let b=[...f].sort((x,C)=>x.slideNumber-C.slideNumber);Qo({title:i.title,language:i.language,outline:b.map(x=>x.title||""),researchReport:i.researchReport,design:i.design,slides:b}),L.activeSlideId=`ppt-live-slide-${m}`,L.selectedElementId="",jt()}let A=[...f].sort((y,m)=>y.slideNumber-m.slideNumber);L.agentSession.id=c.id||L.agentSession.id,Rt({title:U("generationParsingDeck"),detail:"",kind:"parsing"}),It(U("generationParsingDeck")),Qo({title:i.title,language:i.language,outline:Ff(i),researchReport:i.researchReport,design:i.design,slides:A}),await ns(`agent:${e}`),Rt({title:U("processEventDone"),detail:"",kind:"done"}),Ft("design","done"),Ft("compile","done",U("generationCompiled")),tc(U("deckReady")),n=!0,jt(),await Ct(!0)}finally{!Tn(r)&&(L.generation.active&&!n&&(L.generation.active=!1),En(!1)),Er(L),Rr(L)}}function rh(e,t){It(U("generationAgentWorking")),Rt({title:U("generationAgentWorking"),detail:nn(t||""),kind:"start"}),e==="auto"&&($l()||as())&&(L.title=U("agentWorkingTitle")),jt()}var gb=new Set(["ParamsPartial","Queued","Waiting","Progress","Streaming","StreamChunk","Confirmed","Rejected","EarlyDetected","Started"]),mb=new Set(["read","write","grep","glob","list","todowrite","todo_write","skill","bash","shell","edit","delete","apply_patch","strreplace","search_replace"]);function nh(e,t={}){let r=String(e||"").trim().toLowerCase();return!r||mb.has(r)?"":r==="websearch"?t.isSubagent?U("eventSubagentWebSearchDone"):U("eventToolWebSearchDone"):r==="webfetch"?t.isSubagent?U("eventSubagentWebFetchDone"):U("eventToolWebFetchDone"):r==="task"?U("eventToolTaskDone"):""}function vb(e,t,r={}){let n=uc(e),a=n.event_type||n.eventType||"";if(gb.has(a))return!1;let i=String(n.tool_name||n.toolName||"tool").toLowerCase();if(a==="Completed"&&!nh(i,r))return!1;let o=n.params&&typeof n.params=="object"?n.params:{},l=String(o.file_path||o.path||o.command||"").trim(),c=l?`${i}:${l}:${a}`:`${i}:${a}`;return t.has(c)?!1:(t.add(c),a==="Completed"||a==="Failed"||a==="Cancelled"||a==="ConfirmationNeeded")}function yb(){let e=0,t="";return{get lastProgressLogAt(){return e},touch(){e=Date.now()},note(r,n="",a="phase",i=0){let o=Date.now(),l=r===t;return i>0&&l&&o-e0&&(L.generation.draftedCount=r),Er(L),Rr(L)}function Pb(e,t,r){let n=bb(e),a=wb(n);Ft(n,"running",a),kb(e,n),t.touch()}function Fb(e,t={}){let r=uc(e.toolEvent||{}),n=r.event_type||r.eventType||"ToolEvent",a=String(r.tool_name||r.toolName||"").trim();if(n==="Completed"){let i=nh(a,t);return i?{title:i,detail:"",kind:"tool"}:null}return n==="Failed"||n==="Cancelled"?{title:U("eventToolFailedUser"),detail:"",kind:"error"}:n==="ConfirmationNeeded"?{title:U("processEventWaiting"),detail:"",kind:"tool"}:null}function uc(e){if(e.event_type||e.eventType||e.tool_name||e.toolName)return e;let r=["EarlyDetected","ParamsPartial","Queued","Waiting","Started","Progress","Streaming","StreamChunk","ConfirmationNeeded","Confirmed","Rejected","Completed","Failed","Cancelled"].find(a=>e&&Object.prototype.hasOwnProperty.call(e,a));return r?{...e[r]||{},event_type:r}:e||{}}function nn(e,t=180){let r=String(e||"").replace(/\s+/g," ").trim();return r?r.length>t?`${r.slice(0,t-1)}...`:r:""}function Tb(e,t){if(!e||!t)return;Dn.some(n=>n.sessionId===e&&n.turnId===t)||Dn.push({sessionId:e,turnId:t})}function Db(e,t){Dn=Dn.filter(r=>!(r.sessionId===e&&r.turnId===t))}function Tn(e){return e!==Ln}async function fc(){let e=[...Dn];Dn=[],!(!e.length||!it().backend?.cancel)&&await Promise.all(e.map(async t=>{try{await it().backend.cancel(t.sessionId,t.turnId)}catch(r){it().log?.warn?.("PPT Live backend cancel failed",{sessionId:t.sessionId,turnId:t.turnId,error:String(r)})}}))}async function Eb(e=!1,t={}){let r=Dn.length>0;Ln+=1,await fc(),L.generation.active=!1,L.generation.steps=L.generation.steps.map(n=>n.status==="running"?{...n,status:"error"}:n),!t.silent&&r&&(It(e?U("generationTimedOut"):U("generationStopped")),Rt(e?U("generationTimedOut"):U("generationStopped"))),En(!1),Er(L),Rr(L),t.silent||await Ct(!0)}async function Rb(e=!1){await Eb(e)}function Qo(e){if(Nb(e)){e.researchReport&&Rf(e.researchReport),e.design?.palette&&typeof e.design.palette=="object"&&(L.deckPalette=e.design.palette);return}let t=Ib(e);if(t.length)L.title=String(e.title||L.title||U("blankDeckTitle")),L.slides=t.map((r,n)=>jr(r,n,{...L,slides:t})),L.outline=L.slides.map(r=>r.title),L.activeSlideId=L.slides[0]?.id||"",L.selectedElementId="";else{if(!Array.isArray(e?.slides)||e.slides.length===0)throw new Error("PPT Live deck payload has no slides");L.title=String(e.title||L.title||U("blankDeckTitle")),L.slides=e.slides.map((r,n)=>jr({...r,html:r.html||r.sourceHtml||r.slideHtml||""},n,{...L,slides:e.slides})),L.outline=L.slides.map(r=>r.title),L.activeSlideId=L.slides[0]?.id||"",L.selectedElementId=L.slides[0]?.elements[0]?.id||""}Array.isArray(e.outline)&&e.outline.length&&(L.outline=e.outline.map(Zf).filter(Boolean)),e.researchReport&&Rf(e.researchReport),e.design?.palette&&typeof e.design.palette=="object"&&(L.deckPalette=e.design.palette)}function Rf(e){L.sources={...L.sources,facts:e.verifiedFacts||L.sources?.facts||[],warnings:e.warnings||L.sources?.warnings||[],summary:e.summary||L.sources?.summary||"",fetchedAt:Date.now()}}function Bb(e){return Array.isArray(e?.deckPatch?.changes)?e.deckPatch.changes:Array.isArray(e?.patch?.changes)?e.patch.changes:Array.isArray(e?.changes)?e.changes:Array.isArray(e?.patches)?e.patches:[]}function Bf(e,t,r=0){let n=String(e?.slideId||e?.id||e?.targetSlideId||e?.targetId||"").trim();if(n){let o=t.findIndex(l=>l.id===n);if(o>=0)return o}let a=Number(e?.slideNumber??e?.pageNumber);if(Number.isFinite(a)&&a>0)return mt(Math.round(a)-1,0,Math.max(0,t.length-1));let i=Number(e?.slideIndex??e?.index??e?.targetSlideIndex);return Number.isFinite(i)?i>=t.length&&i-1>=0&&i-1i.id===r);if(a>=0)return a+1}let n=String(e?.beforeSlideId||"").trim();if(n){let a=t.findIndex(i=>i.id===n);if(a>=0)return a}return e?.afterSlideNumber?mt(Number(e.afterSlideNumber),0,t.length):e?.beforeSlideNumber?mt(Number(e.beforeSlideNumber)-1,0,t.length):e?.slideNumber?mt(Number(e.slideNumber)-1,0,t.length):e?.slideIndex!==void 0?mt(Number(e.slideIndex),0,t.length):Math.min(t.length,$t(L)+1)}function Lf(e,t,r,n){let a=e?.slide||e?.replacement||e?.newSlide||e?.payload||e;if(!a||typeof a!="object")return null;let i={...t||{},...a,id:a.id||a.slideId||t?.id||gr("html-slide"),html:a.html||a.sourceHtml||a.slideHtml||t?.html||""};return jr(i,r,{...L,slides:n})}function Nb(e){let t=Bb(e);if(!t.length)return!1;let r=qr(L.slides||[]),n=[],a=0;if(t.forEach(o=>{let l=String(o?.op||o?.operation||o?.type||"replace_slide").toLowerCase();if(l==="delete_slide"||l==="delete"||l==="remove_slide"||l==="remove"){if(!r.length)return;let u=Bf(o,r,$t(L)),[h]=r.splice(u,1);h?.id&&n.push(h.id),a+=1;return}if(l==="insert_slide"||l==="insert"||l==="add_slide"||l==="add"){let u=Lb(o,r),h=Lf(o,null,u,r);if(!h)return;r.splice(u,0,h),n.push(h.id),a+=1;return}let c=Bf(o,r,$t(L)),s=r[c],d=Lf(o,s,c,r);d&&(r[c]=d,n.push(d.id),a+=1)}),!a)throw new Error("PPT Live deck patch had no applicable changes");L.title=String(e.deckPatch?.title||e.patch?.title||e.title||L.title||U("blankDeckTitle")),L.slides=r.map((o,l)=>jr(o,l,{...L,slides:r})),L.outline=Array.isArray(e.outline)&&e.outline.length?e.outline.map(String):L.slides.map(o=>o.title);let i=n.find(o=>L.slides.some(l=>l.id===o));return L.activeSlideId=i||L.slides[Math.min($t(L),L.slides.length-1)]?.id||L.slides[0]?.id||"",L.selectedElementId=_t(L)?.elements?.[0]?.id||"",!0}function Ib(e){let t=[];return Array.isArray(e?.htmlSlides)&&t.push(...e.htmlSlides),Array.isArray(e?.slides)&&t.push(...e.slides.filter(r=>r?.html||r?.sourceHtml||r?.slideHtml)),t.map((r,n)=>{let a=String(r?.html||r?.sourceHtml||r?.slideHtml||"").trim();return a?{id:r.id||r.slideId||gr("html-slide"),title:String(r.title||r.label||`${U("newSlideTitle")} ${n+1}`),subtitle:String(r.subtitle||""),kicker:String(r.kicker||""),claim:String(r.claim||r.title||""),proofObject:String(r.proofObject||""),supportNote:String(r.supportNote||""),sourceNote:String(r.sourceNote||""),notes:String(r.notes||""),layout:"html",theme:r.theme||{},html:a,elements:[]}:null}).filter(Boolean)}function Nf(...e){for(let t of e){let r=String(t||"").trim();if(r)try{return Ta(r),r}catch{}}return String(e.find(t=>String(t||"").trim())||"").trim()}async function Mb(e,t,r,n=null,a={}){let i=it(),o=a.expectJson!==!1;if(!t||!r||!i.backend?.turnText)return e;let l=!1,c=Promise.resolve(e).finally(()=>{l=!0}),s=new Promise((d,u)=>{let h=Date.now(),f=300*1e3,p=10*1e3,A=3600*1e3,y=()=>Number(n?.lastEventAt||h);(async()=>{for(;!l&&Date.now()-hf)break;if(bsetTimeout(x,Math.min(1e3,p-b)));continue}try{let x=await i.backend.turnText(t,r),C=String(x?.text||"").trim();if(C){if(!o){d({answer:C,thinking:""});return}try{Ta(C),d({answer:C,thinking:""});return}catch{}}}catch{}await new Promise(x=>setTimeout(x,2e3))}l||u(new Error("PPT Live backend did not publish a final deck JSON"))})()});return Promise.race([c,s])}async function zb(e,t,r,n=""){let a=Date.now(),i=25e3,o=String(r||"").trim(),l=String(n||"").trim(),s=Nf(o,l,`${o} -${l}`.trim());if(s)try{return Ta(s),s}catch{}let d=it();if(!e||!t||!d.backend?.turnText){if(!s)throw new Error("PPT Live backend produced no text");return s}let u=0;for(;Date.now()-a{setTimeout(()=>A(new Error("turnText timeout")),4e3)})]),f=String(h?.text||"").trim();if(s=Nf(f,s,l,o),s)return Ta(s),s}catch{}await new Promise(h=>setTimeout(h,500))}if(!s)throw new Error("PPT Live backend produced no text");return s}function Ta(e){let t=String(e||"").trim();if(!t)throw new Error("PPT Live backend produced no text");try{return JSON.parse(t)}catch{let r=t.match(/```(?:json)?\s*([\s\S]*?)```/i);if(r)return JSON.parse(r[1]);let n=t.indexOf("{"),a=t.lastIndexOf("}");if(n>=0&&a>n)return JSON.parse(t.slice(n,a+1));throw new Error("PPT Live backend returned invalid JSON")}}function Ob(e){let t=String(e?.message||e||"");return/ppt_live:\/\/round-budget-exhausted|exhausted its \d+-round tool budget|tool budget before producing deck JSON/i.test(t)}function Ub(e){return String(e||"").includes("timed out")}function is(e){let t=String(e||"");return t.includes("dialog-turn-cancelled")||t.includes("Generation stopped")}async function Wb(e,t={}){let r=Nn();t.readBrief!==!1&&an({includeTopic:!r});let n=[e,Xf()].filter(Boolean).join(": ");if(!n){It(U("promptRequired"));return}try{await lc("revise_slide",n,{includeTopic:!r})}catch(a){if(is(a))return;it().log?.warn?.("PPT Live backend slide revision failed",{action:e,error:String(a)}),rc(a),await Ct(!0)}}async function Gb(){if(an({includeTopic:!Nn()}),(L.slides||[]).some(e=>String(e?.html||"").trim())){let e=`Restyle the existing deck without changing its facts or narrative. Apply these exact settings to every slide HTML: ${JSON.stringify(on())}. Preserve each page's informationIntent and visualStrategy while making the deck visually coherent.`;try{await lc("revise_deck",e,{includeTopic:!1});return}catch(t){if(is(t))return;it().log?.warn?.("PPT Live Agent restyle failed",{error:String(t)}),rc(t),await Ct(!0);return}}L.slides=L.slides.map((e,t)=>jr({...e,theme:void 0},t,L)),It(U("deckRestyled")),jt(),await Ct(!0)}function Ql(){an({includeTopic:!Nn()});let e=new Map(L.slides.map(t=>[t.title,t]));L.slides=L.outline.map((t,r)=>{let n=e.get(t);return n?jr(n,r,L):gs(t,r,L.outline.length,L)}),L.activeSlideId=L.slides[0]?.id||"",L.selectedElementId=L.slides[0]?.elements[0]?.id||"",jt(),Ct(!0)}async function qb(){Ln+=1,await ns("before-new"),await fc(),L.generation.active=!1,En(!1),L=jb(),yi(),jt(),Na(L),It(U("blankDeckReady")),await Ct(!0)}function jb(){return cn(zn())}function Vb(e){if(!hs.includes(e))return;let t=_t(L);if(!t)return;let r=ms({...Ti(e),x:10+t.elements.length%5*4,y:14+t.elements.length%5*4});t.elements.push(r),L.selectedElementId=r.id,jt(),Ct(!0)}function Xb(){let e=_t(L);!e||!L.selectedElementId||(e.elements=e.elements.filter(t=>t.id!==L.selectedElementId),L.selectedElementId=e.elements[0]?.id||"",jt(),Ct(!0))}function If(e){let t=e.elements.find(r=>r.type==="text"&&r.text);t&&(e.title=t.text.slice(0,90),L.outline[$t(L)]=e.title,$t(L)===0&&(L.title=e.title))}function Mf(){L.presentIndex=$t(L),ah(),Se("previewDialog")?.showModal()}function ah(){let e=L.slides[L.presentIndex]||L.slides[0];Se("presentSlide")&&(Se("presentSlide").innerHTML=e?un(e):""),Se("presentCounter")&&(Se("presentCounter").textContent=`${Math.max(1,L.presentIndex+1)} / ${Math.max(1,L.slides.length)}`),Un()}function _o(e){L.presentIndex=mt(L.presentIndex+e,0,L.slides.length-1),ah()}function Hb(){if(!(L.slides||[]).length)return ka(U("exportDeckEmpty")),null;an({includeTopic:!Nn()});let e=jl(L);return ka(U("exportSavedTo",{path:e})),e}function _b(){return an({includeTopic:!Nn()}),(L.slides||[]).length?!0:(ka(U("exportDeckEmpty")),!1)}function ih(e){return{html:{working:U("exportHtmlWorking"),done:U("exportHtmlDone"),failed:U("exportHtmlFailed")},pptx:{working:U("exportPptxWorking"),done:U("exportPptxDone"),failed:U("exportPptxFailed")},pdf:{working:U("exportPdfWorking"),done:U("exportPdfDone"),failed:U("exportPdfFailed")},png:{working:U("exportPngWorking"),done:U("exportPngDone"),failed:U("exportPngFailed")}}[e]||null}function Zl(e,t,r){let n=ih(r==="pptx"?"pptx":r);if(!n||t<=0)return;let a=Math.min(t,Math.max(1,e+1));Yo("loading",`${n.working} (${a}/${t})`)}async function zf(e,t){let r=it();if(!r?.deck?.renderPage)throw new Error("Host WebView export is unavailable in this runtime.");let n=[],a=e.length;for(let[i,o]of e.entries()){Zl(i,a,t);let l=await r.deck.renderPage({html:Ds(o),format:t,width:or.width,height:or.height});if(!l)throw new Error(`Host WebView returned empty ${t} for slide ${i+1}`);n.push({index:i,base64:String(l).replace(/^data:.*;base64,/,"")})}return n}async function Kb(e){if(e==="html"){an({includeTopic:!Nn()});let o=jl(L);if(!o)throw new Error(U("exportDeckEmpty"));return{filename:o}}let t=L.slides||[];if(!t.length)throw new Error(U("exportDeckEmpty"));let r,n=qr(L);if(e==="pptx")if(t.some(o=>o?.html)){let o=it(),l=typeof o?.deck?.renderPage=="function"?async(s,d)=>{Zl(d,t.length,"pptx");let u=await o.deck.renderPage({html:s,format:"png",width:or.width,height:or.height});return String(u||"").replace(/^data:.*;base64,/,"")}:null,c=await nd(t,{renderRaster:l,onRasterProgress:s=>Zl(s,t.length,"pptx")});r=await Wl(n,c)}else r=await Ul(n);else if(e==="pdf"){let o=await zf(t,"pdf");r=await Gl(n,o.map(l=>l.base64))}else if(e==="png"){let o=await zf(t,"png");r=await ql(n,o)}else throw new Error(U("exportFormatUnavailable"));let a=typeof r?.base64=="string"?r.base64.replace(/^data:.*;base64,/,""):"";if(!a)throw new Error(`export${e} returned no data`);let i=r.filename||`${Vl(L.title||"ppt-live")}`;return gf(a,i,r.mimeType||"application/octet-stream"),{filename:i}}var Zo=!1,Jt={updateOutline(e,t){L.outline[e]=t,L.slides[e]&&(L.slides[e].title=t),jt(),Ct(!0)},moveOutline(e,t){let r=e+t;r<0||r>=L.outline.length||([L.outline[e],L.outline[r]]=[L.outline[r],L.outline[e]],Ql())},removeOutline(e){L.outline.length<=1||(L.outline.splice(e,1),Ql())},selectSlide(e){L.activeSlideId=e,L.selectedElementId=_t(L)?.elements[0]?.id||"",jt(),Ct(!0)},selectElement(e){L.selectedElementId=e,dn(L,Jt),Gi(L,Jt),Ct(!0)},updateElementTextDirect(e,t){let r=_t(L),n=r?.elements.find(a=>a.id===e);n&&(n.text=String(t||"").trim(),If(r),Wn(L,Jt),renderOutline(L,Jt),Ct(!1))},updateElementListItemDirect(e,t,r){let a=_t(L)?.elements.find(i=>i.id===e);!a||!Array.isArray(a.items)||(a.items[t]=String(r||"").trim(),a.items=a.items.filter(Boolean),dn(L,Jt),Wn(L,Jt),Ct(!1))},updateSlideHtmlDirect(e,t){let r=L.slides.find(a=>a.id===e);if(!r)return;let n=String(t||"");r.html!==n&&(r.html=n,Wn(L,Jt),Ct(!1))},updateSlideNotes(e){let t=_t(L);t&&(t.notes=e),Ct(!0)},updateSlideMethodology(){let e=_t(L);e&&(e.kicker=Se("slideKickerInput")?.value||e.kicker,e.claim=Se("slideClaimInput")?.value||e.claim,e.proofObject=Se("slideProofInput")?.value||e.proofObject,e.supportNote=Se("slideSupportInput")?.value||e.supportNote,e.sourceNote=Se("slideSourceInput")?.value||e.sourceNote,dn(L,Jt),Wn(L,Jt),Ct(!0))},updateElementFromInspector(){let e=_t(L),t=On(L);!e||!t||(t.text=Se("elementTextInput")?.value||"",t.items=(Se("elementItemsInput")?.value||"").split(` -`).map(r=>r.trim()).filter(Boolean),t.data=Zb(Se("elementDataInput")?.value||""),t.x=mt(Number(Se("elementXInput")?.value??t.x),0,100),t.y=mt(Number(Se("elementYInput")?.value??t.y),0,100),t.w=mt(Number(Se("elementWInput")?.value??t.w),3,100),t.h=mt(Number(Se("elementHInput")?.value??t.h),3,100),t.style.fontSize=mt(Number(Se("elementFontInput")?.value??t.style.fontSize),8,88),t.style.fontWeight=mt(Number(Se("elementWeightInput")?.value??t.style.fontWeight),100,900),t.style.color=Se("elementColorInput")?.value||t.style.color,t.style.background=Se("elementBgInput")?.value||t.style.background,Jt.updateSlideMethodology(),e.notes=Se("slideNotesInput")?.value||e.notes,If(e),dn(L,Jt),Ct(!0))},beginDrag(e,t){if(e.button!==0)return;let n=_t(L)?.elements.find(i=>i.id===t);if(!n)return;L.selectedElementId=n.id;let a=Se("slideCanvas").getBoundingClientRect();Tr={resizing:e.target.classList.contains("resize-handle"),startX:e.clientX,startY:e.clientY,rect:a,start:{x:n.x,y:n.y,w:n.w,h:n.h}},e.currentTarget.setPointerCapture?.(e.pointerId),window.addEventListener("pointermove",oh),window.addEventListener("pointerup",Qb,{once:!0})}};function oh(e){if(!Tr)return;let t=On(L);if(!t)return;let r=(e.clientX-Tr.startX)/Tr.rect.width*100,n=(e.clientY-Tr.startY)/Tr.rect.height*100;Tr.resizing?(t.w=mt(Tr.start.w+r,3,100-t.x),t.h=mt(Tr.start.h+n,3,100-t.y)):(t.x=mt(Tr.start.x+r,0,100-t.w),t.y=mt(Tr.start.y+n,0,100-t.h)),dn(L,Jt),Gi(L,Jt)}function Qb(){Tr=null,window.removeEventListener("pointermove",oh),Ct(!0)}function Zb(e){return e.split(` -`).map((t,r)=>{let[n,a]=t.split(":");return{label:(n||`Item ${r+1}`).trim(),value:Number(a||0)}}).filter(t=>t.label)}function Yb(){let e=document.querySelector(".studio-shell");if(!e)return;let t=document.documentElement,r=Number(_l("pptLiveFilmstripWidth")||0),n=Number(_l("pptLiveAgentWidth")||0);r>=128&&r<=360&&t.style.setProperty("--filmstrip-width",`${r}px`),n>=240&&n<=460&&t.style.setProperty("--agent-width",`${n}px`);let a=(i,o)=>{let l=e.getBoundingClientRect(),c=128,s=Math.min(360,l.width*.34),d=240,u=Math.min(460,l.width*.42),h=360,f=A=>{if(i==="filmstrip"){let y=Math.max(c,Math.min(s,A.clientX-l.left));if(l.width-y-parseFloat(getComputedStyle(t).getPropertyValue("--agent-width"))-12{e.classList.remove("is-resizing"),document.querySelectorAll(".panel-resizer.is-dragging").forEach(A=>A.classList.remove("is-dragging")),window.removeEventListener("pointermove",f),window.removeEventListener("pointerup",p),window.removeEventListener("pointercancel",p),Kl("pptLiveFilmstripWidth",String(parseFloat(getComputedStyle(t).getPropertyValue("--filmstrip-width"))||"")),Kl("pptLiveAgentWidth",String(parseFloat(getComputedStyle(t).getPropertyValue("--agent-width"))||"")),Un()};e.classList.add("is-resizing"),window.addEventListener("pointermove",f),window.addEventListener("pointerup",p,{once:!0}),window.addEventListener("pointercancel",p,{once:!0}),f({clientX:o})};Se("filmstripResizer")?.addEventListener("pointerdown",i=>{i.button===0&&(i.preventDefault(),i.currentTarget.classList.add("is-dragging"),a("filmstrip",i.clientX))}),Se("agentResizer")?.addEventListener("pointerdown",i=>{i.button===0&&(i.preventDefault(),i.currentTarget.classList.add("is-dragging"),a("agent",i.clientX))})}function Jb(){let e=null,t=()=>{e&&clearTimeout(e),e=setTimeout(()=>{Un()},60)};window.addEventListener("resize",t),Se("toggleHistory")?.addEventListener("click",()=>{let r=Se("historyDrawer");r&&(r.hidden=!r.hidden)}),Se("closeHistory")?.addEventListener("click",()=>{let r=Se("historyDrawer");r&&(r.hidden=!0)}),document.querySelectorAll("[data-sidebar-tab]").forEach(r=>{r.addEventListener("click",()=>{let n=r.dataset.sidebarTab;document.querySelectorAll("[data-sidebar-tab]").forEach(a=>{a.classList.toggle("is-active",a.dataset.sidebarTab===n)}),document.querySelectorAll("[data-sidebar-panel]").forEach(a=>{a.classList.toggle("is-active",a.dataset.sidebarPanel===n)})})}),Se("topicInput")?.addEventListener("input",()=>{if(Nn()){L.promptDraft=Se("topicInput")?.value||"",Ct(!0);return}an({includeTopic:!0}),Ct(!0)}),Se("newDeck")?.addEventListener("click",()=>{qb()}),Se("cancelGeneration")?.addEventListener("click",()=>{Rb(!1)}),Se("sendPrompt")?.addEventListener("click",()=>{ec()}),Se("generateOutline")?.addEventListener("click",()=>{Xy()}),Se("generateDeck")?.addEventListener("click",()=>{Hy()}),Se("addOutlineItem")?.addEventListener("click",()=>{L.outline.push(U("newSlideTitle")),jt(),Ct(!0)}),Se("syncSlidesFromOutline")?.addEventListener("click",Ql),Se("deleteElement")?.addEventListener("click",Xb),Se("previewDeck")?.addEventListener("click",Mf),Se("closePreview")?.addEventListener("click",()=>Se("previewDialog")?.close()),Se("prevPresent")?.addEventListener("click",()=>_o(-1)),Se("nextPresent")?.addEventListener("click",()=>_o(1)),Se("exportHtml")?.addEventListener("click",Hb),Se("restyleDeck")?.addEventListener("click",Gb),document.querySelectorAll("[data-add-element]").forEach(r=>{r.addEventListener("click",()=>Vb(r.dataset.addElement))}),document.querySelectorAll(".ai-action").forEach(r=>{r.addEventListener("click",()=>{Wb(r.dataset.action)})}),document.querySelectorAll(".segment").forEach(r=>{r.addEventListener("click",()=>{L.mode=r.dataset.mode,L.mode==="present"&&Mf(),jt(),Ct(!0)})}),document.addEventListener("keydown",r=>{Se("previewDialog")?.open&&((r.key==="ArrowRight"||r.key==="PageDown")&&_o(1),(r.key==="ArrowLeft"||r.key==="PageUp")&&_o(-1),r.key==="Escape"&&Se("previewDialog")?.close())});try{Yb()}catch(r){it().log?.warn?.("Failed to bind PPT Live panel resizers",{error:String(r)})}if(typeof ResizeObserver<"u"){let r=[document.querySelector(".ppt-live"),document.querySelector(".studio-shell"),document.querySelector(".stage-shell"),document.querySelector(".canvas-area")].filter(Boolean),n=new ResizeObserver(t);r.forEach(a=>n.observe(a))}t1(),r1(),n1(),l1(),d1()}var tn=1,Ca=.25,$b=.25,e1=2;function gi(e){tn=mt(e,$b,e1);let t=document.querySelector(".canvas-stage");t&&(t.style.transform=tn===1?"":`scale(${tn})`);let r=Se("zoomValue"),n=Se("statusZoomValue"),a=Math.round(tn*100)+"%";r&&(r.textContent=a),n&&(n.textContent=a)}function t1(){Se("zoomIn")?.addEventListener("click",()=>gi(tn+Ca)),Se("zoomOut")?.addEventListener("click",()=>gi(tn-Ca)),Se("statusZoomIn")?.addEventListener("click",()=>gi(tn+Ca)),Se("statusZoomOut")?.addEventListener("click",()=>gi(tn-Ca)),document.querySelector(".canvas-area")?.addEventListener("wheel",e=>{if(e.ctrlKey||e.metaKey){e.preventDefault();let t=e.deltaY>0?-Ca:Ca;gi(tn+t)}},{passive:!1})}function r1(){Se("floatingToolbar")&&document.querySelectorAll(".floating-toolbar-btn").forEach(t=>{t.addEventListener("click",()=>{let r=t.dataset.tool;if(!r)return;let n=_t(L),a=On(L);if(!(!n||!a)){switch(r){case"bold":a.fontWeight=a.fontWeight==="700"?"400":"700";break;case"italic":a.fontStyle=a.fontStyle==="italic"?"normal":"italic";break;case"underline":a.textDecoration=a.textDecoration==="underline"?"none":"underline";break;case"align-left":a.align="left";break;case"align-center":a.align="center";break;case"align-right":a.align="right";break;case"duplicate":n.elements.push({...qr(a),id:gr("el"),x:a.x+5,y:a.y+5});break;case"delete":n.elements=n.elements.filter(i=>i.id!==a.id),L.selectedElementId=null;break}dn(L,Jt),Wn(L,Jt),Ct(!0)}})})}function n1(){document.querySelectorAll(".property-section__header").forEach(n=>{let a=n.closest(".property-section");if(!a)return;let i=()=>{a.classList.toggle("is-collapsed");let o=!a.classList.contains("is-collapsed");n.setAttribute("aria-expanded",String(o))};n.addEventListener("click",i),n.addEventListener("keydown",o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),i())})});let e=Se("densitySlider"),t=e?.querySelector(".density-slider__track");e&&t&&(t.addEventListener("pointerdown",n=>{n.preventDefault(),Pn(Pf(n.clientX,t)),t.setPointerCapture(n.pointerId)}),t.addEventListener("pointermove",n=>{t.hasPointerCapture(n.pointerId)&&Pn(Pf(n.clientX,t))}),t.addEventListener("pointerup",n=>{t.hasPointerCapture(n.pointerId)&&t.releasePointerCapture(n.pointerId)}),t.addEventListener("pointercancel",n=>{t.hasPointerCapture(n.pointerId)&&(t.releasePointerCapture(n.pointerId),Ui(L.style?.density))}),e.querySelectorAll("[data-density-index]").forEach(n=>{n.addEventListener("click",a=>{a.stopPropagation(),Pn(n.dataset.densityIndex)})}),e.addEventListener("keydown",n=>{let a=Se("densitySlider"),i=Number(a?.dataset.index??1);n.key==="ArrowLeft"||n.key==="ArrowDown"?(n.preventDefault(),Pn(i-1)):n.key==="ArrowRight"||n.key==="ArrowUp"?(n.preventDefault(),Pn(i+1)):n.key==="Home"?(n.preventDefault(),Pn(0)):n.key==="End"&&(n.preventDefault(),Pn(2))})),document.querySelectorAll("[data-font-family]").forEach(n=>{n.addEventListener("click",()=>{zi(n.dataset.fontFamily==="serif"?"serif":"sans")})}),document.querySelectorAll("[data-color-mode]").forEach(n=>{n.addEventListener("click",()=>{Oi(n.dataset.colorMode==="dark"?"dark":"light")})});let r=Se("stylePresetSelect");r&&(dh(),Rc(r),Na(L),r.addEventListener("change",()=>{let n=r.value;if(!n)return;let a=ls(n);a&&(Oi(a.colorMode||"light"),zi(a.fontFamily||"sans"),Pn(ki(a.density||"standard"))),Ea(r)}))}var rn=0;function sh(){return Se("formatGrid")?.querySelector(".format-card.is-selected")?.dataset.format||"pptx"}function a1(){let e=Se("exportOverlay");e&&(lh(),rn=Math.max(0,$t(L)),e.classList.add("is-visible"),e.setAttribute("aria-hidden","false"),i1(),ts(),requestAnimationFrame(()=>Fa()))}function Fa(){Hc(Se("exportPreviewFrame"))}function lh(){let e=Se("exportModalFeedback"),t=Se("exportModalFeedbackText"),r=Se("exportModalSpinner");Se("exportOverlay")?.classList.remove("is-exporting"),e&&(e.hidden=!0,e.classList.remove("is-success","is-error")),t&&(t.textContent=""),r&&(r.hidden=!1),Yl(!1)}function Yl(e){["exportCancel","exportConfirm","closeExport"].forEach(t=>{let r=Se(t);r&&(r.disabled=e)}),Se("formatGrid")?.querySelectorAll(".format-card").forEach(t=>{t.tabIndex=e?-1:0,t.style.pointerEvents=e?"none":""}),["exportPreviewPrev","exportPreviewNext"].forEach(t=>{let r=Se(t);r&&(r.disabled=e)})}function Yo(e,t){let r=Se("exportModalFeedback"),n=Se("exportModalFeedbackText"),a=Se("exportModalSpinner");!r||!n||(r.hidden=!1,r.classList.toggle("is-success",e==="success"),r.classList.toggle("is-error",e==="error"),a&&(a.hidden=e!=="loading"),n.textContent=t)}function Jo(){let e=Se("exportOverlay");e&&(e.classList.remove("is-visible"),e.setAttribute("aria-hidden","true"),lh())}function i1(){let e=Se("formatGrid");if(!e)return;let t=[{id:"pptx",name:"PPTX",desc:"Editable PowerPoint"},{id:"pdf",name:"PDF",desc:"Universal format"},{id:"html",name:"HTML",desc:"Interactive web deck"},{id:"png",name:"PNG",desc:"Image sequence"}];e.innerHTML=t.map((r,n)=>` +${uy(e)} +\`\``,e?.continueAfterInterruption&&(a=`\u4E0A\u4E00\u6B21\u751F\u6210\u88AB\u4E2D\u65AD\u4E86\u3002\u8BF7\u7EE7\u7EED\u5B8C\u6210\u4EFB\u52A1\uFF1A\u68C0\u67E5 project.json \u548C\u5DF2\u5199\u7684 slides/ \u6587\u4EF6\uFF0C\u53EA\u8865\u5199\u8FD8\u6CA1\u5B8C\u6210\u7684\u9875\u9762\uFF0C\u4E0D\u8981\u91CD\u5199\u5DF2\u6709\u7684\u9875\u9762\u3002 + +${a}`),a}function Ay(e){let t=!1,r=()=>{t||(t=!0,e.agent.onEvent(n=>{!n||typeof n!="object"||dy(n)}))};e.backend={protocol:"files",async call(n,a,i={}){if(n!=="ppt.generate")throw new Error(`Unsupported PPT Live action: ${n}`);r();let o=py(a),l=await e.agent.run(o,{runId:i.idempotencyKey,sessionName:"PPT Live",sessionId:i.sessionId,appDataWorkspace:i.appDataWorkspace});if(!l?.sessionId||!l?.turnId)throw new Error("PPT Live agent backend did not return sessionId/turnId");return{sessionId:l.sessionId,turnId:l.turnId,actionRunId:l.actionRunId||l.turnId}},onEvent(n){Ml.add(n)},offEvent(n){Ml.delete(n)},async cancel(n,a){await e.agent.cancel(n,a)},async turnText(n,a){return{text:(await e.agent.turnText(n,a))?.text||""}},async cancelStaleRuns(){await e.agent.cancelStaleRuns()}}}function cf(e=window.app){!e||e.backend?.call||e.agent?.run&&Ay(e)}var N=Bn(),Pn=!1,Fr=null,Fn=[],Xn=0,Wo=!1,hi=!1,Vn=[],zl=0,Se=e=>document.getElementById(e),ot=()=>window.app||{};cf(ot());var Sf=2500,Xo=new Map;function Ol(e){try{return localStorage.getItem(e)}catch{return Xo.has(e)?Xo.get(e):null}}function Ul(e,t){try{localStorage.setItem(e,t)}catch{Xo.set(e,t)}}var pi={get:async e=>JSON.parse(Ol(e)||"null"),set:async(e,t)=>Ul(e,JSON.stringify(t))};function Cf(){let e=ot();return e.storage?e.storage:pi}async function kf(e){let t=Cf();if(t===pi||!ot().storage)return t.get(e);try{return await Promise.race([t.get(e),new Promise((r,n)=>setTimeout(()=>n(new Error("storage-timeout")),Sf))])}catch(r){return ot().log?.warn?.("Host storage read timed out, using local fallback",{key:e,error:String(r)}),pi.get(e)}}async function _o(e,t){let r=Cf();if(r===pi||!ot().storage){await r.set(e,t);return}try{await Promise.race([r.set(e,t),new Promise((n,a)=>setTimeout(()=>a(new Error("storage-timeout")),Sf))])}catch(n){ot().log?.warn?.("Host storage write timed out, using local fallback",{key:e,error:String(n)}),await pi.set(e,t)}}async function gy(){try{Vn=await vy();let e=await kf(xa);if(e){N=ln(e),Vl(N)&&(N=Bn(),await _o(xa,{...N,updatedAt:Date.now()}));return}N=Bn(),await Pt(!0)}catch(e){ot().log?.warn?.("Failed to load PPT Live state",{error:String(e)}),N=Bn()}}async function Pt(e=!1){N=ln(N),await _o(xa,{...N,updatedAt:Date.now()}),await jl(e?"autosave":"manual"),e||Zt(V("saved"))}async function vy(){try{let e=await kf(rs);return Array.isArray(e)?e.map(Pf).filter(Boolean).slice(0,40):[]}catch(e){return ot().log?.warn?.("Failed to load PPT Live history",{error:String(e)}),[]}}async function jl(e="autosave"){if(!N?.slides?.length||Vl(N))return;let t=Date.now();if(e==="autosave"&&zl&&t-zl<15e3)return;zl=t;let r=Pf({id:N.sessionId||ar("deck"),title:N.title||V("blankDeckTitle"),updatedAt:t,slideCount:N.slides.length,reason:e,prompt:N.promptDraft||N.brief?.topic||"",state:sn({...N,generation:{...N.generation,active:!1}})});r&&(Vn=[r,...Vn.filter(n=>n.id!==r.id)].slice(0,40),await _o(rs,Vn),Ff())}function Vl(e){let t=Array.isArray(e?.slides)?e.slides:[];return t.length===1&&!t[0]?.html&&String(t[0]?.id||"").startsWith("agent-working-slide")&&String(e?.title||"")===V("agentWorkingTitle")&&!e?.generation?.active}function Pf(e){return!e?.id||!e?.state?null:{id:String(e.id),title:String(e.title||e.state?.title||V("blankDeckTitle")),updatedAt:Number(e.updatedAt||Date.now()),slideCount:Number(e.slideCount||e.state?.slides?.length||0),reason:String(e.reason||"autosave"),prompt:String(e.prompt||e.state?.brief?.topic||""),state:e.state}}function Ff(){let e=Se("historyList");if(e){if(e.innerHTML="",!Vn.length){let t=document.createElement("div");t.className="history-empty",t.textContent=V("historyEmpty"),e.append(t);return}Vn.slice(0,12).forEach(t=>{let r=document.createElement("button");r.type="button",r.className=`history-card${t.id===N.sessionId?" is-active":""}`,r.innerHTML=` + ${df(t.title)} + ${V("historyMeta",{count:t.slideCount,time:yy(t.updatedAt)})} + ${t.prompt?`${df(t.prompt)}`:""} + `,r.addEventListener("click",()=>{my(t.id)}),e.append(r)})}}async function my(e){let t=Vn.find(r=>r.id===e);t&&(Xn+=1,await $l(),N=ln(sn(t.state)),N.generation.active=!1,Ko(),Yt(),Ta(N),Zt(V("historyRestored")),await _o(xa,{...N,updatedAt:Date.now()}))}function yy(e){let t=new Date(e);if(Number.isNaN(t.getTime()))return"";let r=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),a=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${r}/${n} ${a}:${i}`}function df(e){return String(e??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function Zt(e){let t=Se("statusLine");t&&(t.textContent=e)}function ma(e){let t=Se("exportStatus");t&&(t.textContent=e)}function ba(e,t){Pn=e,document.querySelector(".ppt-live")?.classList.toggle("is-busy",Pn),document.querySelectorAll("button, input, select, textarea").forEach(n=>{if(!["closePreview","prevPresent","nextPresent"].includes(n.id)){if(n.id==="cancelGeneration"){n.disabled=!Pn,n.hidden=!Pn;return}n.id!=="newDeck"&&(n.disabled=Pn)}});let r=Se("aiStatusPill");r&&(r.textContent=Pn?V("statusPillBusy"):V("statusPillReady"),r.classList.toggle("is-busy",Pn)),t&&Zt(t)}function hr(e,t,r){N.generation.current=e,N.generation.steps=N.generation.steps.map(n=>({...n,status:n.id===e?t:n.status})),N.generation.active=t==="running"||N.generation.steps.some(n=>n.status==="running"),vr(N),Ir(N),r&&Zt(r)}function Ko(){N.generation.active=!1,N.generation.current="idle",N.generation.draftedCount=0,N.generation.slideTarget=0,N.generation.eventSeq=0,N.generation.steps=N.generation.steps.map(e=>({...e,status:"pending"})),N.generation.events=[],N.generation.agentStream=[],vr(N),Ir(N)}function Tr(e,t="",r="info"){N.generation=Sa(N.generation||{});let n=typeof e=="string"?{title:e,detail:t,kind:r}:{...e||{}},a=Bt(n.title||n.label||n.message||V("processEventUnknown"),160),i=Bt(n.detail??t??"",260),o=String(n.kind||r||"info").toLowerCase().replace(/[^a-z0-9-]/g,"")||"info";if(!a&&!i)return;let l=Array.isArray(N.generation.events)?N.generation.events:[],c=l[l.length-1];if(c&&c.title===a&&c.detail===i&&c.kind===o)c.timestamp=Date.now(),N.generation.events=l;else{let s=l.reduce((u,f)=>Math.max(u,Number(f.seq)||0),0),d=Math.max(Number(N.generation.eventSeq)||0,s)+1;N.generation.eventSeq=d,N.generation.events=[...l,{id:ar("generation-event"),seq:d,title:a||V("processEventUnknown"),detail:i,kind:o,timestamp:Date.now()}].slice(-80)}vr(N),Ir(N)}function $r(e){N.generation=Sa(N.generation||{});let t=Array.isArray(N.generation.agentStream)?N.generation.agentStream:[];if(e.kind==="text"){let r=t[t.length-1];if(r&&r.kind==="text"){r.text=String(r.text||"")+String(e.text||""),r.timestamp=Date.now(),N.generation.agentStream=t,vr(N);return}}t.push({id:ar("agent-stream"),timestamp:Date.now(),...e}),N.generation.agentStream=t,vr(N)}function Yt(){N=ln(N),Fc(N,Qt),Ff()}function rn(e={}){Mc(N,e),N=ln(N)}function Tf(){return Se("topicInput")?.value.trim()||""}function Df(){let e=cc().join(` +`);return!N.outline.length||N.outline.join(` +`)===e||N.title===V("defaultDeckTitle")||Xl()}function Xl(){let e=String(N.title||"").trim();return N.slides.length===1&&N.outline.length===1&&N.outline[0]===V("newSlideTitle")&&(e===V("blankDeckTitle")||e===V("newSlideTitle"))}function nn(){return Array.isArray(N.slides)&&N.slides.length>0&&!Df()&&!Xl()&&!Vl(N)}async function by(){await Hl()}async function wy(){await Hl()}async function Hl(){if(Wo||hi)return;let e=Tf();if(!e){Zt(V("promptRequired"));return}Wo=!0;let t=nn();N.promptDraft=e,N.lastSubmittedPrompt=e,rn({includeTopic:!t}),t||(N.brief.topic=e);try{await Yl("auto",e,{includeTopic:!t,persistBeforeRun:!0});return}catch(r){if(Qo(r))return;ot().log?.warn?.("PPT Live backend generation failed",{error:String(r)}),_l(r),Yt(),await Pt(!0)}finally{Wo=!1}}function Bf(e=V("deckReady")){N.generation.active=!1,N.generation.draftedCount=N.slides.length,N.generation.slideTarget=0,N.generation.steps=(N.generation.steps||[]).map(t=>({...t,status:t.status==="error"?"error":"done"})),Zt(e),vr(N),Ir(N)}function xy(e=V("backendGenerationFailed"),t=""){N.generation.active=!1,N.generation.steps=(N.generation.steps||[]).map(r=>({...r,status:r.status==="done"?"done":"error"})),Zt(e),Tr({title:e,detail:t||V("agentOnlyRetryHint"),kind:"error"}),ba(!1),vr(N),Ir(N)}function Sy(e,t=5){let r=[],n=new Set,a=e;for(let i=0;a&&i({kind:n.kind,title:n.title,url:n.url,text:String(n.text||"").slice(0,6e3)}))}:null);let r=Number(N.brief?.slideTarget)||0;return r>0&&(t.slideTarget=r),t}function Kl({includePreset:e=!0}={}){let t=Jo(N.style?.stylePreset),r=N.style?.colorMode==="dark"?"dark":"light",n={fontFamily:N.style?.fontFamily==="serif"?"serif":"sans",density:Dn(N.style?.density),colorMode:r,theme:r,palette:tc(t,r)};return e&&(n.stylePreset=N.style?.stylePreset||an),n}function ky(e){let t=String(e||"").trim();if(!t)return"";try{let r=new DOMParser().parseFromString(t,"text/html");return r.querySelectorAll("style,script,svg").forEach(n=>n.remove()),Bt(r.body?.textContent||r.documentElement?.textContent||"",1800)}catch{return Bt(t.replace(/<[^>]+>/g," "),1800)}}function Py(e){let t=new Set,r=String(e||""),n=Jt(N);return/(当前|本页|这一页|此页|current\s+(slide|page)|this\s+(slide|page))/i.test(r)&&t.add(n),[/第\s*(\d{1,2})\s*(页|頁|张|張)/gi,/\b(?:slide|page)\s*(\d{1,2})\b/gi,/\b(\d{1,2})\s*(?:slide|slides|page|pages)\b/gi].forEach(i=>{let o=i.exec(r);for(;o;){let l=Number(o[1])-1;l>=0&&li-o)}function Fy(e){let t=Py(e),r=Jt(N),n=new Set(t.length?t:[r]);return{title:N.title,outline:sn(N.outline||[]),slideCount:N.slides.length,activeSlideIndex:r,activeSlideId:N.slides[r]?.id||"",targetHints:t.map(a=>({slideIndex:a,slideNumber:a+1,slideId:N.slides[a]?.id||"",title:N.slides[a]?.title||""})),slides:N.slides.map((a,i)=>{let o=a.html?ky(a.html):Bt((a.elements||[]).flatMap(c=>[c.text,c.label,...Array.isArray(c.items)?c.items:[]]).filter(Boolean).join(` +`),1800),l={slideIndex:i,slideNumber:i+1,id:a.id,title:a.title,kicker:a.kicker,claim:a.claim,proofObject:a.proofObject,supportNote:a.supportNote,sourceNote:a.sourceNote,notes:a.notes,layout:a.layout,visibleText:o,hasHtml:!!a.html};return n.has(i)&&a.html&&(l.html=String(a.html).slice(0,12e3)),l})}}function uf(e,t){let r=t.getBoundingClientRect(),n=vt((e-r.left)/r.width,0,1);return Math.round(n*2)}function kn(e){Ni(xi(vt(Math.round(Number(e)),0,2)))}function Ty(){Ic(N),N=ln(N)}var di=2,Dy=750;function By(e){let t=String(e?.message||e||"");return!(Qo(e)||/Generation stopped/i.test(t)||/backend is unavailable|did not return sessionId/i.test(t)||/permission|workspacePath is required|unsupported PPT Live action/i.test(t))}function Ey(e,t){let r=String(e?.message||e||"");return/rate limit|network|timed? out|connection|temporar|overload|service unavailable|502|503|504/i.test(r)?Math.min(15e3,1e3*2**Math.min(Math.max(0,t-1),4)):Dy}function Ry(e){return/Unknown MiniApp agent session|session workspace does not match/i.test(String(e?.message||e||""))}function Ly(){let e=ot();return e.backend?.protocol==="files"&&!!e.appDataDir&&!!e.fs?.readFile}function Ny(){let e=`deck-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;return{runId:e,workspaceSubdir:`decks/${e}`,dir:`${ot().appDataDir}/decks/${e}`}}function Iy(){let e=String(N.agentSession?.workspaceSubdir||"");return!e||!ot().appDataDir?null:{runId:String(N.agentSession?.runId||e.split("/").pop()||""),workspaceSubdir:e,dir:`${ot().appDataDir}/${e}`}}function Rf(e){return`slides/slide-${String(e).padStart(2,"0")}.html`}function Ql(e){return typeof e=="string"?e:String(e?.title||"")}function Lf(e){return Array.isArray(e?.outline)?e.outline.map(Ql).filter(Boolean):[]}function Nf(e){let t=String(e||"").trim();return t?[V("agentWorkingTitle"),V("generationAgentWorking"),V("blankDeckTitle"),V("defaultDeckTitle"),V("newSlideTitle")].includes(t):!0}function Ai({plan:e=null,payload:t=null,state:r=null,instruction:n="",slides:a=[]}={}){let i=Array.isArray(e?.outline)?e.outline:Array.isArray(t?.outline)?t.outline:[],o=i.length?Ql(i[0]):"",l=Array.isArray(a)&&a.length?String(a[0]?.title||"").trim():"",c=[t?.deckPatch?.title,t?.patch?.title,t?.title,e?.title,o,r?.brief?.topic,n,r?.promptDraft,l];for(let s of c){let d=String(s||"").trim();if(d&&!Nf(d))return d}return V("blankDeckTitle")}async function If(e,t){let r=ot().fs;if(!r?.readFile)throw new Error("PPT Live fs API is unavailable");return await r.readFile(`${e.dir}/${t}`)}async function My(e,t){try{let r=String(await If(e,t)||"");return r.trim()?wa(r):null}catch{return null}}async function Zl(e){return await My(e,"project.json")}async function Mf(e,t){try{let r=String(await If(e,Rf(t))||"").trim();return!r||!/<\/html>\s*$/i.test(r)?null:r}catch{return null}}async function zy(e,t,r=6,n=120){for(let a=1;a<=r;a+=1){let i=await Mf(e,t);if(i)return i;asetTimeout(o,n))}return null}async function Oy(e){let t=await Zl(e);if(!t)throw new Error("PPT Live agent finished without a valid project.json");let r=Array.isArray(t.slide_order)&&t.slide_order.length?t.slide_order:Array.isArray(t.outline)?t.outline.map((a,i)=>`slide-${String(i+1).padStart(2,"0")}`):[],n=[];for(let a=0;a({id:`slide-${String(o+1).padStart(2,"0")}`,title:String(i.title||""),bullets:[],slide_id:`slide-${String(o+1).padStart(2,"0")}`})),a={title:N.title||"",language:Nr(),outline:n,slide_order:n.map(i=>i.slide_id),style:Kl()};await t.writeFile(`${e.dir}/project.json`,`${JSON.stringify(a,null,2)} +`);for(let i=0;itypeof i=="string"?i:i?.name).filter(i=>typeof i=="string"&&i.startsWith("deck-")&&i!==e);for(let i of a)await t.rm(`${r}/${i}`,{recursive:!0})}catch{}}async function Yl(e,t,r={}){if(!ot().backend?.call)throw new Error("PPT Live backend is unavailable");if(!hi){hi=!0;try{rn({includeTopic:r.includeTopic!==!1}),Ty(),r.persistBeforeRun&&await Pt(!0),await jy(e,t)}finally{hi=!1}}}async function Gy(e,t={},r={}){let n=ot(),a=Xn,i=null,o=null,l="",c="",s=!1,d=0,u=null,f=[],h=new Set,p=[],A=new Set,y=Ky(),m={lastEventAt:Date.now()};try{let b=await n.backend.call("ppt.generate",e,{entityId:"deck",idempotencyKey:`ppt-live-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,sessionId:r.sessionId||void 0,appDataWorkspace:r.appDataWorkspace||void 0});if(i=b?.sessionId||null,o=b?.turnId||b?.actionRunId||null,!i||!o)throw new Error("PPT Live backend did not return sessionId/turnId");if(ib(i,o),fi(a))throw new Error("Generation stopped");let x=new Promise((W,q)=>{let C=v=>{let z=v.sessionId,ne=z===i,_=String(v.sourceEvent||"");if(_.endsWith("subagent-session-linked")){v.parentSessionId===i&&z&&(A.add(z),Tr({title:V("eventSubagentStarted"),detail:"",kind:"tool"}),$r({kind:"system",text:`[subagent started] ${String(v.subagentName||v.sessionName||z).slice(0,120)}`}));return}let ce=A.has(z);if(!(!ne&&!ce)&&!(ne&&v.turnId&&v.turnId!==o)){if(m.lastEventAt=Date.now(),!ne&&z&&A.add(z),_.endsWith("dialog-turn-started"))y.note(V("eventTurnStarted"),"","turn");else if(_.endsWith("model-round-started"))ne?t.onToolPhase?.("round"):y.note(V("eventSubagentWorking"),"","pulse",8e3),y.touch();else if(_.endsWith("model-round-completed"))y.touch();else if(_.endsWith("tool-event")){let J=Jl(v.toolEvent||{}),ge=J.event_type||J.eventType||"",K=String(J.tool_name||J.toolName||"").trim().toLowerCase();if(ne&&(ge==="Started"?p.push({eventType:ge,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",params:J.params||{}}):ge==="Completed"?p.push({eventType:ge,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",result:J.result||{}}):(ge==="Failed"||ge==="Cancelled")&&p.push({eventType:ge,toolId:J.tool_id||J.toolId||"",toolName:J.tool_name||J.toolName||"",error:J.error||J.message||ge})),ge==="Started"&&K==="task"&&(y.note(V("eventToolTaskStarted"),"","tool"),y.touch()),ge==="Started"&&K){let Q=J.params||J.result||{},G=Qy(K,Q);$r({kind:"tool-start",toolName:K,isSubagent:!ne,text:G})}else if(ge==="Completed"&&K){let Q=Zy(K,J.result||{});Q&&$r({kind:"tool-done",toolName:K,isSubagent:!ne,text:Q})}else(ge==="Failed"||ge==="Cancelled")&&$r({kind:"tool-error",toolName:K,isSubagent:!ne,text:Bt(String(J.error||J.message||ge),200)});if(_y(J,h,{isSubagent:!ne})){let Q=ab(v,{isSubagent:!ne});Q&&Tr(Q),y.touch()}if(ne&&(ge==="EarlyDetected"||ge==="Started"))t.onToolPhase?.("detected");else if(ne&&ge==="Completed"){let Q=K;if(t.onToolPhase?.("completed"),Q==="skill"?y.note(V("eventToolSkillReady"),"","phase"):(Q==="websearch"||Q==="webfetch")&&t.onToolPhase?.("research"),(Q==="write"||Q==="edit")&&typeof t.onSlideFileWritten=="function"){let ee=Of(J,p).match(/slides\/slide-(\d{2})\.html/i);if(ee){let Y=parseInt(ee[1],10);Number.isFinite(Y)&&Y>0&&Promise.resolve(t.onSlideFileWritten(Y)).catch(()=>{})}}}}else if(_.endsWith("text-chunk")){let J=String(v.text||"");if(v.contentType==="thinking")c+=J;else{l+=J,y.touch(),$r({kind:"text",text:J});let K=Date.now();K-d>=500&&(d=K,t.onTextProgress?.(l))}}else if(!_.endsWith("token-usage-updated")){if(_.endsWith("dialog-turn-completed")){if(!ne){y.note(V("eventSubagentDone"),"","tool"),$r({kind:"system",isSubagent:!0,text:"[subagent done]"});return}$r({kind:"system",text:"[turn completed]"}),s=!0,u={success:v.success,finishReason:v.finishReason||v.finish_reason||"",partialRecoveryReason:v.partialRecoveryReason||v.partial_recovery_reason||""},W({answer:l,thinking:c})}else if(_.endsWith("dialog-turn-failed")||_.endsWith("dialog-turn-cancelled")){if(!ne){y.note(V("eventSubagentFailed"),"","error"),$r({kind:"system",isSubagent:!0,text:"[subagent failed]"});return}s=!0,l&&t.onTextProgress?.(l);let J=Bt(v.error||v.message||"");$r({kind:"system",text:_.endsWith("dialog-turn-cancelled")?"[turn cancelled]":"[turn failed]"}),Tr({title:_.endsWith("dialog-turn-cancelled")?V("eventTurnCancelled"):V("eventTurnFailed"),detail:J,kind:"error"}),q(new Error(J||_))}}}};n.backend.onEvent(C),f.push(()=>n.backend.offEvent?.(C));let j=setInterval(()=>{if(s||Date.now()-y.lastProgressLogAt<12e3)return;let z=(N.generation?.steps||[]).find(ne=>ne.status==="running");y.note(z?.label?`${z.label}\u2026`:V("generationProgressPulse"),z?.detail||"","pulse",0)},12e3);f.push(()=>clearInterval(j))}),S=r.resultKind!=="text",F=await hb(x,i,o,m,{expectJson:S}),L=typeof F=="string"?F:F?.answer||"",R=typeof F=="string"?"":F?.thinking||"";if(fi(a))throw new Error("Generation stopped");if(!S)return{payload:null,text:L,sessionId:i,toolTrace:p,completion:u};let E=await pb(i,o,L,R);if(fi(a))throw new Error("Generation stopped");let T=wa(E);if(fi(a))throw new Error("Generation stopped");return{payload:T,sessionId:i,toolTrace:p,completion:u}}catch(b){if(b&&typeof b=="object"&&i&&(b.pptLiveSessionId=i,b.pptLiveToolTrace=p),!s&&i&&o&&n.backend?.cancel)try{await n.backend.cancel(i,o)}catch(x){ot().log?.warn?.("PPT Live backend cancel after failure failed",{sessionId:i,turnId:o,error:String(x)})}throw b}finally{f.forEach(b=>b()),i&&o&&ob(i,o)}}function qy(e,t){return{operation:e,instruction:t,locale:Nr(),brief:Cy(),style:Kl()}}async function jy(e,t){let r=Xn;ba(!0,V("working")),Ko(),hr("brief","running",V("generationReadingBrief")),Tr({title:V("processEventStarted"),detail:V("processEventWaiting"),kind:"start"}),Vy(e,t);let n=!1,a=Ly()?Iy()||Ny():null;a&&!N.agentSession?.workspaceSubdir&&(await Wy(a.runId),await Uy(a));let i={id:N.agentSession?.id||null,project:a},o={value:""},l={touch:()=>{},note:()=>{},lastProgressLogAt:0},c=new Map,s=null;try{let d=null;for(let u=1;u<=di;u+=1)try{u>1&&(Tr({title:V("generationRetryAttempt",{attempt:u,max:di}),detail:Ef(d),kind:"start"}),Zt(V("generationRetrying",{attempt:u,max:di})),await new Promise(y=>setTimeout(y,Ey(d,u))));let f={...qy(e,t),...i?.id?{continueAfterInterruption:!0}:{}};nn()&&(f.currentSlideIndex=Jt(N),f.currentDeck=Fy(t));let{sessionId:p}=await Gy(f,{onToolPhase:y=>{y==="detected"?hr("brief","running",V("generationReadingBrief")):y==="completed"?(hr("brief","done"),hr("spine","running",V("generationWritingClaims"))):y==="research"?hr("proof","running",V("generationChoosingProof")):y==="round"&&hr("spine","running",V("generationWritingClaims"))},onTextProgress:y=>nb(y,l,o),onSlideFileWritten:a?async y=>{let m=await zy(a,y);if(!m)return;c.set(y,m);let b=Array.isArray(s?.outline)?s.outline.length:0;if(!s||y>b){let L=await Zl(a);if(L){s=L;let R=Ai({plan:L,state:N,instruction:t});Nf(R)||(N.title=R)}}let x=s||{},S=[...c.entries()].sort((L,R)=>L[0]-R[0]).map(([L,R])=>({id:`ppt-live-slide-${L}`,slideNumber:L,title:typeof x?.outline?.[L-1]=="string"?x.outline[L-1]:x?.outline?.[L-1]?.title||`${V("newSlideTitle")} ${L}`,html:R})),F=Ai({plan:x,state:N,instruction:t,slides:S});hr("design","running",V("generationSlideReady",{slide:y,total:S.length})),Zt(V("generationRenderingSlide",{slide:y,total:x?.outline?.length||S.length})),Tr({title:V("generationSlideReady",{slide:y,total:x?.outline?.length||S.length}),detail:"",kind:"slide"}),hf({title:F,language:x?.language||"",outline:Lf(x||{}),researchReport:x?.researchReport||null,design:x?.design||{},slides:S},{instruction:t}),N.activeSlideId=`ppt-live-slide-${y}`,N.selectedElementId="",Yt()}:void 0},{sessionId:i?.id||void 0,appDataWorkspace:i?.project?.workspaceSubdir,resultKind:a?"text":void 0});i.id=p||i.id,N.agentSession={id:i.id||"",workspaceSubdir:i?.project?.workspaceSubdir||"",runId:i?.project?.runId||"",skillKey:lf},Tr({title:V("generationParsingDeck"),detail:"",kind:"parsing"}),Zt(V("generationParsingDeck")),hr("design","running",V("generationDesigningLayouts"));let A=a?await Oy(a):null;if(!A)throw new Error("PPT Live agent did not produce a readable deck");hf(A,{instruction:t}),await jl(`agent:${e}`),Tr({title:V("processEventDone"),detail:"",kind:"done"}),hr("spine","done"),hr("proof","done"),hr("design","done"),hr("compile","done",V("generationCompiled")),Bf(V("deckReady")),n=!0,Yt(),await Pt(!0);break}catch(f){if(d=f,Ry(f)?i.id=null:f?.pptLiveSessionId&&(i.id=f.pptLiveSessionId),!By(f)||u>=di)throw f;ot().log?.warn?.("PPT Live cowork generation attempt failed, retrying",{attempt:u,maxAttempts:di,continueInSession:!!i.id,error:String(f)})}}finally{!fi(r)&&(N.generation.active&&!n&&(N.generation.active=!1),ba(!1)),vr(N),Ir(N)}}function Vy(e,t){Zt(V("generationAgentWorking")),Tr({title:V("generationAgentWorking"),detail:Bt(t||""),kind:"start"}),e==="auto"&&(Df()||Xl())&&(N.title=V("agentWorkingTitle")),Yt()}var Xy=new Set(["ParamsPartial","Queued","Waiting","Progress","Streaming","StreamChunk","Confirmed","Rejected","EarlyDetected","Started"]),Hy=new Set(["read","write","grep","glob","list","todowrite","todo_write","skill","bash","shell","edit","delete","apply_patch","strreplace","search_replace"]);function zf(e,t={}){let r=String(e||"").trim().toLowerCase();return!r||Hy.has(r)?"":r==="websearch"?t.isSubagent?V("eventSubagentWebSearchDone"):V("eventToolWebSearchDone"):r==="webfetch"?t.isSubagent?V("eventSubagentWebFetchDone"):V("eventToolWebFetchDone"):r==="task"?V("eventToolTaskDone"):""}function _y(e,t,r={}){let n=Jl(e),a=n.event_type||n.eventType||"";if(Xy.has(a))return!1;let i=String(n.tool_name||n.toolName||"tool").toLowerCase();if(a==="Completed"&&!zf(i,r))return!1;let o=Of(n)||String(n.params&&typeof n.params=="object"&&n.params.command||"").trim(),l=o?`${i}:${o}:${a}`:`${i}:${a}`;return t.has(l)?!1:(t.add(l),a==="Completed"||a==="Failed"||a==="Cancelled"||a==="ConfirmationNeeded")}function Ky(){let e=0,t="";return{get lastProgressLogAt(){return e},touch(){e=Date.now()},note(r,n="",a="phase",i=0){let o=Date.now(),l=r===t;return i>0&&l&&o-eo?.content||"").filter(Boolean).join(" | "),200)}if(r==="execcommand"||r==="bash"||r==="shell")return Bt(String(n.cmd||n.command||""),160);let a=Object.values(n).find(i=>typeof i=="string"&&i.trim());return Bt(String(a||""),160)}function Zy(e,t={}){let r=String(e||"").toLowerCase(),n=t&&typeof t=="object"?t:{};if(r==="websearch"){let a=Array.isArray(n.results)?n.results:[];return a.length?`${a.length} results`:""}if(r==="webfetch"||r==="mcp__web_reader__webreader"){let a=String(n.content||n.text||n.markdown||"").length;return a?`${a} chars`:""}if(r==="read"){let a=Number(n.lineCount||(Array.isArray(n.lines)?n.lines.length:0));return a?`${a} lines`:""}if(r==="write"||r==="edit")return Bt(String(n.message||"written"),80);if(r==="grep"||r==="glob"){let a=Array.isArray(n.matches)?n.matches.length:Array.isArray(n.files)?n.files.length:0;return a?`${a} matches`:"no matches"}return r==="skill"?Bt(String(n.skill_key||n.message||"loaded"),80):r==="task"?Bt(String(n.result||n.message||""),160):""}function Yy(e){let t=String(e||"");return/"html"\s*:/.test(t)?"design":/"slides"\s*:/.test(t)?"proof":(/"outline"\s*:/.test(t),"spine")}function Jy(e){switch(e){case"proof":return V("generationChoosingProof");case"design":return V("generationDesigningLayouts");default:return V("generationWritingClaims")}}function ff(e,t){let n=new RegExp(`"${t}"\\s*:\\s*\\[`).exec(String(e||""));return n?String(e).slice(n.index+n[0].length):""}function $y(e){let t=0,r=0,n=!1,a=!1;for(let i=0;i0&&(N.generation.draftedCount=r),vr(N),Ir(N)}function nb(e,t,r){let n=Yy(e),a=Jy(n);hr(n,"running",a),rb(e,n),t.touch()}function ab(e,t={}){let r=Jl(e.toolEvent||{}),n=r.event_type||r.eventType||"ToolEvent",a=String(r.tool_name||r.toolName||"").trim();if(n==="Completed"){let i=zf(a,t);return i?{title:i,detail:"",kind:"tool"}:null}return n==="Failed"||n==="Cancelled"?{title:V("eventToolFailedUser"),detail:"",kind:"error"}:n==="ConfirmationNeeded"?{title:V("processEventWaiting"),detail:"",kind:"tool"}:null}function Of(e,t=[]){let r=e?.params&&typeof e.params=="object"?e.params:{},n=String(r.file_path||r.path||"").trim();if(n)return n;let a=e?.result&&typeof e.result=="object"?e.result:{},i=String(a.file_path||a.path||"").trim();if(i)return i;let o=String(e?.tool_id||e?.toolId||"").trim();if(!o)return"";let l=[...t].reverse().find(s=>s.eventType==="Started"&&String(s.toolId||s.tool_id||"")===o),c=l?.params&&typeof l.params=="object"?l.params:{};return String(c.file_path||c.path||"").trim()}function Jl(e){if(e.event_type||e.eventType||e.tool_name||e.toolName)return e;let r=["EarlyDetected","ParamsPartial","Queued","Waiting","Started","Progress","Streaming","StreamChunk","ConfirmationNeeded","Confirmed","Rejected","Completed","Failed","Cancelled"].find(a=>e&&Object.prototype.hasOwnProperty.call(e,a));return r?{...e[r]||{},event_type:r}:e||{}}function Bt(e,t=180){let r=String(e||"").replace(/\s+/g," ").trim();return r?r.length>t?`${r.slice(0,t-1)}...`:r:""}function ib(e,t){if(!e||!t)return;Fn.some(n=>n.sessionId===e&&n.turnId===t)||Fn.push({sessionId:e,turnId:t})}function ob(e,t){Fn=Fn.filter(r=>!(r.sessionId===e&&r.turnId===t))}function fi(e){return e!==Xn}async function $l(){let e=[...Fn];Fn=[],!(!e.length||!ot().backend?.cancel)&&await Promise.all(e.map(async t=>{try{await ot().backend.cancel(t.sessionId,t.turnId)}catch(r){ot().log?.warn?.("PPT Live backend cancel failed",{sessionId:t.sessionId,turnId:t.turnId,error:String(r)})}}))}async function sb(e=!1,t={}){let r=Fn.length>0;Xn+=1,await $l(),N.generation.active=!1,N.generation.steps=N.generation.steps.map(n=>n.status==="running"?{...n,status:"error"}:n),!t.silent&&r&&(Zt(e?V("generationTimedOut"):V("generationStopped")),Tr(e?V("generationTimedOut"):V("generationStopped"))),ba(!1),vr(N),Ir(N),t.silent||await Pt(!0)}async function lb(e=!1){await sb(e)}function hf(e,t={}){if(ub(e,t)){e.researchReport&&pf(e.researchReport),e.design?.palette&&typeof e.design.palette=="object"&&(N.deckPalette=e.design.palette);return}let r=Ai({payload:e,state:N,instruction:t.instruction||"",slides:e?.slides||[]}),n=fb(e);if(n.length)N.title=r,N.slides=n.map((a,i)=>Gr(a,i,{...N,slides:n})),N.outline=N.slides.map(a=>a.title),N.activeSlideId=N.slides[0]?.id||"",N.selectedElementId="";else{if(!Array.isArray(e?.slides)||e.slides.length===0)throw new Error("PPT Live deck payload has no slides");N.title=r,N.slides=e.slides.map((a,i)=>Gr({...a,html:a.html||a.sourceHtml||a.slideHtml||""},i,{...N,slides:e.slides})),N.outline=N.slides.map(a=>a.title),N.activeSlideId=N.slides[0]?.id||"",N.selectedElementId=N.slides[0]?.elements[0]?.id||""}Array.isArray(e.outline)&&e.outline.length&&(N.outline=e.outline.map(Ql).filter(Boolean)),e.researchReport&&pf(e.researchReport),e.design?.palette&&typeof e.design.palette=="object"&&(N.deckPalette=e.design.palette)}function pf(e){N.sources={...N.sources,facts:e.verifiedFacts||N.sources?.facts||[],warnings:e.warnings||N.sources?.warnings||[],summary:e.summary||N.sources?.summary||"",fetchedAt:Date.now()}}function cb(e){return Array.isArray(e?.deckPatch?.changes)?e.deckPatch.changes:Array.isArray(e?.patch?.changes)?e.patch.changes:Array.isArray(e?.changes)?e.changes:Array.isArray(e?.patches)?e.patches:[]}function Af(e,t,r=0){let n=String(e?.slideId||e?.id||e?.targetSlideId||e?.targetId||"").trim();if(n){let o=t.findIndex(l=>l.id===n);if(o>=0)return o}let a=Number(e?.slideNumber??e?.pageNumber);if(Number.isFinite(a)&&a>0)return vt(Math.round(a)-1,0,Math.max(0,t.length-1));let i=Number(e?.slideIndex??e?.index??e?.targetSlideIndex);return Number.isFinite(i)?i>=t.length&&i-1>=0&&i-1i.id===r);if(a>=0)return a+1}let n=String(e?.beforeSlideId||"").trim();if(n){let a=t.findIndex(i=>i.id===n);if(a>=0)return a}return e?.afterSlideNumber?vt(Number(e.afterSlideNumber),0,t.length):e?.beforeSlideNumber?vt(Number(e.beforeSlideNumber)-1,0,t.length):e?.slideNumber?vt(Number(e.slideNumber)-1,0,t.length):e?.slideIndex!==void 0?vt(Number(e.slideIndex),0,t.length):Math.min(t.length,Jt(N)+1)}function gf(e,t,r,n){let a=e?.slide||e?.replacement||e?.newSlide||e?.payload||e;if(!a||typeof a!="object")return null;let i={...t||{},...a,id:a.id||a.slideId||t?.id||ar("html-slide"),html:a.html||a.sourceHtml||a.slideHtml||t?.html||""};return Gr(i,r,{...N,slides:n})}function ub(e,t={}){let r=cb(e);if(!r.length)return!1;let n=sn(N.slides||[]),a=[],i=0;if(r.forEach(l=>{let c=String(l?.op||l?.operation||l?.type||"replace_slide").toLowerCase();if(c==="delete_slide"||c==="delete"||c==="remove_slide"||c==="remove"){if(!n.length)return;let f=Af(l,n,Jt(N)),[h]=n.splice(f,1);h?.id&&a.push(h.id),i+=1;return}if(c==="insert_slide"||c==="insert"||c==="add_slide"||c==="add"){let f=db(l,n),h=gf(l,null,f,n);if(!h)return;n.splice(f,0,h),a.push(h.id),i+=1;return}let s=Af(l,n,Jt(N)),d=n[s],u=gf(l,d,s,n);u&&(n[s]=u,a.push(u.id),i+=1)}),!i)throw new Error("PPT Live deck patch had no applicable changes");N.title=Ai({payload:e,state:N,instruction:t.instruction||"",slides:n}),N.slides=n.map((l,c)=>Gr(l,c,{...N,slides:n})),N.outline=Array.isArray(e.outline)&&e.outline.length?e.outline.map(String):N.slides.map(l=>l.title);let o=a.find(l=>N.slides.some(c=>c.id===l));return N.activeSlideId=o||N.slides[Math.min(Jt(N),N.slides.length-1)]?.id||N.slides[0]?.id||"",N.selectedElementId=Vt(N)?.elements?.[0]?.id||"",!0}function fb(e){let t=[];return Array.isArray(e?.htmlSlides)&&t.push(...e.htmlSlides),Array.isArray(e?.slides)&&t.push(...e.slides.filter(r=>r?.html||r?.sourceHtml||r?.slideHtml)),t.map((r,n)=>{let a=String(r?.html||r?.sourceHtml||r?.slideHtml||"").trim();return a?{id:r.id||r.slideId||ar("html-slide"),title:String(r.title||r.label||`${V("newSlideTitle")} ${n+1}`),subtitle:String(r.subtitle||""),kicker:String(r.kicker||""),claim:String(r.claim||r.title||""),proofObject:String(r.proofObject||""),supportNote:String(r.supportNote||""),sourceNote:String(r.sourceNote||""),notes:String(r.notes||""),layout:"html",theme:r.theme||{},html:a,elements:[]}:null}).filter(Boolean)}function vf(...e){for(let t of e){let r=String(t||"").trim();if(r)try{return wa(r),r}catch{}}return String(e.find(t=>String(t||"").trim())||"").trim()}async function hb(e,t,r,n=null,a={}){let i=ot(),o=a.expectJson!==!1;if(!t||!r||!i.backend?.turnText)return e;let l=!1,c=Promise.resolve(e).finally(()=>{l=!0}),s=new Promise((d,u)=>{let f=Date.now(),h=300*1e3,p=10*1e3,A=3600*1e3,y=()=>Number(n?.lastEventAt||f);(async()=>{for(;!l&&Date.now()-fh)break;if(bsetTimeout(x,Math.min(1e3,p-b)));continue}try{let x=await i.backend.turnText(t,r),S=String(x?.text||"").trim();if(S){if(!o){d({answer:S,thinking:""});return}try{wa(S),d({answer:S,thinking:""});return}catch{}}}catch{}await new Promise(x=>setTimeout(x,2e3))}l||u(new Error("PPT Live backend did not publish a final deck JSON"))})()});return Promise.race([c,s])}async function pb(e,t,r,n=""){let a=Date.now(),i=25e3,o=String(r||"").trim(),l=String(n||"").trim(),s=vf(o,l,`${o} +${l}`.trim());if(s)try{return wa(s),s}catch{}let d=ot();if(!e||!t||!d.backend?.turnText){if(!s)throw new Error("PPT Live backend produced no text");return s}let u=0;for(;Date.now()-a{setTimeout(()=>A(new Error("turnText timeout")),4e3)})]),h=String(f?.text||"").trim();if(s=vf(h,s,l,o),s)return wa(s),s}catch{}await new Promise(f=>setTimeout(f,500))}if(!s)throw new Error("PPT Live backend produced no text");return s}function wa(e){let t=String(e||"").trim();if(!t)throw new Error("PPT Live backend produced no text");try{return JSON.parse(t)}catch{let r=t.match(/```(?:json)?\s*([\s\S]*?)```/i);if(r)return JSON.parse(r[1]);let n=t.indexOf("{"),a=t.lastIndexOf("}");if(n>=0&&a>n)return JSON.parse(t.slice(n,a+1));throw new Error("PPT Live backend returned invalid JSON")}}function Ab(e){let t=String(e?.message||e||"");return/ppt_live:\/\/round-budget-exhausted|exhausted its \d+-round tool budget|tool budget before producing deck JSON/i.test(t)}function gb(e){return String(e||"").includes("timed out")}function Qo(e){let t=String(e||"");return t.includes("dialog-turn-cancelled")||t.includes("Generation stopped")}async function vb(e,t={}){let r=nn();t.readBrief!==!1&&rn({includeTopic:!r});let n=[e,Tf()].filter(Boolean).join(": ");if(!n){Zt(V("promptRequired"));return}try{await Yl("revise_slide",n,{includeTopic:!r})}catch(a){if(Qo(a))return;ot().log?.warn?.("PPT Live backend slide revision failed",{action:e,error:String(a)}),_l(a),await Pt(!0)}}async function mb(){if(rn({includeTopic:!nn()}),(N.slides||[]).some(e=>String(e?.html||"").trim())){let e=`Restyle the existing deck without changing its facts or narrative. Apply these exact settings to every slide HTML: ${JSON.stringify(Kl())}. Preserve each page's informationIntent and visualStrategy while making the deck visually coherent.`;try{await Yl("revise_deck",e,{includeTopic:!1});return}catch(t){if(Qo(t))return;ot().log?.warn?.("PPT Live Agent restyle failed",{error:String(t)}),_l(t),await Pt(!0);return}}N.slides=N.slides.map((e,t)=>Gr({...e,theme:void 0},t,N)),Zt(V("deckRestyled")),Yt(),await Pt(!0)}function Wl(){rn({includeTopic:!nn()});let e=new Map(N.slides.map(t=>[t.title,t]));N.slides=N.outline.map((t,r)=>{let n=e.get(t);return n?Gr(n,r,N):os(t,r,N.outline.length,N)}),N.activeSlideId=N.slides[0]?.id||"",N.selectedElementId=N.slides[0]?.elements[0]?.id||"",Yt(),Pt(!0)}async function yb(){Xn+=1,await jl("before-new"),await $l(),N.generation.active=!1,ba(!1),N=bb(),Ko(),Yt(),Ta(N),Zt(V("blankDeckReady")),await Pt(!0)}function bb(){return ln(Bn())}function wb(e){if(!ns.includes(e))return;let t=Vt(N);if(!t)return;let r=ss({...Si(e),x:10+t.elements.length%5*4,y:14+t.elements.length%5*4});t.elements.push(r),N.selectedElementId=r.id,Yt(),Pt(!0)}function xb(){let e=Vt(N);!e||!N.selectedElementId||(e.elements=e.elements.filter(t=>t.id!==N.selectedElementId),N.selectedElementId=e.elements[0]?.id||"",Yt(),Pt(!0))}function mf(e){let t=e.elements.find(r=>r.type==="text"&&r.text);t&&(e.title=t.text.slice(0,90),N.outline[Jt(N)]=e.title,Jt(N)===0&&(N.title=e.title))}function yf(){N.presentIndex=Jt(N),Uf(),Se("previewDialog")?.showModal()}function Uf(){let e=N.slides[N.presentIndex]||N.slides[0];Se("presentSlide")&&(Se("presentSlide").innerHTML=e?dn(e):""),Se("presentCounter")&&(Se("presentCounter").textContent=`${Math.max(1,N.presentIndex+1)} / ${Math.max(1,N.slides.length)}`),Rn()}function Uo(e){N.presentIndex=vt(N.presentIndex+e,0,N.slides.length-1),Uf()}function Sb(){if(!(N.slides||[]).length)return ma(V("exportDeckEmpty")),null;rn({includeTopic:!nn()});let e=Nl(N);return ma(V("exportSavedTo",{path:e})),e}function Cb(){return rn({includeTopic:!nn()}),(N.slides||[]).length?!0:(ma(V("exportDeckEmpty")),!1)}function Wf(e){return{html:{working:V("exportHtmlWorking"),done:V("exportHtmlDone"),failed:V("exportHtmlFailed")},pptx:{working:V("exportPptxWorking"),done:V("exportPptxDone"),failed:V("exportPptxFailed")},pdf:{working:V("exportPdfWorking"),done:V("exportPdfDone"),failed:V("exportPdfFailed")},png:{working:V("exportPngWorking"),done:V("exportPngDone"),failed:V("exportPngFailed")}}[e]||null}function Gl(e,t,r){let n=Wf(r==="pptx"?"pptx":r);if(!n||t<=0)return;let a=Math.min(t,Math.max(1,e+1));qo("loading",`${n.working} (${a}/${t})`)}async function bf(e,t){let r=ot();if(!r?.deck?.renderPage)throw new Error("Host WebView export is unavailable in this runtime.");let n=[],a=e.length;for(let[i,o]of e.entries()){Gl(i,a,t);let l=await r.deck.renderPage({html:bs(o),format:t,width:ir.width,height:ir.height});if(!l)throw new Error(`Host WebView returned empty ${t} for slide ${i+1}`);n.push({index:i,base64:String(l).replace(/^data:.*;base64,/,"")})}return n}async function kb(e){if(e==="html"){rn({includeTopic:!nn()});let o=Nl(N);if(!o)throw new Error(V("exportDeckEmpty"));return{filename:o}}let t=N.slides||[];if(!t.length)throw new Error(V("exportDeckEmpty"));let r,n=sn(N);if(e==="pptx")if(t.some(o=>o?.html)){let o=ot(),l=typeof o?.deck?.renderPage=="function"?async(s,d)=>{Gl(d,t.length,"pptx");let u=await o.deck.renderPage({html:s,format:"png",width:ir.width,height:ir.height});return String(u||"").replace(/^data:.*;base64,/,"")}:null,c=await Xc(t,{renderRaster:l,onRasterProgress:s=>Gl(s,t.length,"pptx")});r=await El(n,c)}else r=await Bl(n);else if(e==="pdf"){let o=await bf(t,"pdf");r=await Rl(n,o.map(l=>l.base64))}else if(e==="png"){let o=await bf(t,"png");r=await Ll(n,o)}else throw new Error(V("exportFormatUnavailable"));let a=typeof r?.base64=="string"?r.base64.replace(/^data:.*;base64,/,""):"";if(!a)throw new Error(`export${e} returned no data`);let i=r.filename||`${Il(N.title||"ppt-live")}`;return nf(a,i,r.mimeType||"application/octet-stream"),{filename:i}}var Go=!1,Qt={updateOutline(e,t){N.outline[e]=t,N.slides[e]&&(N.slides[e].title=t),Yt(),Pt(!0)},moveOutline(e,t){let r=e+t;r<0||r>=N.outline.length||([N.outline[e],N.outline[r]]=[N.outline[r],N.outline[e]],Wl())},removeOutline(e){N.outline.length<=1||(N.outline.splice(e,1),Wl())},selectSlide(e){N.activeSlideId=e,N.selectedElementId=Vt(N)?.elements[0]?.id||"",Yt(),Pt(!0)},selectElement(e){N.selectedElementId=e,cn(N,Qt),Mi(N,Qt),Pt(!0)},updateElementTextDirect(e,t){let r=Vt(N),n=r?.elements.find(a=>a.id===e);n&&(n.text=String(t||"").trim(),mf(r),Ln(N,Qt),renderOutline(N,Qt),Pt(!1))},updateElementListItemDirect(e,t,r){let a=Vt(N)?.elements.find(i=>i.id===e);!a||!Array.isArray(a.items)||(a.items[t]=String(r||"").trim(),a.items=a.items.filter(Boolean),cn(N,Qt),Ln(N,Qt),Pt(!1))},updateSlideHtmlDirect(e,t){let r=N.slides.find(a=>a.id===e);if(!r)return;let n=String(t||"");r.html!==n&&(r.html=n,Ln(N,Qt),Pt(!1))},updateSlideNotes(e){let t=Vt(N);t&&(t.notes=e),Pt(!0)},updateSlideMethodology(){let e=Vt(N);e&&(e.kicker=Se("slideKickerInput")?.value||e.kicker,e.claim=Se("slideClaimInput")?.value||e.claim,e.proofObject=Se("slideProofInput")?.value||e.proofObject,e.supportNote=Se("slideSupportInput")?.value||e.supportNote,e.sourceNote=Se("slideSourceInput")?.value||e.sourceNote,cn(N,Qt),Ln(N,Qt),Pt(!0))},updateElementFromInspector(){let e=Vt(N),t=En(N);!e||!t||(t.text=Se("elementTextInput")?.value||"",t.items=(Se("elementItemsInput")?.value||"").split(` +`).map(r=>r.trim()).filter(Boolean),t.data=Fb(Se("elementDataInput")?.value||""),t.x=vt(Number(Se("elementXInput")?.value??t.x),0,100),t.y=vt(Number(Se("elementYInput")?.value??t.y),0,100),t.w=vt(Number(Se("elementWInput")?.value??t.w),3,100),t.h=vt(Number(Se("elementHInput")?.value??t.h),3,100),t.style.fontSize=vt(Number(Se("elementFontInput")?.value??t.style.fontSize),8,88),t.style.fontWeight=vt(Number(Se("elementWeightInput")?.value??t.style.fontWeight),100,900),t.style.color=Se("elementColorInput")?.value||t.style.color,t.style.background=Se("elementBgInput")?.value||t.style.background,Qt.updateSlideMethodology(),e.notes=Se("slideNotesInput")?.value||e.notes,mf(e),cn(N,Qt),Pt(!0))},beginDrag(e,t){if(e.button!==0)return;let n=Vt(N)?.elements.find(i=>i.id===t);if(!n)return;N.selectedElementId=n.id;let a=Se("slideCanvas").getBoundingClientRect();Fr={resizing:e.target.classList.contains("resize-handle"),startX:e.clientX,startY:e.clientY,rect:a,start:{x:n.x,y:n.y,w:n.w,h:n.h}},e.currentTarget.setPointerCapture?.(e.pointerId),window.addEventListener("pointermove",Gf),window.addEventListener("pointerup",Pb,{once:!0})}};function Gf(e){if(!Fr)return;let t=En(N);if(!t)return;let r=(e.clientX-Fr.startX)/Fr.rect.width*100,n=(e.clientY-Fr.startY)/Fr.rect.height*100;Fr.resizing?(t.w=vt(Fr.start.w+r,3,100-t.x),t.h=vt(Fr.start.h+n,3,100-t.y)):(t.x=vt(Fr.start.x+r,0,100-t.w),t.y=vt(Fr.start.y+n,0,100-t.h)),cn(N,Qt),Mi(N,Qt)}function Pb(){Fr=null,window.removeEventListener("pointermove",Gf),Pt(!0)}function Fb(e){return e.split(` +`).map((t,r)=>{let[n,a]=t.split(":");return{label:(n||`Item ${r+1}`).trim(),value:Number(a||0)}}).filter(t=>t.label)}function Tb(){let e=document.querySelector(".studio-shell");if(!e)return;let t=document.documentElement,r=Number(Ol("pptLiveFilmstripWidth")||0),n=Number(Ol("pptLiveAgentWidth")||0);r>=128&&r<=360&&t.style.setProperty("--filmstrip-width",`${r}px`),n>=240&&n<=460&&t.style.setProperty("--agent-width",`${n}px`);let a=(i,o)=>{let l=e.getBoundingClientRect(),c=128,s=Math.min(360,l.width*.34),d=240,u=Math.min(460,l.width*.42),f=360,h=A=>{if(i==="filmstrip"){let y=Math.max(c,Math.min(s,A.clientX-l.left));if(l.width-y-parseFloat(getComputedStyle(t).getPropertyValue("--agent-width"))-12{e.classList.remove("is-resizing"),document.querySelectorAll(".panel-resizer.is-dragging").forEach(A=>A.classList.remove("is-dragging")),window.removeEventListener("pointermove",h),window.removeEventListener("pointerup",p),window.removeEventListener("pointercancel",p),Ul("pptLiveFilmstripWidth",String(parseFloat(getComputedStyle(t).getPropertyValue("--filmstrip-width"))||"")),Ul("pptLiveAgentWidth",String(parseFloat(getComputedStyle(t).getPropertyValue("--agent-width"))||"")),Rn()};e.classList.add("is-resizing"),window.addEventListener("pointermove",h),window.addEventListener("pointerup",p,{once:!0}),window.addEventListener("pointercancel",p,{once:!0}),h({clientX:o})};Se("filmstripResizer")?.addEventListener("pointerdown",i=>{i.button===0&&(i.preventDefault(),i.currentTarget.classList.add("is-dragging"),a("filmstrip",i.clientX))}),Se("agentResizer")?.addEventListener("pointerdown",i=>{i.button===0&&(i.preventDefault(),i.currentTarget.classList.add("is-dragging"),a("agent",i.clientX))})}function Db(){let e=null,t=()=>{e&&clearTimeout(e),e=setTimeout(()=>{Rn()},60)};window.addEventListener("resize",t),Se("toggleHistory")?.addEventListener("click",()=>{let r=Se("historyDrawer");r&&(r.hidden=!r.hidden)}),Se("closeHistory")?.addEventListener("click",()=>{let r=Se("historyDrawer");r&&(r.hidden=!0)}),document.querySelectorAll("[data-sidebar-tab]").forEach(r=>{r.addEventListener("click",()=>{let n=r.dataset.sidebarTab;document.querySelectorAll("[data-sidebar-tab]").forEach(a=>{a.classList.toggle("is-active",a.dataset.sidebarTab===n)}),document.querySelectorAll("[data-sidebar-panel]").forEach(a=>{a.classList.toggle("is-active",a.dataset.sidebarPanel===n)})})}),Se("topicInput")?.addEventListener("input",()=>{if(nn()){N.promptDraft=Se("topicInput")?.value||"",Pt(!0);return}rn({includeTopic:!0}),Pt(!0)}),Se("newDeck")?.addEventListener("click",()=>{yb()}),Se("cancelGeneration")?.addEventListener("click",()=>{lb(!1)}),Se("sendPrompt")?.addEventListener("click",()=>{Hl()}),Se("generateOutline")?.addEventListener("click",()=>{by()}),Se("generateDeck")?.addEventListener("click",()=>{wy()}),Se("addOutlineItem")?.addEventListener("click",()=>{N.outline.push(V("newSlideTitle")),Yt(),Pt(!0)}),Se("syncSlidesFromOutline")?.addEventListener("click",Wl),Se("deleteElement")?.addEventListener("click",xb),Se("previewDeck")?.addEventListener("click",yf),Se("closePreview")?.addEventListener("click",()=>Se("previewDialog")?.close()),Se("prevPresent")?.addEventListener("click",()=>Uo(-1)),Se("nextPresent")?.addEventListener("click",()=>Uo(1)),Se("exportHtml")?.addEventListener("click",Sb),Se("restyleDeck")?.addEventListener("click",mb),document.querySelectorAll("[data-add-element]").forEach(r=>{r.addEventListener("click",()=>wb(r.dataset.addElement))}),document.querySelectorAll(".ai-action").forEach(r=>{r.addEventListener("click",()=>{vb(r.dataset.action)})}),document.querySelectorAll(".segment").forEach(r=>{r.addEventListener("click",()=>{N.mode=r.dataset.mode,N.mode==="present"&&yf(),Yt(),Pt(!0)})}),document.addEventListener("keydown",r=>{Se("previewDialog")?.open&&((r.key==="ArrowRight"||r.key==="PageDown")&&Uo(1),(r.key==="ArrowLeft"||r.key==="PageUp")&&Uo(-1),r.key==="Escape"&&Se("previewDialog")?.close())});try{Tb()}catch(r){ot().log?.warn?.("Failed to bind PPT Live panel resizers",{error:String(r)})}if(typeof ResizeObserver<"u"){let r=[document.querySelector(".ppt-live"),document.querySelector(".studio-shell"),document.querySelector(".stage-shell"),document.querySelector(".canvas-area")].filter(Boolean),n=new ResizeObserver(t);r.forEach(a=>n.observe(a))}Rb(),Lb(),Nb(),Ub(),Gb()}var en=1,va=.25,Bb=.25,Eb=2;function ui(e){en=vt(e,Bb,Eb);let t=document.querySelector(".canvas-stage");t&&(t.style.transform=en===1?"":`scale(${en})`);let r=Se("zoomValue"),n=Se("statusZoomValue"),a=Math.round(en*100)+"%";r&&(r.textContent=a),n&&(n.textContent=a)}function Rb(){Se("zoomIn")?.addEventListener("click",()=>ui(en+va)),Se("zoomOut")?.addEventListener("click",()=>ui(en-va)),Se("statusZoomIn")?.addEventListener("click",()=>ui(en+va)),Se("statusZoomOut")?.addEventListener("click",()=>ui(en-va)),document.querySelector(".canvas-area")?.addEventListener("wheel",e=>{if(e.ctrlKey||e.metaKey){e.preventDefault();let t=e.deltaY>0?-va:va;ui(en+t)}},{passive:!1})}function Lb(){Se("floatingToolbar")&&document.querySelectorAll(".floating-toolbar-btn").forEach(t=>{t.addEventListener("click",()=>{let r=t.dataset.tool;if(!r)return;let n=Vt(N),a=En(N);if(!(!n||!a)){switch(r){case"bold":a.fontWeight=a.fontWeight==="700"?"400":"700";break;case"italic":a.fontStyle=a.fontStyle==="italic"?"normal":"italic";break;case"underline":a.textDecoration=a.textDecoration==="underline"?"none":"underline";break;case"align-left":a.align="left";break;case"align-center":a.align="center";break;case"align-right":a.align="right";break;case"duplicate":n.elements.push({...sn(a),id:ar("el"),x:a.x+5,y:a.y+5});break;case"delete":n.elements=n.elements.filter(i=>i.id!==a.id),N.selectedElementId=null;break}cn(N,Qt),Ln(N,Qt),Pt(!0)}})})}function Nb(){document.querySelectorAll(".property-section__header").forEach(n=>{let a=n.closest(".property-section");if(!a)return;let i=()=>{a.classList.toggle("is-collapsed");let o=!a.classList.contains("is-collapsed");n.setAttribute("aria-expanded",String(o))};n.addEventListener("click",i),n.addEventListener("keydown",o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),i())})});let e=Se("densitySlider"),t=e?.querySelector(".density-slider__track");e&&t&&(t.addEventListener("pointerdown",n=>{n.preventDefault(),kn(uf(n.clientX,t)),t.setPointerCapture(n.pointerId)}),t.addEventListener("pointermove",n=>{t.hasPointerCapture(n.pointerId)&&kn(uf(n.clientX,t))}),t.addEventListener("pointerup",n=>{t.hasPointerCapture(n.pointerId)&&t.releasePointerCapture(n.pointerId)}),t.addEventListener("pointercancel",n=>{t.hasPointerCapture(n.pointerId)&&(t.releasePointerCapture(n.pointerId),Ni(N.style?.density))}),e.querySelectorAll("[data-density-index]").forEach(n=>{n.addEventListener("click",a=>{a.stopPropagation(),kn(n.dataset.densityIndex)})}),e.addEventListener("keydown",n=>{let a=Se("densitySlider"),i=Number(a?.dataset.index??1);n.key==="ArrowLeft"||n.key==="ArrowDown"?(n.preventDefault(),kn(i-1)):n.key==="ArrowRight"||n.key==="ArrowUp"?(n.preventDefault(),kn(i+1)):n.key==="Home"?(n.preventDefault(),kn(0)):n.key==="End"&&(n.preventDefault(),kn(2))})),document.querySelectorAll("[data-font-family]").forEach(n=>{n.addEventListener("click",()=>{Ri(n.dataset.fontFamily==="serif"?"serif":"sans")})}),document.querySelectorAll("[data-color-mode]").forEach(n=>{n.addEventListener("click",()=>{Li(n.dataset.colorMode==="dark"?"dark":"light")})});let r=Se("stylePresetSelect");r&&(Xf(),mc(r),Ta(N),r.addEventListener("change",()=>{let n=r.value;if(!n)return;let a=Jo(n);a&&(Li(a.colorMode||"light"),Ri(a.fontFamily||"sans"),kn(wi(a.density||"standard"))),Ca(r)}))}var tn=0;function qf(){return Se("formatGrid")?.querySelector(".format-card.is-selected")?.dataset.format||"pptx"}function Ib(){let e=Se("exportOverlay");e&&(jf(),tn=Math.max(0,Jt(N)),e.classList.add("is-visible"),e.setAttribute("aria-hidden","false"),Mb(),Ho(),requestAnimationFrame(()=>ya()))}function ya(){Lc(Se("exportPreviewFrame"))}function jf(){let e=Se("exportModalFeedback"),t=Se("exportModalFeedbackText"),r=Se("exportModalSpinner");Se("exportOverlay")?.classList.remove("is-exporting"),e&&(e.hidden=!0,e.classList.remove("is-success","is-error")),t&&(t.textContent=""),r&&(r.hidden=!1),ql(!1)}function ql(e){["exportCancel","exportConfirm","closeExport"].forEach(t=>{let r=Se(t);r&&(r.disabled=e)}),Se("formatGrid")?.querySelectorAll(".format-card").forEach(t=>{t.tabIndex=e?-1:0,t.style.pointerEvents=e?"none":""}),["exportPreviewPrev","exportPreviewNext"].forEach(t=>{let r=Se(t);r&&(r.disabled=e)})}function qo(e,t){let r=Se("exportModalFeedback"),n=Se("exportModalFeedbackText"),a=Se("exportModalSpinner");!r||!n||(r.hidden=!1,r.classList.toggle("is-success",e==="success"),r.classList.toggle("is-error",e==="error"),a&&(a.hidden=e!=="loading"),n.textContent=t)}function jo(){let e=Se("exportOverlay");e&&(e.classList.remove("is-visible"),e.setAttribute("aria-hidden","true"),jf())}function Mb(){let e=Se("formatGrid");if(!e)return;let t=[{id:"pptx",name:"PPTX",desc:"Editable PowerPoint"},{id:"pdf",name:"PDF",desc:"Universal format"},{id:"html",name:"HTML",desc:"Interactive web deck"},{id:"png",name:"PNG",desc:"Image sequence"}];e.innerHTML=t.map((r,n)=>`
                  -
                  ${vf(r.id)}
                  +
                  ${of(r.id)}
                  ${r.name} ${r.desc}
                  - `).join(""),e.querySelectorAll(".format-card").forEach(r=>{let n=()=>{e.querySelectorAll(".format-card").forEach(a=>a.classList.remove("is-selected")),r.classList.add("is-selected"),ts()};r.addEventListener("click",n),r.addEventListener("keydown",a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),n())})})}function o1(e,t){if(!e||!t)return;e.innerHTML="";let r=document.createElement("div");r.className="export-preview__viewport";let n=document.createElement("div");if(n.className="export-preview__scale",t.html)n.appendChild(Xc(t.html));else{let a=document.createElement("div");a.className="export-preview__element-stage",a.innerHTML=un(t),n.append(a)}r.append(n),e.append(r),requestAnimationFrame(()=>{Fa(),requestAnimationFrame(()=>Fa())})}function ts(){let e=Se("exportPreviewInfo"),t=Se("exportPreviewCounter"),r=Se("exportPreviewFrame"),n=L.slides||[],a=sh().toUpperCase(),i=Math.max(1,n.length);if(rn=mt(rn,0,Math.max(0,n.length-1)),e&&(e.textContent=`${a} \xB7 ${n.length} slides`),t&&(t.textContent=`${rn+1} / ${i}`),!r)return;let o=n[rn];if(!o){r.innerHTML=`
                  ${Le(U("slidesEmptyHint"))}
                  `;return}o1(r,o)}async function s1(){if(Zo||!_b())return;let e=sh(),t=ih(e);if(!t){ka(U("exportFormatUnavailable"));return}Zo=!0,Se("exportOverlay")?.classList.add("is-exporting"),Yl(!0),Yo("loading",t.working);let r=Se("exportPreviewFrame"),n=r?.innerHTML||"";try{let{filename:a}=await Kb(e),i=U("exportSavedTo",{path:a});Se("exportOverlay")?.classList.remove("is-exporting"),Yo("success",i),ka(i),await new Promise(o=>setTimeout(o,1600)),Jo()}catch(a){let i=a instanceof Error?a.message:String(a);it().log?.error?.(`PPT Live ${e} export failed`,{error:i}),Se("exportOverlay")?.classList.remove("is-exporting"),Yo("error",`${t.failed} ${i}`),ka(`${t.failed} ${i}`)}finally{r&&n&&(r.innerHTML=n),Yl(!1),Zo=!1}}function l1(){if(Se("exportPptx")?.addEventListener("click",()=>a1()),Se("closeExport")?.addEventListener("click",Jo),Se("exportCancel")?.addEventListener("click",Jo),Se("exportConfirm")?.addEventListener("click",()=>{s1()}),Se("exportOverlay")?.addEventListener("click",e=>{e.target===Se("exportOverlay")&&!Zo&&Jo()}),Se("exportPreviewPrev")?.addEventListener("click",()=>{rn=Math.max(0,rn-1),ts(),requestAnimationFrame(()=>Fa())}),Se("exportPreviewNext")?.addEventListener("click",()=>{let e=(L.slides||[]).length-1;rn=Math.min(e,rn+1),ts(),requestAnimationFrame(()=>Fa())}),typeof ResizeObserver<"u"){let e=Se("exportPreviewFrame");e&&new ResizeObserver(()=>{Se("exportOverlay")?.classList.contains("is-visible")&&Fa()}).observe(e)}}var Of="pptLiveTheme";function ch(e){return e==="dark"||e==="light"?e:window.matchMedia?.("(prefers-color-scheme: dark)")?.matches?"dark":"light"}function c1(){let e=document.documentElement.getAttribute("data-theme-type")||document.documentElement.getAttribute("data-theme");if(e==="dark"||e==="light")return e;let t=it().theme;return t==="dark"||t==="light"?t:ch()}function Uf(e){let t=ch(e),r=document.documentElement;r.setAttribute("data-theme",t),r.setAttribute("data-theme-type",t),r.style.colorScheme=t,Un(),jt()}function d1(){try{localStorage.removeItem(Of)}catch{es.delete(Of)}Uf(c1()),it().onThemeChange?.(e=>{let t=e?.type==="dark"?"dark":"light";Uf(t)})}async function u1(){Ln+=1,Dn=[],mi=!1,Ko=!1,(L.generation?.active||L.generation?.steps?.some(t=>t.status==="running"))&&(tc(U("generationStopped")),yi()),En(!1);let e=it();e.backend?.cancelStaleRuns&&e.backend.cancelStaleRuns().catch(t=>{it().log?.warn?.("Failed to cancel stale PPT Live backend runs",{error:String(t)})})}function dh(){let e=Se("stylePresetSelect");if(!e)return;let t=e.value||L.style?.stylePreset||sn;e.textContent="",Ac(Dr()).forEach(({key:r,displayName:n,description:a})=>{let i=document.createElement("option");i.value=r,i.textContent=n,a&&(i.title=a),e.append(i)}),e.value=t,e.selectedIndex<0&&(e.value=sn),Ea(e)}function $o(){L.generation=Fi(L.generation),Uc(),dh();let e=Se("aiStatusPill");e&&(e.textContent=Fn?U("statusPillBusy"):U("statusPillReady")),jt()}async function f1(){$o();try{await Gy(),await u1(),$o(),Na(L),await Ct(!0)}catch(e){it().log?.error?.("PPT Live init failed",{error:String(e)}),It(U("ready")),$o()}finally{Un()}}Jb();Wi();it().onLocaleChange?.(()=>$o());f1(); + `).join(""),e.querySelectorAll(".format-card").forEach(r=>{let n=()=>{e.querySelectorAll(".format-card").forEach(a=>a.classList.remove("is-selected")),r.classList.add("is-selected"),Ho()};r.addEventListener("click",n),r.addEventListener("keydown",a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),n())})})}function zb(e,t){if(!e||!t)return;e.innerHTML="";let r=document.createElement("div");r.className="export-preview__viewport";let n=document.createElement("div");if(n.className="export-preview__scale",t.html)n.appendChild(Rc(t.html));else{let a=document.createElement("div");a.className="export-preview__element-stage",a.innerHTML=dn(t),n.append(a)}r.append(n),e.append(r),requestAnimationFrame(()=>{ya(),requestAnimationFrame(()=>ya())})}function Ho(){let e=Se("exportPreviewInfo"),t=Se("exportPreviewCounter"),r=Se("exportPreviewFrame"),n=N.slides||[],a=qf().toUpperCase(),i=Math.max(1,n.length);if(tn=vt(tn,0,Math.max(0,n.length-1)),e&&(e.textContent=`${a} \xB7 ${n.length} slides`),t&&(t.textContent=`${tn+1} / ${i}`),!r)return;let o=n[tn];if(!o){r.innerHTML=`
                  ${Be(V("slidesEmptyHint"))}
                  `;return}zb(r,o)}async function Ob(){if(Go||!Cb())return;let e=qf(),t=Wf(e);if(!t){ma(V("exportFormatUnavailable"));return}Go=!0,Se("exportOverlay")?.classList.add("is-exporting"),ql(!0),qo("loading",t.working);let r=Se("exportPreviewFrame"),n=r?.innerHTML||"";try{let{filename:a}=await kb(e),i=V("exportSavedTo",{path:a});Se("exportOverlay")?.classList.remove("is-exporting"),qo("success",i),ma(i),await new Promise(o=>setTimeout(o,1600)),jo()}catch(a){let i=a instanceof Error?a.message:String(a);ot().log?.error?.(`PPT Live ${e} export failed`,{error:i}),Se("exportOverlay")?.classList.remove("is-exporting"),qo("error",`${t.failed} ${i}`),ma(`${t.failed} ${i}`)}finally{r&&n&&(r.innerHTML=n),ql(!1),Go=!1}}function Ub(){if(Se("exportPptx")?.addEventListener("click",()=>Ib()),Se("closeExport")?.addEventListener("click",jo),Se("exportCancel")?.addEventListener("click",jo),Se("exportConfirm")?.addEventListener("click",()=>{Ob()}),Se("exportOverlay")?.addEventListener("click",e=>{e.target===Se("exportOverlay")&&!Go&&jo()}),Se("exportPreviewPrev")?.addEventListener("click",()=>{tn=Math.max(0,tn-1),Ho(),requestAnimationFrame(()=>ya())}),Se("exportPreviewNext")?.addEventListener("click",()=>{let e=(N.slides||[]).length-1;tn=Math.min(e,tn+1),Ho(),requestAnimationFrame(()=>ya())}),typeof ResizeObserver<"u"){let e=Se("exportPreviewFrame");e&&new ResizeObserver(()=>{Se("exportOverlay")?.classList.contains("is-visible")&&ya()}).observe(e)}}var wf="pptLiveTheme";function Vf(e){return e==="dark"||e==="light"?e:window.matchMedia?.("(prefers-color-scheme: dark)")?.matches?"dark":"light"}function Wb(){let e=document.documentElement.getAttribute("data-theme-type")||document.documentElement.getAttribute("data-theme");if(e==="dark"||e==="light")return e;let t=ot().theme;return t==="dark"||t==="light"?t:Vf()}function xf(e){let t=Vf(e),r=document.documentElement;r.setAttribute("data-theme",t),r.setAttribute("data-theme-type",t),r.style.colorScheme=t,Rn(),Yt()}function Gb(){try{localStorage.removeItem(wf)}catch{Xo.delete(wf)}xf(Wb()),ot().onThemeChange?.(e=>{let t=e?.type==="dark"?"dark":"light";xf(t)})}async function qb(){Xn+=1,Fn=[],hi=!1,Wo=!1,(N.generation?.active||N.generation?.steps?.some(t=>t.status==="running"))&&(Bf(V("generationStopped")),Ko()),ba(!1);let e=ot();e.backend?.cancelStaleRuns&&e.backend.cancelStaleRuns().catch(t=>{ot().log?.warn?.("Failed to cancel stale PPT Live backend runs",{error:String(t)})})}function Xf(){let e=Se("stylePresetSelect");if(!e)return;let t=e.value||N.style?.stylePreset||an;e.textContent="",rc(Nr()).forEach(({key:r,displayName:n,description:a})=>{let i=document.createElement("option");i.value=r,i.textContent=n,a&&(i.title=a),e.append(i)}),e.value=t,e.selectedIndex<0&&(e.value=an),Ca(e)}function Vo(){N.generation=Sa(N.generation),Pc(),Xf();let e=Se("aiStatusPill");e&&(e.textContent=Pn?V("statusPillBusy"):V("statusPillReady")),Yt()}async function jb(){Vo();try{await gy(),await qb(),Vo(),Ta(N),await Pt(!0)}catch(e){ot().log?.error?.("PPT Live init failed",{error:String(e)}),Zt(V("ready")),Vo()}finally{Rn()}}Db();Ii();ot().onLocaleChange?.(()=>Vo());jb(); diff --git a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/index.html b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/index.html index 54c109f42a..b44f4cd2ec 100644 --- a/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/index.html +++ b/src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/index.html @@ -165,6 +165,8 @@

                  Create or edit by prompt