Files
OSIT-AE-App-Svelte/src/lib/app_components/e_app_url_builder.svelte
Scott Idem a3de95629a feat(sys-menu): add URL param builder to app config panel
New e_app_url_builder.svelte component lets admins construct and copy
shareable URLs with any combination of core global params (iframe, theme,
theme_mode, key). Outputs full URL by default; toggleable to params-only
string for pasting onto existing links. Integrated into e_app_cfg Utilities
section (visible in edit mode).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:27:19 -04:00

169 lines
6.8 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
/**
* e_app_url_builder.svelte
* Core URL Param Builder — lets admins construct and copy shareable URLs
* with any combination of the core global URL params applied.
*
* Core params:
* ?iframe=true|false — hide sys/debug menus (kiosk mode)
* ?theme=<name> — set theme name on load (stripped from URL after apply)
* ?theme_mode=light|dark — set light/dark mode on load (stripped from URL after apply)
* ?key=<access_key> — site access key
*/
import { page } from '$app/stores';
import { Copy, Check, Link } from '@lucide/svelte';
// --- Per-param: include this param in the output URL?
let use_iframe = $state(false);
let use_theme = $state(false);
let use_theme_mode = $state(false);
let use_key = $state(false);
// --- Param values
let val_iframe = $state<'true' | 'false'>('true');
let val_theme = $state('nouveau');
let val_theme_mode = $state<'light' | 'dark'>('dark');
let val_key = $state('');
// Build the output URL reactively
let built_url = $derived.by(() => {
const base = $page.url;
const u = new URL(base.pathname + base.search + base.hash, base.origin);
// Remove all core params first so we start clean each time
u.searchParams.delete('iframe');
u.searchParams.delete('theme');
u.searchParams.delete('theme_mode');
u.searchParams.delete('key');
if (use_iframe) u.searchParams.set('iframe', val_iframe);
if (use_theme) u.searchParams.set('theme', val_theme);
if (use_theme_mode) u.searchParams.set('theme_mode', val_theme_mode);
if (use_key && val_key.trim()) u.searchParams.set('key', val_key.trim());
return u.toString();
});
// Output mode: full URL (default) or params-only string
let params_only = $state(false);
let output = $derived.by(() => {
if (!params_only) return built_url;
const u = new URL(built_url);
return u.search || '(no params set)';
});
let copied = $state(false);
function copy_url() {
navigator.clipboard.writeText(output).then(() => {
copied = true;
setTimeout(() => copied = false, 2000);
});
}
const theme_options = [
{ value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' },
{ value: 'crimson', label: 'Crimson' },
{ value: 'hamlindigo', label: 'Hamlindigo' },
{ value: 'modern', label: 'Modern' },
{ value: 'nouveau', label: 'Nouveau' },
{ value: 'rocket', label: 'Rocket' },
{ value: 'terminus', label: 'Terminus' },
{ value: 'vintage', label: 'Vintage' },
{ value: 'wintry', label: 'Wintry' },
{ value: 'AE_OSIT_default', label: 'OSIT' },
{ value: 'AE_Firefly', label: 'Firefly ✦' },
{ value: 'AE_Firefly_SteelBlue', label: 'Firefly SteelBlue ✦' },
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' },
];
</script>
<section class="space-y-3">
<h2 class="text-xs font-semibold uppercase tracking-widest text-surface-500 flex items-center gap-1">
<Link size="0.9em" /> URL Param Builder
</h2>
<!-- Param toggles -->
<div class="space-y-2">
<!-- iframe -->
<div class="flex items-center gap-2">
<input id="ubld_iframe" type="checkbox" class="checkbox checkbox-sm" bind:checked={use_iframe} />
<label for="ubld_iframe" class="w-20 text-xs font-mono">iframe</label>
<select bind:value={val_iframe} disabled={!use_iframe} class="select select-sm text-xs flex-1">
<option value="true">true</option>
<option value="false">false</option>
</select>
</div>
<!-- theme -->
<div class="flex items-center gap-2">
<input id="ubld_theme" type="checkbox" class="checkbox checkbox-sm" bind:checked={use_theme} />
<label for="ubld_theme" class="w-20 text-xs font-mono">theme</label>
<select bind:value={val_theme} disabled={!use_theme} class="select select-sm text-xs flex-1">
{#each theme_options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
</div>
<!-- theme_mode -->
<div class="flex items-center gap-2">
<input id="ubld_theme_mode" type="checkbox" class="checkbox checkbox-sm" bind:checked={use_theme_mode} />
<label for="ubld_theme_mode" class="w-20 text-xs font-mono">theme_mode</label>
<select bind:value={val_theme_mode} disabled={!use_theme_mode} class="select select-sm text-xs flex-1">
<option value="light">light</option>
<option value="dark">dark</option>
</select>
</div>
<!-- key -->
<div class="flex items-center gap-2">
<input id="ubld_key" type="checkbox" class="checkbox checkbox-sm" bind:checked={use_key} />
<label for="ubld_key" class="w-20 text-xs font-mono">key</label>
<input
type="text"
bind:value={val_key}
disabled={!use_key}
placeholder="access key"
class="input input-sm text-xs font-mono flex-1"
/>
</div>
</div>
<!-- params_only toggle + output -->
<div class="flex items-center gap-2">
<input id="ubld_params_only" type="checkbox" class="checkbox checkbox-sm" bind:checked={params_only} />
<label for="ubld_params_only" class="text-xs text-surface-500">Params only</label>
</div>
<!-- Output URL -->
<div class="flex gap-1 items-stretch">
<input
type="text"
readonly
value={output}
class="input input-sm text-xs font-mono flex-1 bg-surface-50/50 dark:bg-surface-700/50 cursor-text"
onclick={(e) => (e.target as HTMLInputElement).select()}
title="Click to select all"
/>
<button
class="btn btn-sm {copied ? 'preset-filled-success' : 'preset-tonal-primary'} shrink-0 transition-all"
onclick={copy_url}
title="Copy URL to clipboard"
>
{#if copied}
<Check size="1em" />
{:else}
<Copy size="1em" />
{/if}
</button>
</div>
</section>