feat(launcher): add display mode toggle; fix silent display layout failures
- Add Extend/Mirror toggle to Native OS config section (always visible,
no Technical Mode required). Default: Extend. State updates on success.
- Replace .catch(() => {}) swallowing with console.warn logging on both
set_display_layout call sites so failures appear in the Electron console
- Remove old edit-mode-only Extend button (replaced by new toggle)
- Update PROJECT doc: displayplacer install note, binary lookup order, GitHub link
- Clean up TODO__Agents.md: resolve stale items, add new low-priority Electron items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||
import {
|
||||
Code,
|
||||
Columns2,
|
||||
Copy,
|
||||
FlaskConical,
|
||||
FolderOpen,
|
||||
Image,
|
||||
@@ -27,6 +28,7 @@ let test_cmd_result = $state('');
|
||||
let remote_app: 'powerpoint' | 'keynote' = $state('powerpoint');
|
||||
let remote_status = $state('');
|
||||
let system_status = $state('');
|
||||
let display_mode = $state<'extend' | 'mirror'>('extend');
|
||||
|
||||
async function handle_remote_control(
|
||||
action: 'next' | 'prev' | 'start' | 'stop'
|
||||
@@ -55,6 +57,18 @@ async function handle_system_action(promise: Promise<any>, label: string) {
|
||||
setTimeout(() => (system_status = ''), 3000);
|
||||
}
|
||||
|
||||
async function handle_display_mode(mode: 'extend' | 'mirror') {
|
||||
system_status = `Setting display: ${mode}...`;
|
||||
const res = await native.set_display_layout({ mode });
|
||||
if (res?.success) {
|
||||
display_mode = mode;
|
||||
system_status = `Display: ${mode}`;
|
||||
} else {
|
||||
system_status = `Display error: ${res?.error || 'No external display?'}`;
|
||||
}
|
||||
setTimeout(() => (system_status = ''), 4000);
|
||||
}
|
||||
|
||||
// Modal state for dangerous actions
|
||||
let show_power_confirm = $state<{ action: string; label: string } | null>(null);
|
||||
</script>
|
||||
@@ -124,6 +138,33 @@ let show_power_confirm = $state<{ action: string; label: string } | null>(null);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display mode toggle: Extend = independent screens (default); Mirror = same content on both -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">Display Mode</p>
|
||||
<div class="grid grid-cols-2 gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_display_mode('extend')}
|
||||
class="btn btn-xs justify-start border-2"
|
||||
class:border-primary-500={display_mode === 'extend'}
|
||||
class:preset-tonal-primary={display_mode === 'extend'}
|
||||
class:border-surface-400={display_mode !== 'extend'}
|
||||
class:preset-tonal-surface={display_mode !== 'extend'}>
|
||||
<Columns2 size="0.85em" class="mr-1 shrink-0" /> Extend
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_display_mode('mirror')}
|
||||
class="btn btn-xs justify-start border-2"
|
||||
class:border-warning-500={display_mode === 'mirror'}
|
||||
class:preset-tonal-warning={display_mode === 'mirror'}
|
||||
class:border-surface-400={display_mode !== 'mirror'}
|
||||
class:preset-tonal-surface={display_mode !== 'mirror'}>
|
||||
<Copy size="0.85em" class="mr-1 shrink-0" /> Mirror
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Presentation Remote Control (Common) -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-row items-center justify-between px-1">
|
||||
@@ -186,19 +227,6 @@ let show_power_confirm = $state<{ action: string; label: string } | null>(null);
|
||||
System Actions
|
||||
</p>
|
||||
<div class="grid grid-cols-1 gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() =>
|
||||
handle_system_action(
|
||||
native.set_display_layout({
|
||||
mode: 'extend'
|
||||
}),
|
||||
'Extend Display'
|
||||
)}
|
||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start">
|
||||
<Columns2 size="0.85em" class="mr-1 shrink-0" /> Extend
|
||||
Mode
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() =>
|
||||
|
||||
@@ -219,9 +219,8 @@ async function handle_open_file() {
|
||||
// URL presentations may still want to set display mode (e.g. Google Slides → extend)
|
||||
if (profile.display_mode !== 'none') {
|
||||
open_file_status_message = `Setting display (${profile.display_mode})...`;
|
||||
await native.set_display_layout({ mode: profile.display_mode }).catch(() => {
|
||||
/* No external display or displayplacer unconfigured — continue */
|
||||
});
|
||||
const disp_res = await native.set_display_layout({ mode: profile.display_mode }).catch((e: any) => ({ success: false, error: String(e) }));
|
||||
if (!disp_res?.success) console.warn('[Launcher] set_display_layout:', disp_res?.error ?? 'no external display');
|
||||
}
|
||||
|
||||
open_file_status_message = `Opening ${event_file_obj.title || 'URL'}...`;
|
||||
@@ -357,9 +356,8 @@ async function handle_open_file() {
|
||||
// --- Step 3: Set display layout (skip silently on failure / no external display) ---
|
||||
if (profile.display_mode !== 'none') {
|
||||
open_file_status_message = `Setting display (${profile.display_mode})...`;
|
||||
await native.set_display_layout({ mode: profile.display_mode }).catch(() => {
|
||||
/* No external display or displayplacer unavailable — continue */
|
||||
});
|
||||
const disp_res = await native.set_display_layout({ mode: profile.display_mode }).catch((e: any) => ({ success: false, error: String(e) }));
|
||||
if (!disp_res?.success) console.warn('[Launcher] set_display_layout:', disp_res?.error ?? 'no external display');
|
||||
}
|
||||
|
||||
// --- Step 4: Open the file ---
|
||||
|
||||
Reference in New Issue
Block a user