Trixie
◇TRIXIE
{connectionState}{#if connectionNotice}{connectionNotice}{/if}{#if authenticated}
{#if showNotifications}
NOTIFICATIONS
{#if notifications.length}{#each notifications as notification}
{notification.title}{notification.severity}
{notification.message}
{#if !notification.seen_at}{/if}
{/each}{:else}
No notifications.
{/if}
{/if}
{/if}
{#if !authenticated}
AUTHENTICATION REQUIRED
⌁
Sign in to TrixieThe server connection is ready, but the dashboard requires administrator authentication.
{:else if mustReplaceCredentials}
FIRST-RUN SETUP
Replace the generated administrator credentials {#if authError}
{authError}
{/if}
{:else if activeView === 'Plugins'}
WEBASSEMBLY PLUGINS

Packages are validated and started in a resource-limited WebAssembly runtime. Only explicitly declared Trixie permissions are accepted; installation never grants filesystem or process access.

{#if pluginError}
{pluginError}
{/if}{#if plugins.length}
{#each plugins as plugin}
{plugin.name}{plugin.plugin_id} · v{plugin.version}
API {plugin.manifest.api_version} · {plugin.manifest.permissions.length ? plugin.manifest.permissions.join(' · ') : 'NO PERMISSIONS'}
{plugin.enabled ? 'ENABLED' : 'DISABLED'}
{/each}
{:else}
No plugins are installed.
{/if}
PLUGIN SECRET APPROVAL

Plugins cannot read secrets until an administrator explicitly approves a binding. The secret value is never shown here.

{#if plugins.some((plugin) => plugin.manifest.required_secrets?.length)}
{#each plugins.filter((plugin) => plugin.manifest.required_secrets?.length) as plugin}
{plugin.name}Required: {plugin.manifest.required_secrets?.join(' · ')}
{/each}
{:else}
Installed plugins have no secret requirements.
{/if}
{:else if activeView === 'Bot'}
{commandFormId ? 'EDIT COMMAND' : 'NEW COMMAND'}
{#if commandVariableSuggestions.length}
{#each commandVariableSuggestions as variable}{/each}
{/if}
SIMULATION

Preview with literal sample data. Real requests are opt-in and use the same server-side safety checks.

{#if commandSimulationError}
{commandSimulationError}
{/if}{#if commandSimulationResult}
{commandSimulationResult.rendered ?? commandSimulationResult.error ?? 'Simulation completed.'}{#if commandSimulationResult.extracted_variables && Object.keys(commandSimulationResult.extracted_variables).length}Extracted: {formatExtractedVariables(commandSimulationResult.extracted_variables)}{/if}
{/if}
BOT COMMANDS
PUBLIC COMMAND CATALOG
{#if botCommandError}
{botCommandError}
{:else if botCommands.length}
{#each botCommands as command}
!{command.name}{command.description}{#if command.tags.length}
{command.tags.join(' · ')}
{/if}
{command.permission_level.toUpperCase()}{#if command.cooldown_seconds}{command.cooldown_seconds}s / {command.cooldown_scope}{/if}
{/each}
{:else}
No enabled commandsCreate commands on the server to make them available here.
{/if}
SECRETS MANAGEMENT
Values are encrypted and cannot be read after saving.
{#if botSecrets.length}
{#each botSecrets as secret}
{secret.name}{secret.description || 'No description'}{#if secret.used_by.length}
USED BY: {secret.used_by.join(', ')}
{/if}
{/each}
{:else}
There are no secrets yet.
{/if}{#if secretError}
{secretError}
{/if}
COMMAND MANAGEMENT
{#each botCommands as command}
!{command.name}{command.enabled ? 'ENABLED' : 'DISABLED'} · {command.permission_level.toUpperCase()}
{/each}{#if !botCommands.length}
No commands are configured yet.
{/if}
WEB ACTION CONFIGURATION

Optional HTTPS request and response variables. The server validates the URL and never executes template values as code.

USER-SPECIFIC RULES{commandFormId ? `COMMAND ${commandFormName}` : 'SELECT A COMMAND'}

A command with rules is limited to the named Twitch users. Add an optional response for a personalized variant.

{#if commandRuleError}
{commandRuleError}
{/if}{#if commandRules.length}
{#each commandRules as rule}
{rule.user_id}{rule.response_template || 'Uses the command response template'}
{/each}
{:else}
No user-specific rules configured.
{/if}
TYPED ARGUMENTSJSON DEFINITIONS

Declare text, number, choice, username, or duration arguments. Values are validated as data and never executed.

Example: a JSON array containing count, number, required, minimum, and maximum fields.
{:else if activeView === 'Collaboration'} {#if collaborationSession}
ACTIVE COLLABORATION{collaborationSession.guests.length} GUESTS
GUEST STAR CALL
The call is controlled by Twitch

Twitch does not expose the call media as an embeddable Trixie API. Use Twitch for the live call controls while Trixie keeps the session, slots, invites, and shared-chat state synchronized.

PARTICIPANTS
{#each collaborationSession.guests as guest}
{guest.user_display_name || guest.user_id}{guest.is_live ? 'LIVE' : 'WAITING'}{guest.slot_id ? ` · SLOT ${guest.slot_id}` : ''}
{:else}
No participants have joined.
{/each}
SHARED CHAT
{#if sharedChatSession}
ACTIVE · {sharedChatSession.participants.length} CHANNELS
{:else}
Shared Chat is not active.
{/if}{#if collaborationSettings}
CHANNEL SETTINGS
{collaborationSettings.slot_count} SLOTS · {collaborationSettings.group_layout}
AUDIO {collaborationSettings.is_browser_source_audio_enabled ? 'ENABLED' : 'DISABLED'} · MODERATOR LIVE CONTROL {collaborationSettings.is_moderator_send_live_enabled ? 'ENABLED' : 'DISABLED'}
{/if}
{#if collaborationNotice}
{collaborationNotice}
{/if}{#if collaborationError}
{collaborationError}
{/if}
PARTICIPANT CONTROLSTWITCH-AUTHORITATIVE

Assign, remove, or toggle live state for ready participants. Twitch remains authoritative for slot availability.

{#each collaborationSession.guests as guest}
{guest.user_display_name || guest.user_id}{guest.slot_id ? ` SLOT ${guest.slot_id}` : ' UNSLOTTED'}
{#if guest.slot_id}{/if}
{/each}{#if !collaborationSession.guests.length}
No participants are ready for slot controls.
{/if}
{:else}
COLLABORATIONSEARCH OR REVIEW INCOMING
FIND A COLLABORATOR
searchCollaborators(event.currentTarget.value)} autocomplete="off" placeholder="Search Twitch" />
{#if collaboratorSearchBusy}
SEARCHING TWITCH…
{/if}{#if collaboratorSearchError}
{collaboratorSearchError}
{/if}{#if collaboratorResults.length}
{#each collaboratorResults as result}
{result.display_name} @{result.login}
{/each}
{:else if collaboratorSearch && !collaboratorSearchBusy && !collaboratorSearchError}
No exact Twitch user found.
{/if}
FAVORITES & RECENT
{#if collaborators.length}
{#each collaborators as collaborator}
{collaborator.display_name}{collaborator.favorite ? 'FAVORITE' : 'RECENT'}
{/each}
{:else}
No saved collaborators yet.
{/if}
INCOMING INVITATIONS
⌁
Waiting for a collaboration inviteAccept or decline invitations in Twitch. Trixie will reflect the authoritative status here when Twitch reports it.
{#if collaborationNotice}
{collaborationNotice}
{/if}{#if collaborationError}
{collaborationError}
{/if}
{/if} {:else if activeView === 'Overlays'}
OVERLAYS
{#if selectedOverlayId || overlayName}
LIVE PREVIEW
{#if overlayError}
{overlayError}
{/if}{:else}
Select or create an overlayDrafts autosave when explicitly saved; publishing controls the browser source.
{/if}
ALERT PLAYBACK

Configure grouping and playback for the selected browser-source overlay. Changes are saved with the next draft save.

{:else if activeView === 'Settings'}
SERVER SETTINGS
DATABASE BACKUPS

Daily backups use the server's local schedule. Restore replaces the current database and is permanently destructive.

{#if backupError}
{backupError}
{/if}{#if backups.length}
{#each backups as backup}
{backup.filename}{Math.round(backup.size_bytes / 1024)} KiB{backup.modified_at ? new Date(backup.modified_at).toLocaleString() : 'UNKNOWN TIME'}
{/each}
{:else}
No backup archives are available.
{/if}
{:else}
STREAM PREVIEW⋮
{#if playerSrc}{:else}
STREAM OFFLINE
CONNECT TWITCH TO BEGIN
{/if}
ACTIVITY FEED⋮
{#if activityEvents.filter((event) => activityFilter === 'all' || event.event_type.startsWith('moderation.')).length}
{#each activityEvents.filter((event) => activityFilter === 'all' || event.event_type.startsWith('moderation.')) as event}
{event.event_type}{new Date(event.occurred_at).toLocaleTimeString()}
{/each}
{:else}
◌
Nothing here yet{activityFilter === 'moderation' ? 'Moderation events will appear here.' : 'Follows, subscriptions, raids, and server events will appear here.'}
{/if}
MY CHAT⋮
{#if pinnedMessageIds.length}
PINNED{#each chatMessages.filter((message) => pinnedMessageIds.includes(message.message_id)) as message}
{message.user_name}{message.text}
{/each}
{/if}{#if chatMessages.length}
{#each chatMessages as message}
{#if message.fragments.length}{#each message.fragments as fragment}{#if fragment.type === 'emote' && fragment.emote_id}{fragment.text}{:else}{fragment.text}{/if}{/each}{:else}{message.text}{/if}
{/each}
{:else}
⌁
Chat is waitingConnect a streamer or bot account to begin.
{/if}
{#if chatReplyParent}
REPLYING TO @{chatReplyParent.user_name}
{/if}
{#if chatEmoteOptions.length}
{#each chatEmoteOptions as emote}{/each}
{/if}
searchChatEmotes(chatMessage)} onkeydown={(event) => event.key === 'Enter' && sendChatMessage()} />
{#if chatError}
{chatError}
{/if}
QUICK ACTIONS⋮
{#if showCollaboration}
GUEST STAR SESSION
{#if collaborationSession}
ACTIVE · {collaborationSession.guests.length} GUESTS
{#if collaborationInvites.length}
PENDING INVITES
{#each collaborationInvites as invite}
{invite.user_id} · {invite.status}
{/each}{/if}{:else}
No active collaboration.
{/if}{#if collaborationError}
{collaborationError}
{/if}
{/if}{#if showStreamEditor}
searchCategories(event.currentTarget.value)} autocomplete="off" placeholder="Search Twitch categories" />{#if categoryOptions.length}
{#each categoryOptions as category}{/each}
{/if}{#if streamError}
{streamError}
{/if}
{/if}{#if showModeration}
{#if moderationError}
{moderationError}
{/if}
{/if}
{/if}
{#if authenticated && showCollaboration}
COLLABORATOR SEARCH⋮
searchCollaborators(event.currentTarget.value)} autocomplete="off" placeholder="Search Twitch" />
{#if collaboratorSearchBusy}
SEARCHING TWITCH…
{/if} {#if collaboratorSearchError}
{collaboratorSearchError}
{/if} {#if collaboratorResults.length}
{#each collaboratorResults as result}
{result.display_name} @{result.login}
{/each}
{:else if collaboratorSearch && !collaboratorSearchBusy && !collaboratorSearchError}
No exact Twitch user found.
{/if}
FAVORITES & RECENT
{#if collaborators.length}
{#each collaborators as collaborator}
{collaborator.display_name}
{/each}
{:else}
No saved collaborators yet.
{/if}
{/if} {#if chatMessages.some((message) => message.reply_parent_message_id)}
CHAT THREADS
{#each chatMessages.filter((message) => message.reply_parent_message_id) as reply} {@const root = chatMessages.find((message) => message.message_id === reply.reply_parent_message_id)} {#if root}{/if} {/each}
{/if} {#if chatThreadRoot}
THREAD · @{chatThreadRoot.user_name}
{chatThreadRoot.user_name}{chatThreadRoot.text}
{#each chatMessages.filter((message) => message.reply_parent_message_id === chatThreadRoot?.message_id) as reply}
{reply.user_name}{reply.text}
{:else}
No replies received yet.
{/each}
{/if} {#if pendingOverlayDraft}
LOCAL OVERLAY DRAFT FOUND
A newer unsaved draft is available for this overlay.
{/if} {#if activeView === 'Overlays' && selectedOverlayId}
OVERLAY LIFECYCLE
DELETION IS PERMANENT AND INVALIDATES THE BROWSER-SOURCE URL
{/if} {#if activeView === 'Overlays' && (selectedOverlayId || overlayName) && overlayVariableWarnings().length}
{#if overlayDiagnosticsOpen}
{#each overlayVariableWarnings() as warning}
{warning}
{/each}
{/if}
{/if} {#if !authenticated && publicBotCommands.length}
PUBLIC BOT COMMANDS
AVAILABLE COMMANDS
{#each publicBotCommands as command}
!{command.name}{command.description}{#if command.tags.length}
{command.tags.join(' · ')}
{/if}
{command.permission_level.toUpperCase()}
{/each}
{/if}