Slight UI refactor

This commit is contained in:
2025-04-22 19:55:19 +02:00
parent cd40d8fe47
commit e5dcbe223d
5 changed files with 581 additions and 494 deletions

View File

@@ -0,0 +1,129 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { profiles, activeProfileId } from '$lib/stores/mudStore';
import { ModalHelper } from '$lib/utils/ModalHelper';
import type { MudProfile } from '$lib/profiles/ProfileManager';
const dispatch = createEventDispatcher<{
saveProfile: { profile: MudProfile },
deleteProfile: { profileId: string },
editProfile: { profile: MudProfile }
}>();
// Create a new profile
function createProfile() {
console.log('New Profile button clicked - using full profile editor');
try {
ModalHelper.showProfileEditor(
(profile) => {
console.log('Profile saved from profile editor:', profile);
dispatch('saveProfile', { profile });
},
() => {
console.log('Profile creation cancelled');
}
);
} catch (error) {
console.error('Error showing profile editor:', error);
alert('Error showing modal: ' + error.message);
}
}
// Edit an existing profile
function editProfile(profile: MudProfile) {
dispatch('editProfile', { profile });
}
// Delete a profile
function deleteProfile(profileId: string) {
dispatch('deleteProfile', { profileId });
}
</script>
<div
id="panel-profiles"
role="tabpanel"
aria-labelledby="tab-profiles"
tabindex="0">
<div class="sidebar-header">
<h3>MUD Profiles</h3>
<button
class="btn btn-sm btn-primary"
on:click={createProfile}>New Profile</button>
</div>
<div class="profile-list">
{#if $profiles.length === 0}
<p class="no-items">No profiles found. Create one to get started.</p>
{:else}
{#each $profiles as profile (profile.id)}
<div class="profile-item" class:active={$activeProfileId === profile.id}>
<span class="profile-name">{profile.name}</span>
<div class="profile-actions">
<button class="btn btn-sm" on:click={() => editProfile(profile)} aria-label="Edit profile {profile.name}">✏️</button>
<button class="btn btn-sm" on:click={() => deleteProfile(profile.id)} aria-label="Delete profile {profile.name}">🗑️</button>
</div>
</div>
{/each}
{/if}
</div>
</div>
<style>
[role="tabpanel"]:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
[role="tabpanel"]:focus:not(:focus-visible) {
outline: none;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.sidebar-header h3 {
margin: 0;
font-size: 1.1rem;
}
.profile-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.profile-item {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
border-radius: var(--border-radius);
background-color: var(--color-input-bg);
border: 1px solid var(--color-border);
}
.profile-item.active {
border-color: var(--color-primary);
background-color: rgba(33, 150, 243, 0.1);
}
.profile-name {
font-weight: bold;
}
.profile-actions {
display: flex;
gap: 0.25rem;
}
.no-items {
color: var(--color-text-muted);
font-style: italic;
}
</style>

View File

@@ -0,0 +1,118 @@
<script lang="ts">
import SidebarTabs from './SidebarTabs.svelte';
import ProfileList from './ProfileList.svelte';
import TriggerList from './TriggerList.svelte';
import SettingsPanel from './SettingsPanel.svelte';
import type { MudProfile } from '$lib/profiles/ProfileManager';
import type { Trigger } from '$lib/triggers/TriggerSystem';
export let sidebarTab: 'profiles' | 'triggers' | 'settings' = 'profiles';
// Create event dispatcher
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher<{
saveProfile: { profile: MudProfile },
deleteProfile: { profileId: string },
editProfile: { profile: MudProfile },
saveTrigger: { trigger: Trigger },
deleteTrigger: { triggerId: string },
}>();
// Emit events to parent
function handleSaveProfile(event: CustomEvent<{profile: MudProfile}>) {
const profile = event.detail.profile;
dispatch('saveProfile', { profile });
}
function handleDeleteProfile(event: CustomEvent<{profileId: string}>) {
if (confirm(`Are you sure you want to delete this profile?`)) {
dispatch('deleteProfile', { profileId: event.detail.profileId });
}
}
function handleEditProfile(event: CustomEvent<{profile: MudProfile}>) {
dispatch('editProfile', { profile: event.detail.profile });
}
function handleSaveTrigger(event: CustomEvent<{trigger: Trigger}>) {
dispatch('saveTrigger', { trigger: event.detail.trigger });
}
function handleDeleteTrigger(event: CustomEvent<{triggerId: string}>) {
if (confirm(`Are you sure you want to delete this trigger?`)) {
dispatch('deleteTrigger', { triggerId: event.detail.triggerId });
}
}
function handleTabChange(event: CustomEvent<{tab: 'profiles' | 'triggers' | 'settings'}>) {
sidebarTab = event.detail.tab;
}
</script>
<aside class="mud-sidebar">
<SidebarTabs activeTab={sidebarTab} on:tabChange={handleTabChange} />
<div class="sidebar-content">
{#if sidebarTab === 'profiles'}
<ProfileList
on:saveProfile={handleSaveProfile}
on:deleteProfile={handleDeleteProfile}
on:editProfile={handleEditProfile}
/>
{:else if sidebarTab === 'triggers'}
<TriggerList
on:saveTrigger={handleSaveTrigger}
on:deleteTrigger={handleDeleteTrigger}
/>
{:else if sidebarTab === 'settings'}
<div
id="panel-settings"
role="tabpanel"
aria-labelledby="tab-settings"
tabindex="0">
<div class="sidebar-header">
<h3>Settings</h3>
</div>
<SettingsPanel />
</div>
{/if}
</div>
</aside>
<style>
.mud-sidebar {
display: flex;
flex-direction: column;
width: 250px;
border-right: 1px solid var(--color-border);
background-color: var(--color-bg);
}
.sidebar-content {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
[role="tabpanel"]:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
[role="tabpanel"]:focus:not(:focus-visible) {
outline: none;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.sidebar-header h3 {
margin: 0;
font-size: 1.1rem;
}
</style>

View File

@@ -0,0 +1,133 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let activeTab: 'profiles' | 'triggers' | 'settings' = 'profiles';
const dispatch = createEventDispatcher<{
tabChange: { tab: 'profiles' | 'triggers' | 'settings' }
}>();
// Handle keyboard navigation for sidebar tabs
function handleSidebarTabKeydown(event: KeyboardEvent) {
// Define the tab order
const tabs = ['profiles', 'triggers', 'settings'];
const currentIndex = tabs.indexOf(activeTab);
switch (event.key) {
case 'ArrowRight':
case 'ArrowDown':
// Move to next tab
event.preventDefault();
const nextIndex = (currentIndex + 1) % tabs.length;
dispatch('tabChange', { tab: tabs[nextIndex] as any });
document.getElementById(`tab-${tabs[nextIndex]}`)?.focus();
break;
case 'ArrowLeft':
case 'ArrowUp':
// Move to previous tab
event.preventDefault();
const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
dispatch('tabChange', { tab: tabs[prevIndex] as any });
document.getElementById(`tab-${tabs[prevIndex]}`)?.focus();
break;
case 'Home':
// Move to first tab
event.preventDefault();
dispatch('tabChange', { tab: tabs[0] as any });
document.getElementById(`tab-${tabs[0]}`)?.focus();
break;
case 'End':
// Move to last tab
event.preventDefault();
dispatch('tabChange', { tab: tabs[tabs.length - 1] as any });
document.getElementById(`tab-${tabs[tabs.length - 1]}`)?.focus();
break;
case 'Enter':
case ' ':
// Activate current tab
event.preventDefault();
// The click handler will handle the tab change
break;
default:
// Let other keys function normally
break;
}
}
</script>
<div class="sidebar-tabs" role="tablist" aria-label="Sidebar tabs">
<button
class="sidebar-tab"
id="tab-profiles"
role="tab"
aria-controls="panel-profiles"
aria-selected={activeTab === 'profiles'}
class:active={activeTab === 'profiles'}
tabindex={activeTab === 'profiles' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'profiles' })}
on:keydown={handleSidebarTabKeydown}>
Profiles
</button>
<button
class="sidebar-tab"
id="tab-triggers"
role="tab"
aria-controls="panel-triggers"
aria-selected={activeTab === 'triggers'}
class:active={activeTab === 'triggers'}
tabindex={activeTab === 'triggers' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'triggers' })}
on:keydown={handleSidebarTabKeydown}>
Triggers
</button>
<button
class="sidebar-tab"
id="tab-settings"
role="tab"
aria-controls="panel-settings"
aria-selected={activeTab === 'settings'}
class:active={activeTab === 'settings'}
tabindex={activeTab === 'settings' ? "0" : "-1"}
on:click={() => dispatch('tabChange', { tab: 'settings' })}
on:keydown={handleSidebarTabKeydown}>
Settings
</button>
</div>
<style>
.sidebar-tabs {
display: flex;
border-bottom: 1px solid var(--color-border);
}
.sidebar-tab {
flex: 1;
padding: 0.5rem;
text-align: center;
background: none;
border: none;
cursor: pointer;
color: var(--color-text-muted);
}
.sidebar-tab.active {
color: var(--color-text);
border-bottom: 2px solid var(--color-primary);
}
.sidebar-tab:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
position: relative;
z-index: 2;
}
.sidebar-tab:focus:not(:focus-visible) {
outline: none;
}
</style>

View File

@@ -0,0 +1,184 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { triggers } from '$lib/stores/mudStore';
import { ModalHelper } from '$lib/utils/ModalHelper';
import { TriggerSystem } from '$lib/triggers/TriggerSystem';
import type { Trigger } from '$lib/triggers/TriggerSystem';
// Component state
let triggerSystem = new TriggerSystem();
const dispatch = createEventDispatcher<{
saveTrigger: { trigger: Trigger },
deleteTrigger: { triggerId: string },
}>();
// Create a new trigger
function createTrigger() {
console.log('Creating new trigger using ModalHelper');
try {
ModalHelper.showTriggerEditor(
(trigger) => {
console.log('Trigger saved from trigger editor:', trigger);
dispatch('saveTrigger', { trigger });
},
() => {
console.log('Trigger creation cancelled');
}
);
} catch (error) {
console.error('Error showing trigger editor:', error);
alert('Error showing modal: ' + error.message);
}
}
// Edit an existing trigger
function editTrigger(trigger: Trigger) {
console.log('Editing trigger using ModalHelper:', trigger);
try {
ModalHelper.showTriggerEditor(
(updatedTrigger) => {
console.log('Trigger updated from trigger editor:', updatedTrigger);
dispatch('saveTrigger', { trigger: updatedTrigger });
},
() => {
console.log('Trigger edit cancelled');
},
trigger
);
} catch (error) {
console.error('Error showing trigger editor:', error);
alert('Error showing modal: ' + error.message);
}
}
// Delete trigger
function deleteTrigger(triggerId: string) {
dispatch('deleteTrigger', { triggerId });
}
// Toggle trigger enabled state
function toggleTriggerEnabled(triggerId: string, enabled: boolean) {
triggerSystem.setTriggerEnabled(triggerId, enabled);
// Reload triggers to reflect changes
const allTriggers = triggerSystem.getTriggers();
triggers.set(allTriggers);
}
</script>
<div
id="panel-triggers"
role="tabpanel"
aria-labelledby="tab-triggers"
tabindex="0">
<div class="sidebar-header">
<h3>Triggers</h3>
<button class="btn btn-sm btn-primary" on:click={createTrigger}>New Trigger</button>
</div>
<div class="trigger-list">
{#if $triggers.length === 0}
<p class="no-items">No triggers found. Create one to get started.</p>
{:else}
{#each $triggers as trigger (trigger.id)}
<div class="trigger-item" class:disabled={!trigger.isEnabled}>
<span class="trigger-name">{trigger.name}</span>
<div class="trigger-info">
<span class="trigger-pattern">{trigger.pattern}</span>
{#if trigger.soundFile}
<span class="trigger-sound" aria-label="Has sound">🔊</span>
{/if}
{#if trigger.highlightColor}
<span class="trigger-highlight" style="background-color: {trigger.highlightColor}" aria-label="Highlight color"></span>
{/if}
</div>
<div class="trigger-actions">
<button class="btn btn-sm" on:click={() => toggleTriggerEnabled(trigger.id, !trigger.isEnabled)} aria-label="{trigger.isEnabled ? 'Disable' : 'Enable'} trigger {trigger.name}">
{trigger.isEnabled ? '✓' : '✗'}
</button>
<button class="btn btn-sm" on:click={() => editTrigger(trigger)} aria-label="Edit trigger {trigger.name}">✏️</button>
<button class="btn btn-sm" on:click={() => deleteTrigger(trigger.id)} aria-label="Delete trigger {trigger.name}">🗑️</button>
</div>
</div>
{/each}
{/if}
</div>
</div>
<style>
[role="tabpanel"]:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
[role="tabpanel"]:focus:not(:focus-visible) {
outline: none;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.sidebar-header h3 {
margin: 0;
font-size: 1.1rem;
}
.trigger-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.trigger-item {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
border-radius: var(--border-radius);
background-color: var(--color-input-bg);
border: 1px solid var(--color-border);
}
.trigger-item.disabled {
opacity: 0.5;
}
.trigger-info {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.25rem;
font-size: 0.9rem;
color: var(--color-text-muted);
}
.trigger-name {
font-weight: bold;
}
.trigger-pattern {
font-family: monospace;
}
.trigger-highlight {
width: 16px;
height: 16px;
border-radius: 4px;
border: 1px solid var(--color-border);
}
.trigger-actions {
display: flex;
gap: 0.25rem;
}
.no-items {
color: var(--color-text-muted);
font-style: italic;
}
</style>

View File

@@ -9,7 +9,7 @@
} }
import MudMdi from '$lib/components/MudMdi.svelte'; import MudMdi from '$lib/components/MudMdi.svelte';
import KeyboardShortcutsHelp from '$lib/components/KeyboardShortcutsHelp.svelte'; import KeyboardShortcutsHelp from '$lib/components/KeyboardShortcutsHelp.svelte';
import SettingsPanel from '$lib/components/SettingsPanel.svelte'; import Sidebar from '$lib/components/Sidebar.svelte';
import { ModalHelper } from '$lib/utils/ModalHelper'; import { ModalHelper } from '$lib/utils/ModalHelper';
import { import {
profiles, profiles,
@@ -31,11 +31,9 @@
// Component state // Component state
let profileManager: ProfileManager; let profileManager: ProfileManager;
let triggerSystem: TriggerSystem; let triggerSystem: TriggerSystem;
let showTriggerEditor = false; let showKeyboardShortcutsHelp = false;
let editingTrigger: Trigger | null = null;
let showSidebar = true; let showSidebar = true;
let sidebarTab: 'profiles' | 'triggers' | 'settings' = 'profiles'; let sidebarTab: 'profiles' | 'triggers' | 'settings' = 'profiles';
let showKeyboardShortcutsHelp = false;
// Save profile from component // Save profile from component
function saveProfile(event) { function saveProfile(event) {
@@ -252,49 +250,8 @@
// Delete profile // Delete profile
function deleteProfile(profileId: string) { function deleteProfile(profileId: string) {
if (confirm(`Are you sure you want to delete this profile?`)) { profileManager.removeProfile(profileId);
profileManager.removeProfile(profileId); loadProfiles();
loadProfiles();
}
}
// Create a new trigger
function createTrigger() {
console.log('Creating new trigger using ModalHelper');
try {
ModalHelper.showTriggerEditor(
(trigger) => {
console.log('Trigger saved from trigger editor:', trigger);
saveTrigger({ detail: { trigger } });
},
() => {
console.log('Trigger creation cancelled');
}
);
} catch (error) {
console.error('Error showing trigger editor:', error);
alert('Error showing modal: ' + error.message);
}
}
// Edit an existing trigger
function editTrigger(trigger: Trigger) {
console.log('Editing trigger using ModalHelper:', trigger);
try {
ModalHelper.showTriggerEditor(
(updatedTrigger) => {
console.log('Trigger updated from trigger editor:', updatedTrigger);
saveTrigger({ detail: { trigger: updatedTrigger } });
},
() => {
console.log('Trigger edit cancelled');
},
trigger
);
} catch (error) {
console.error('Error showing trigger editor:', error);
alert('Error showing modal: ' + error.message);
}
} }
// Save trigger // Save trigger
@@ -302,20 +259,16 @@
const trigger = event.detail.trigger; const trigger = event.detail.trigger;
triggerSystem.addTrigger(trigger); triggerSystem.addTrigger(trigger);
loadTriggers(); loadTriggers();
showTriggerEditor = false;
editingTrigger = null;
} }
// Delete trigger // Delete trigger
function deleteTrigger(triggerId: string) { function deleteTrigger(triggerId: string) {
if (confirm(`Are you sure you want to delete this trigger?`)) { triggerSystem.removeTrigger(triggerId);
triggerSystem.removeTrigger(triggerId); loadTriggers();
loadTriggers();
}
} }
// Toggle keyboard shortcuts help // Toggle keyboard shortcuts help
function toggleKeyboardShortcutsHelp() { function toggleKeyboardShortcutsHelp() {
showKeyboardShortcutsHelp = !showKeyboardShortcutsHelp; showKeyboardShortcutsHelp = !showKeyboardShortcutsHelp;
} }
@@ -351,58 +304,6 @@
fontSize: size fontSize: size
})); }));
} }
// Handle keyboard navigation for sidebar tabs
function handleSidebarTabKeydown(event: KeyboardEvent) {
// Define the tab order
const tabs = ['profiles', 'triggers', 'settings'];
const currentIndex = tabs.indexOf(sidebarTab);
switch (event.key) {
case 'ArrowRight':
case 'ArrowDown':
// Move to next tab
event.preventDefault();
const nextIndex = (currentIndex + 1) % tabs.length;
sidebarTab = tabs[nextIndex];
document.getElementById(`tab-${tabs[nextIndex]}`)?.focus();
break;
case 'ArrowLeft':
case 'ArrowUp':
// Move to previous tab
event.preventDefault();
const prevIndex = (currentIndex - 1 + tabs.length) % tabs.length;
sidebarTab = tabs[prevIndex];
document.getElementById(`tab-${tabs[prevIndex]}`)?.focus();
break;
case 'Home':
// Move to first tab
event.preventDefault();
sidebarTab = tabs[0];
document.getElementById(`tab-${tabs[0]}`)?.focus();
break;
case 'End':
// Move to last tab
event.preventDefault();
sidebarTab = tabs[tabs.length - 1];
document.getElementById(`tab-${tabs[tabs.length - 1]}`)?.focus();
break;
case 'Enter':
case ' ':
// Activate current tab
event.preventDefault();
// The click handler will handle the tab change
break;
default:
// Let other keys function normally
break;
}
}
</script> </script>
<div class="mud-client"> <div class="mud-client">
@@ -420,147 +321,14 @@
<div class="mud-content"> <div class="mud-content">
{#if showSidebar} {#if showSidebar}
<aside class="mud-sidebar"> <Sidebar
<div class="sidebar-tabs" role="tablist" aria-label="Sidebar tabs"> bind:sidebarTab={sidebarTab}
<button on:saveProfile={saveProfile}
class="sidebar-tab" on:deleteProfile={(e) => deleteProfile(e.detail.profileId)}
id="tab-profiles" on:editProfile={(e) => editProfile(e.detail.profile)}
role="tab" on:saveTrigger={saveTrigger}
aria-controls="panel-profiles" on:deleteTrigger={(e) => deleteTrigger(e.detail.triggerId)}
aria-selected={sidebarTab === 'profiles'} />
class:active={sidebarTab === 'profiles'}
tabindex={sidebarTab === 'profiles' ? "0" : "-1"}
on:click={() => sidebarTab = 'profiles'}
on:keydown={handleSidebarTabKeydown}>
Profiles
</button>
<button
class="sidebar-tab"
id="tab-triggers"
role="tab"
aria-controls="panel-triggers"
aria-selected={sidebarTab === 'triggers'}
class:active={sidebarTab === 'triggers'}
tabindex={sidebarTab === 'triggers' ? "0" : "-1"}
on:click={() => sidebarTab = 'triggers'}
on:keydown={handleSidebarTabKeydown}>
Triggers
</button>
<button
class="sidebar-tab"
id="tab-settings"
role="tab"
aria-controls="panel-settings"
aria-selected={sidebarTab === 'settings'}
class:active={sidebarTab === 'settings'}
tabindex={sidebarTab === 'settings' ? "0" : "-1"}
on:click={() => sidebarTab = 'settings'}
on:keydown={handleSidebarTabKeydown}>
Settings
</button>
</div>
<div class="sidebar-content">
{#if sidebarTab === 'profiles'}
<div
id="panel-profiles"
role="tabpanel"
aria-labelledby="tab-profiles"
tabindex="0">
<div class="sidebar-header">
<h3>MUD Profiles</h3>
<button
class="btn btn-sm btn-primary"
on:click={() => {
console.log('New Profile button clicked - using full profile editor');
try {
ModalHelper.showProfileEditor(
(profile) => {
console.log('Profile saved from profile editor:', profile);
// Use the same saveProfile function for consistency
saveProfile({ detail: { profile } });
},
() => {
console.log('Profile creation cancelled');
}
);
} catch (error) {
console.error('Error showing profile editor:', error);
alert('Error showing modal: ' + error.message);
}
}}>New Profile</button>
</div>
<div class="profile-list">
{#if $profiles.length === 0}
<p class="no-items">No profiles found. Create one to get started.</p>
{:else}
{#each $profiles as profile (profile.id)}
<div class="profile-item" class:active={$activeProfileId === profile.id}>
<span class="profile-name">{profile.name}</span>
<div class="profile-actions">
<button class="btn btn-sm" on:click={() => editProfile(profile)} aria-label="Edit profile {profile.name}">✏️</button>
<button class="btn btn-sm" on:click={() => deleteProfile(profile.id)} aria-label="Delete profile {profile.name}">🗑️</button>
</div>
</div>
{/each}
{/if}
</div>
</div>
{:else if sidebarTab === 'triggers'}
<div
id="panel-triggers"
role="tabpanel"
aria-labelledby="tab-triggers"
tabindex="0">
<div class="sidebar-header">
<h3>Triggers</h3>
<button class="btn btn-sm btn-primary" on:click={createTrigger}>New Trigger</button>
</div>
<div class="trigger-list">
{#if $triggers.length === 0}
<p class="no-items">No triggers found. Create one to get started.</p>
{:else}
{#each $triggers as trigger (trigger.id)}
<div class="trigger-item" class:disabled={!trigger.isEnabled}>
<span class="trigger-name">{trigger.name}</span>
<div class="trigger-info">
<span class="trigger-pattern">{trigger.pattern}</span>
{#if trigger.soundFile}
<span class="trigger-sound" aria-label="Has sound">🔊</span>
{/if}
{#if trigger.highlightColor}
<span class="trigger-highlight" style="background-color: {trigger.highlightColor}" aria-label="Highlight color"></span>
{/if}
</div>
<div class="trigger-actions">
<button class="btn btn-sm" on:click={() => triggerSystem.setTriggerEnabled(trigger.id, !trigger.isEnabled)} aria-label="{trigger.isEnabled ? 'Disable' : 'Enable'} trigger {trigger.name}">
{trigger.isEnabled ? '✓' : '✗'}
</button>
<button class="btn btn-sm" on:click={() => editTrigger(trigger)} aria-label="Edit trigger {trigger.name}">✏️</button>
<button class="btn btn-sm" on:click={() => deleteTrigger(trigger.id)} aria-label="Delete trigger {trigger.name}">🗑️</button>
</div>
</div>
{/each}
{/if}
</div>
</div>
{:else if sidebarTab === 'settings'}
<div
id="panel-settings"
role="tabpanel"
aria-labelledby="tab-settings"
tabindex="0">
<div class="sidebar-header">
<h3>Settings</h3>
</div>
<SettingsPanel />
</div>
{/if}
</div>
</aside>
{/if} {/if}
<main class="mud-main"> <main class="mud-main">
@@ -605,254 +373,9 @@
overflow: hidden; overflow: hidden;
} }
.mud-sidebar {
display: flex;
flex-direction: column;
width: 250px;
border-right: 1px solid var(--color-border);
background-color: var(--color-bg);
}
.sidebar-tabs {
display: flex;
border-bottom: 1px solid var(--color-border);
}
.sidebar-tab {
flex: 1;
padding: 0.5rem;
text-align: center;
background: none;
border: none;
cursor: pointer;
color: var(--color-text-muted);
}
.sidebar-tab.active {
color: var(--color-text);
border-bottom: 2px solid var(--color-primary);
}
.sidebar-tab:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
position: relative;
z-index: 2;
}
.sidebar-tab:focus:not(:focus-visible) {
outline: none;
}
[role="tabpanel"]:focus {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
[role="tabpanel"]:focus:not(:focus-visible) {
outline: none;
}
.sidebar-content {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.sidebar-header h3 {
margin: 0;
font-size: 1.1rem;
}
.profile-list, .trigger-list, .settings-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.profile-item, .trigger-item, .setting-item {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
border-radius: var(--border-radius);
background-color: var(--color-input-bg);
border: 1px solid var(--color-border);
}
.setting-description {
flex-basis: 100%;
margin-top: 5px;
font-size: 0.85rem;
color: var(--color-text-muted);
font-style: italic;
}
.profile-item.active {
border-color: var(--color-primary);
background-color: rgba(33, 150, 243, 0.1);
}
.profile-name, .trigger-name, .setting-name {
font-weight: bold;
}
.profile-actions, .trigger-actions {
display: flex;
gap: 0.25rem;
}
.trigger-item.disabled {
opacity: 0.5;
}
.trigger-info {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.25rem;
font-size: 0.9rem;
color: var(--color-text-muted);
}
.trigger-pattern {
font-family: monospace;
}
.trigger-highlight {
width: 16px;
height: 16px;
border-radius: 4px;
border: 1px solid var(--color-border);
}
.no-items {
color: var(--color-text-muted);
font-style: italic;
}
.mud-main { .mud-main {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
} }
</style>
/* Switch styling */
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: 0.4s;
}
input:checked + .slider {
background-color: var(--color-primary);
}
input:focus + .slider {
box-shadow: 0 0 1px var(--color-primary);
}
input:checked + .slider:before {
transform: translateX(16px);
}
.slider.round {
border-radius: 24px;
}
.slider.round:before {
border-radius: 50%;
}
.range-control {
display: flex;
align-items: center;
gap: 0.5rem;
}
.range-control input {
flex: 1;
}
.range-value {
min-width: 40px;
text-align: right;
}
/* Form elements for modals */
.form-row {
margin-bottom: 15px;
}
.form-row label {
display: block;
margin-bottom: 5px;
}
.form-row input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.buttons button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.buttons button:first-child {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
.buttons button:last-child {
background-color: #2196f3;
color: white;
border: none;
}
</style>