feat(launcher): preset chips + simplified buttons for wallpaper config
Replaces free-text-only inputs with quick-select preset chips (1 primary, 4 client external/projector) that populate the URL field on click. Consolidates Save/Apply/Save&Apply/Restore into a single Save & Apply primary button plus an icon-only Restore button. All status messages merged into one shared state variable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import { events_func } from '$lib/ae_events/ae_events_functions';
|
|||||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
import { events_loc } from '$lib/stores/ae_events_stores';
|
||||||
import * as native from '$lib/electron/electron_relay';
|
import * as native from '$lib/electron/electron_relay';
|
||||||
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||||
import { FlaskConical, Image, Monitor, RotateCcw, Save, Zap } from '@lucide/svelte';
|
import { FlaskConical, Image, RotateCcw, Save, Zap } from '@lucide/svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
on_expand?: () => void;
|
on_expand?: () => void;
|
||||||
@@ -19,11 +19,20 @@ type NativeDeviceLike = {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PRIMARY_PRESETS = [
|
||||||
|
{ label: 'OSIT Default', value: 'https://static.oneskyit.com/images/site_background.webp' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const EXTERNAL_PRESETS = [
|
||||||
|
{ label: 'CMSC', value: 'https://static.oneskyit.com/c/CMSC/images/CMSC_AE_Launcher_bg_secondary.png' },
|
||||||
|
{ label: 'LCI', value: 'https://static.oneskyit.com/c/LCI/images/LCI_AE_Launcher_bg_secondary.png' },
|
||||||
|
{ label: 'LCI Alt', value: 'https://static.oneskyit.com/c/LCI/images/LCI_AE_Launcher_bg_secondary_bak.jpg' },
|
||||||
|
{ label: 'BGH', value: 'https://static.oneskyit.com/c/BGH/images/BGH_AE_Launcher_bg_secondary.png' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
let url_input = $state('');
|
let url_input = $state('');
|
||||||
let url_external_input = $state('');
|
let url_external_input = $state('');
|
||||||
let save_status = $state('');
|
let status = $state('');
|
||||||
let apply_status = $state('');
|
|
||||||
let restore_status = $state('');
|
|
||||||
let last_device_id: string | null = null;
|
let last_device_id: string | null = null;
|
||||||
|
|
||||||
let linux_test_popup_open = $state(false);
|
let linux_test_popup_open = $state(false);
|
||||||
@@ -73,19 +82,14 @@ $effect(() => {
|
|||||||
sync_from_device();
|
sync_from_device();
|
||||||
});
|
});
|
||||||
|
|
||||||
function set_save_status(msg: string) {
|
function set_status(msg: string, duration = 4000) {
|
||||||
save_status = msg;
|
status = msg;
|
||||||
setTimeout(() => { if (save_status === msg) save_status = ''; }, 3000);
|
if (msg) setTimeout(() => { if (status === msg) status = ''; }, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_apply_status(msg: string) {
|
async function handle_save(): Promise<boolean> {
|
||||||
apply_status = msg;
|
|
||||||
setTimeout(() => { if (apply_status === msg) apply_status = ''; }, 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handle_save() {
|
|
||||||
const device_id = get_device_id();
|
const device_id = get_device_id();
|
||||||
if (!device_id) { set_save_status('No device loaded'); return; }
|
if (!device_id) return false;
|
||||||
|
|
||||||
const native_device = get_native_device();
|
const native_device = get_native_device();
|
||||||
const other_json_obj = parse_other_json(native_device?.other_json);
|
const other_json_obj = parse_other_json(native_device?.other_json);
|
||||||
@@ -110,7 +114,6 @@ async function handle_save() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const other_json = JSON.stringify(other_json_obj);
|
const other_json = JSON.stringify(other_json_obj);
|
||||||
set_save_status('Saving...');
|
|
||||||
|
|
||||||
const result = await events_func.update_ae_obj__event_device({
|
const result = await events_func.update_ae_obj__event_device({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
@@ -119,22 +122,21 @@ async function handle_save() {
|
|||||||
log_lvl: 0
|
log_lvl: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) { set_save_status('Save failed'); return; }
|
if (!result) return false;
|
||||||
|
|
||||||
const store_loc = $ae_loc as { native_device?: NativeDeviceLike };
|
const store_loc = $ae_loc as { native_device?: NativeDeviceLike };
|
||||||
store_loc.native_device = { ...get_native_device(), ...result, other_json };
|
store_loc.native_device = { ...get_native_device(), ...result, other_json };
|
||||||
set_save_status('Saved');
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_apply() {
|
async function handle_apply(): Promise<{ success: boolean; linux_test?: boolean }> {
|
||||||
const url = url_input.trim();
|
const url = url_input.trim();
|
||||||
const url_ext = url_external_input.trim();
|
const url_ext = url_external_input.trim();
|
||||||
if (!url && !url_ext) { set_apply_status('Enter a URL first'); return; }
|
if (!url && !url_ext) return { success: false };
|
||||||
|
|
||||||
// If only external is set, target only that display so the built-in stays unchanged.
|
// If only external is set, target only that display so the built-in stays unchanged.
|
||||||
const display = !url && url_ext ? 'external' : 'all';
|
const display = !url && url_ext ? 'external' : 'all';
|
||||||
|
|
||||||
set_apply_status('Downloading & applying...');
|
|
||||||
const result = await native.set_wallpaper({
|
const result = await native.set_wallpaper({
|
||||||
url: url || undefined,
|
url: url || undefined,
|
||||||
url_external: url_ext || undefined,
|
url_external: url_ext || undefined,
|
||||||
@@ -146,34 +148,44 @@ async function handle_apply() {
|
|||||||
if (result?.success && (result as any).linux_test_mode) {
|
if (result?.success && (result as any).linux_test_mode) {
|
||||||
linux_test_popup_data = result as Record<string, unknown>;
|
linux_test_popup_data = result as Record<string, unknown>;
|
||||||
linux_test_popup_open = true;
|
linux_test_popup_open = true;
|
||||||
set_apply_status('Linux dev mode — see popup');
|
return { success: true, linux_test: true };
|
||||||
} else if (result?.success) {
|
} else if (result?.success) {
|
||||||
$events_loc.launcher.wallpaper_applied_url = url || null;
|
$events_loc.launcher.wallpaper_applied_url = url || null;
|
||||||
$events_loc.launcher.wallpaper_applied_url_external = url_ext || null;
|
$events_loc.launcher.wallpaper_applied_url_external = url_ext || null;
|
||||||
set_apply_status('Applied ✓');
|
return { success: true };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handle_save_and_apply() {
|
||||||
|
const device_id = get_device_id();
|
||||||
|
if (!device_id) { set_status('No device loaded'); return; }
|
||||||
|
|
||||||
|
set_status('Saving...');
|
||||||
|
const saved = await handle_save();
|
||||||
|
if (!saved) { set_status('Save failed'); return; }
|
||||||
|
|
||||||
|
set_status('Applying...');
|
||||||
|
const { success, linux_test } = await handle_apply();
|
||||||
|
if (!success) {
|
||||||
|
set_status('Saved — apply failed');
|
||||||
|
} else if (linux_test) {
|
||||||
|
set_status('Linux dev mode — see popup');
|
||||||
} else {
|
} else {
|
||||||
set_apply_status(`Error: ${(result as any)?.error ?? 'Unknown error'}`);
|
set_status('Saved & Applied ✓');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_restore_default() {
|
async function handle_restore_default() {
|
||||||
restore_status = 'Restoring...';
|
set_status('Restoring...');
|
||||||
const result = await native.restore_macos_default_wallpaper('all');
|
const result = await native.restore_macos_default_wallpaper('all');
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
// Clear tracked applied URL so the next config URL re-applies correctly.
|
// Clear tracked applied URL so the next config URL re-applies correctly.
|
||||||
$events_loc.launcher.wallpaper_applied_url = null;
|
$events_loc.launcher.wallpaper_applied_url = null;
|
||||||
$events_loc.launcher.wallpaper_applied_url_external = null;
|
$events_loc.launcher.wallpaper_applied_url_external = null;
|
||||||
restore_status = 'Restored ✓';
|
set_status('Restored ✓');
|
||||||
} else {
|
} else {
|
||||||
restore_status = `Error: ${(result as any)?.error ?? 'Unknown error'}`;
|
set_status(`Restore failed: ${(result as { error?: string })?.error ?? 'Unknown'}`);
|
||||||
}
|
|
||||||
setTimeout(() => { if (restore_status.startsWith('Restored') || restore_status.startsWith('Error')) restore_status = ''; }, 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handle_save_and_apply() {
|
|
||||||
await handle_save();
|
|
||||||
if (save_status !== 'Save failed' && save_status !== 'No device loaded') {
|
|
||||||
await handle_apply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +212,7 @@ const section_description = $derived(
|
|||||||
|
|
||||||
{#if $ae_loc.edit_mode && !$ae_loc.is_native}
|
{#if $ae_loc.edit_mode && !$ae_loc.is_native}
|
||||||
<div
|
<div
|
||||||
class="bg-warning-500/10 border-warning-500/30 mb-1 flex items-center gap-2 rounded-lg border px-2 py-1.5">
|
class="bg-warning-500/10 border-warning-500/30 mb-2 flex items-center gap-2 rounded-lg border px-2 py-1.5">
|
||||||
<FlaskConical size="0.75em" class="text-warning-500" />
|
<FlaskConical size="0.75em" class="text-warning-500" />
|
||||||
<span class="text-warning-500 text-[9px] font-bold tracking-wide uppercase">
|
<span class="text-warning-500 text-[9px] font-bold tracking-wide uppercase">
|
||||||
Dev Preview — Apply requires Electron; Save works from any device
|
Dev Preview — Apply requires Electron; Save works from any device
|
||||||
@@ -208,131 +220,99 @@ const section_description = $derived(
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex flex-col gap-3">
|
{#if !get_device_id()}
|
||||||
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
<div
|
||||||
Desktop Background Images
|
class="bg-warning-500/10 border-warning-500/20 mb-2 rounded-lg border px-2 py-1.5 text-[9px] opacity-80">
|
||||||
</p>
|
No device record loaded — Save requires a native device config.
|
||||||
<p class="ml-1 text-[10px] leading-snug opacity-60">
|
</div>
|
||||||
Paste an HTTPS image URL. Save writes it to this device's config so all
|
{/if}
|
||||||
devices auto-apply on their next heartbeat. Apply sets it immediately on
|
|
||||||
this machine.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{#if !get_device_id()}
|
<div class="flex flex-col gap-2.5">
|
||||||
<div
|
|
||||||
class="bg-warning-500/10 border-warning-500/20 rounded-lg border px-2 py-2 text-[9px] opacity-80">
|
|
||||||
No device record loaded — Save requires a native device config.
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- All Displays (primary URL) -->
|
<!-- Primary display -->
|
||||||
<section class="border-surface-500/10 rounded-lg border p-2">
|
<div class="flex flex-col gap-1">
|
||||||
<div class="mb-1.5 flex items-center gap-1.5">
|
<p class="text-[9px] font-bold uppercase opacity-50">Primary Display</p>
|
||||||
<Monitor size="0.85em" class="opacity-50" />
|
<div class="flex flex-wrap gap-1">
|
||||||
<p class="text-[10px] font-bold uppercase tracking-wide">
|
{#each PRIMARY_PRESETS as preset (preset.value)}
|
||||||
All Displays
|
<button
|
||||||
</p>
|
type="button"
|
||||||
{#if is_applied}
|
onclick={() => (url_input = preset.value)}
|
||||||
<span
|
title={preset.value}
|
||||||
class="text-success-500 ml-auto text-[9px] font-bold">Applied ✓</span>
|
class="btn btn-xs h-6 px-2 text-[9px]"
|
||||||
{:else if $events_loc.launcher.wallpaper_applied_url}
|
class:preset-filled-primary={url_input === preset.value}
|
||||||
<span class="text-warning-500 ml-auto text-[9px] italic"
|
class:preset-tonal-surface={url_input !== preset.value}>
|
||||||
>Pending apply</span>
|
{preset.label}
|
||||||
{/if}
|
</button>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
bind:value={url_input}
|
bind:value={url_input}
|
||||||
placeholder="https://example.com/wallpaper.jpg"
|
placeholder="https://… or select a preset above"
|
||||||
class="input input-sm preset-tonal-surface mb-1.5 h-8 w-full text-[10px]" />
|
class="input input-sm preset-tonal-surface h-7 w-full text-[10px]" />
|
||||||
{#if $events_loc.launcher.wallpaper_applied_url && $events_loc.launcher.wallpaper_applied_url !== url_input.trim()}
|
{#if is_applied}
|
||||||
<p class="text-[9px] italic opacity-40 truncate">
|
<p class="text-success-500 pl-0.5 text-[9px]">Applied ✓</p>
|
||||||
Applied: {$events_loc.launcher.wallpaper_applied_url}
|
|
||||||
</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<!-- External Display (optional override) -->
|
<!-- External / projector display -->
|
||||||
<section class="border-surface-500/10 rounded-lg border p-2">
|
<div class="flex flex-col gap-1">
|
||||||
<div class="mb-1.5 flex items-center gap-1.5">
|
<p class="text-[9px] font-bold uppercase opacity-50">
|
||||||
<Monitor size="0.85em" class="opacity-40" />
|
External / Projector
|
||||||
<p class="text-[10px] font-bold uppercase tracking-wide">
|
<span class="font-normal normal-case opacity-75">(optional)</span>
|
||||||
External / Projector
|
|
||||||
<span class="ml-1 font-normal normal-case opacity-50">(optional)</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p class="mb-1.5 text-[9px] leading-snug opacity-50">
|
|
||||||
Leave blank to use the same image on all displays.
|
|
||||||
</p>
|
</p>
|
||||||
|
<div class="flex flex-wrap gap-1">
|
||||||
|
{#each EXTERNAL_PRESETS as preset (preset.value)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => (url_external_input = preset.value)}
|
||||||
|
title={preset.value}
|
||||||
|
class="btn btn-xs h-6 px-2 text-[9px]"
|
||||||
|
class:preset-filled-primary={url_external_input === preset.value}
|
||||||
|
class:preset-tonal-surface={url_external_input !== preset.value}>
|
||||||
|
{preset.label}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
bind:value={url_external_input}
|
bind:value={url_external_input}
|
||||||
placeholder="https://example.com/projector-bg.jpg"
|
placeholder="Blank = use primary on all displays"
|
||||||
class="input input-sm preset-tonal-surface h-8 w-full text-[10px]" />
|
class="input input-sm preset-tonal-surface h-7 w-full text-[10px]" />
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={handle_save}
|
|
||||||
disabled={!get_device_id()}
|
|
||||||
class="btn btn-sm preset-tonal-surface h-8 text-[10px]">
|
|
||||||
<Save size="0.8em" class="mr-1" /> Save Config
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={handle_apply}
|
|
||||||
disabled={!url_input.trim() && !url_external_input.trim()}
|
|
||||||
class="btn btn-sm preset-tonal-primary h-8 text-[10px]">
|
|
||||||
<Zap size="0.8em" class="mr-1" /> Apply Now
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={handle_save_and_apply}
|
|
||||||
disabled={!get_device_id() || (!url_input.trim() && !url_external_input.trim())}
|
|
||||||
class="btn btn-sm preset-filled-primary h-8 w-full text-[10px] font-bold">
|
|
||||||
<Save size="0.8em" class="mr-1" />
|
|
||||||
<Zap size="0.8em" class="mr-1" />
|
|
||||||
Save & Apply
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Restore macOS default — safety valve for end-of-show cleanup -->
|
<!-- Actions: single Save & Apply + icon-only Restore -->
|
||||||
{#if $ae_loc.is_native || $ae_loc.edit_mode}
|
<div class="flex gap-2 pt-0.5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={handle_restore_default}
|
onclick={handle_save_and_apply}
|
||||||
class="btn btn-sm preset-tonal-surface h-8 w-full text-[10px] opacity-60 hover:opacity-100">
|
disabled={!get_device_id() || (!url_input.trim() && !url_external_input.trim())}
|
||||||
<RotateCcw size="0.8em" class="mr-1" /> Restore macOS Default
|
class="btn btn-sm preset-filled-primary h-8 flex-1 text-[10px] font-bold">
|
||||||
|
<Save size="0.8em" class="mr-1" />
|
||||||
|
<Zap size="0.8em" class="mr-0.5" />
|
||||||
|
Save & Apply
|
||||||
</button>
|
</button>
|
||||||
|
{#if $ae_loc.is_native || $ae_loc.edit_mode}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={handle_restore_default}
|
||||||
|
title="Restore macOS default wallpaper"
|
||||||
|
class="btn btn-sm preset-tonal-surface h-8 px-2.5 opacity-60 hover:opacity-100">
|
||||||
|
<RotateCcw size="0.85em" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if status}
|
||||||
|
<div
|
||||||
|
class="text-center text-[9px] italic"
|
||||||
|
class:text-success-500={status.includes('✓')}
|
||||||
|
class:text-primary-500={status.includes('Saving') || status.includes('Applying') || status.includes('dev')}
|
||||||
|
class:text-error-500={status.includes('failed') || status.includes('No device') || status.includes('Restore failed')}>
|
||||||
|
{status}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if save_status}
|
|
||||||
<div
|
|
||||||
class="text-primary-500 text-center text-[9px] italic"
|
|
||||||
class:text-error-500={save_status.includes('failed') || save_status.includes('No device')}>
|
|
||||||
{save_status}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if apply_status}
|
|
||||||
<div
|
|
||||||
class="text-[9px] italic text-center"
|
|
||||||
class:text-success-500={apply_status.includes('✓')}
|
|
||||||
class:text-primary-500={apply_status.includes('Downloading')}
|
|
||||||
class:text-error-500={apply_status.includes('Error')}>
|
|
||||||
{apply_status}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if restore_status}
|
|
||||||
<div
|
|
||||||
class="text-[9px] italic text-center"
|
|
||||||
class:text-success-500={restore_status.includes('✓')}
|
|
||||||
class:text-primary-500={restore_status === 'Restoring...'}
|
|
||||||
class:text-error-500={restore_status.includes('Error')}>
|
|
||||||
{restore_status}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</Launcher_Cfg_Section>
|
</Launcher_Cfg_Section>
|
||||||
|
|
||||||
@@ -369,7 +349,7 @@ const section_description = $derived(
|
|||||||
<div class="flex flex-col gap-3 overflow-y-auto p-4 font-mono text-xs">
|
<div class="flex flex-col gap-3 overflow-y-auto p-4 font-mono text-xs">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[9px] font-bold uppercase opacity-50">Display Target</span>
|
<span class="text-[9px] font-bold uppercase opacity-50">Display Target</span>
|
||||||
<div class="rounded bg-surface-500/10 px-3 py-2">
|
<div class="bg-surface-500/10 rounded px-3 py-2">
|
||||||
{linux_test_popup_data.display ?? 'all'}
|
{linux_test_popup_data.display ?? 'all'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -377,7 +357,7 @@ const section_description = $derived(
|
|||||||
{#if linux_test_popup_data.url}
|
{#if linux_test_popup_data.url}
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[9px] font-bold uppercase opacity-50">Primary URL</span>
|
<span class="text-[9px] font-bold uppercase opacity-50">Primary URL</span>
|
||||||
<div class="text-primary-500 rounded bg-surface-500/10 px-3 py-2 break-all">
|
<div class="text-primary-500 bg-surface-500/10 rounded px-3 py-2 break-all">
|
||||||
{linux_test_popup_data.url}
|
{linux_test_popup_data.url}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -386,7 +366,7 @@ const section_description = $derived(
|
|||||||
{#if linux_test_popup_data.url_external}
|
{#if linux_test_popup_data.url_external}
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[9px] font-bold uppercase opacity-50">External / Projector URL</span>
|
<span class="text-[9px] font-bold uppercase opacity-50">External / Projector URL</span>
|
||||||
<div class="text-primary-500 rounded bg-surface-500/10 px-3 py-2 break-all">
|
<div class="text-primary-500 bg-surface-500/10 rounded px-3 py-2 break-all">
|
||||||
{linux_test_popup_data.url_external}
|
{linux_test_popup_data.url_external}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -394,7 +374,7 @@ const section_description = $derived(
|
|||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[9px] font-bold uppercase opacity-50">Would Have Run</span>
|
<span class="text-[9px] font-bold uppercase opacity-50">Would Have Run</span>
|
||||||
<div class="rounded bg-surface-500/10 px-3 py-2 leading-relaxed whitespace-pre-wrap text-success-600 dark:text-success-400">
|
<div class="bg-surface-500/10 text-success-600 dark:text-success-400 rounded px-3 py-2 leading-relaxed whitespace-pre-wrap">
|
||||||
{#each (linux_test_popup_data.would_run as string[]) as cmd}
|
{#each (linux_test_popup_data.would_run as string[]) as cmd}
|
||||||
<div class="mb-1">{cmd}</div>
|
<div class="mb-1">{cmd}</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user