{ if (!interactive || event.target !== event.currentTarget) return; // The surrounding bits-ui ContextMenu.Trigger opens on the bubbling // contextmenu event and prevents the native menu itself. Calling // preventDefault() here makes the trigger treat the event as handled // (it bails when defaultPrevented is set), leaving no menu at all. oncontext?.(undefined, event); }} > {#snippet maskedLayer(element: ThemeV2Element, depth: number)} {@const masks = maskedAncestors(element)} {#if depth < masks.length}
{@render maskedLayer(element, depth + 1)}
{:else} {@render layerContent(element)} {/if} {/snippet} {#snippet layerContent(element: ThemeV2Element)} {@const selected = isSelected(element.id)} {@const singleSelected = selected && selectedIds.length === 1} {@const elementLocked = isElementLocked(element)}
beginDrag(event, element)} onclick={() => triggerInteraction(element, 'click')} onpointerenter={() => { triggerInteraction(element, 'hover'); showGroupHover(element); }} onpointerleave={() => { triggerInteraction(element, 'leave'); if (interactive) scheduleHoverHide(); }} oncontextmenu={(event) => { if (!interactive) return; // Let the event bubble so the editor's ContextMenu.Trigger can open. oncontext?.(element.id, event); }} ondblclick={(event) => { if (!interactive || !element.groupId) return; event.preventDefault(); event.stopPropagation(); onselectgroup?.(element.groupId); }} onkeydown={(event) => { if (interactive && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); selectElement(element.id, modifierPressed(event)); } else if ( !interactive && element.interactive && (event.key === 'Enter' || event.key === ' ') ) { event.preventDefault(); triggerInteraction(element, 'click'); } }} >
{#if element.type === 'text' || element.type === 'data'} {@const content = element.type === 'data' ? dataValue(element) : interpolateThemeText(element.text, variables)}
{#if isMorphEnabled(element)} {:else} {content} {/if}
{:else if element.type === 'image'} {#if element.tint}
{:else} {/if} {:else if element.type === 'video'} {:else if element.type === 'svelte'} {:else if element.type === 'shape'} {#if element.shape === 'arc'} {@const arc = arcGeometry(element)} {@const gradientVector = themeSvgGradientVector(element.gradientAngle ?? 135)} {#if element.fillMode === 'gradient'} {/if} {:else}
{/if} {:else if element.type === 'icon'}
{:else if element.type === 'repeater'}
{#each Array(repeaterTiles(element).total) as _, index (index)} {#if element.source === 'image' && element.src} {:else} {/if} {/each}
{:else if element.type === 'gauge'} {#if element.mode === 'ring'}
{#if gaugeLabel(element)} {@const label = gaugeLabel(element)} {#if isMorphEnabled(element)} {:else} {label} {/if} {/if}
{:else}
{#if gaugeLabel(element)} {@const label = gaugeLabel(element)} {#if isMorphEnabled(element)} {:else} {label} {/if} {/if}
{/if} {:else if element.type === 'ticker'}
{#each [0, 1] as copy (copy)} {#if isMorphEnabled(element)} {:else} {interpolateThemeText(element.text, variables)} {/if} {/each}
{:else if element.type === 'lottie'} {#key element.src} {/key} {:else if element.type === 'button'} {@const isActionLaunch = element.action?.type === 'launch'} {:else if element.type === 'path'} {@const gradientVector = themeSvgGradientVector(element.gradientAngle ?? 135)} {#if element.fillMode === 'gradient'} {/if} {:else if element.type === 'particles'}
{#each themeParticleSeeds(element) as seed, index (index)} {/each}
{:else if element.type === 'clock'} {@const now = clockNow} {#if element.mode === 'analog'} {@const hands = analogHands(now)} {#each Array(12) as _, index (index)} {/each} {#if element.showSeconds !== false} {/if} {:else}
{#if element.label} {element.label} {/if} {#if isMorphEnabled(element)} {:else} {themeClockTime(element, now)} {/if} {#if element.showDate} {#if isMorphEnabled(element)} {:else} {themeClockDate(now)} {/if} {/if}
{/if} {:else if element.type === 'countdown'}
{#if isMorphEnabled(element)} {:else} {themeCountdownText(element, clockNow)} {/if}
{:else if element.type === 'widget'}
{#if element.widget === 'launch-button'} {#if element.variant === 'icon-only' || element.variant === 'minimal'} {@const btnRadius = Math.max( 12, Math.round(Math.min(element.width, element.height) / 2) - 6 )} {@const btnCircumference = 2 * Math.PI * btnRadius} {@const strokeOffset = launchProgress < 0 ? btnCircumference * 0.7 : btnCircumference - (Math.max(0, Math.min(100, launchProgress)) / 100) * btnCircumference} {:else if element.variant === 'compact'} {:else}
{ if (interactive) return; if (!launchDisabled && !isLaunching) { onbuttonaction?.({ type: 'launch' }); } }} />
{/if} {:else if element.widget === 'player-card'} {@const playerStats = $currentUserInfo?.stats?.['0'] ?? $themeGlobalData?.user?.stats?.osu} {@const accuracyVal = playerStats && 'acc' in playerStats ? playerStats.acc : playerStats && 'accuracy' in playerStats ? playerStats.accuracy : 98.3} {@const playerId = $currentUser?.id ?? $currentUserInfo?.info?.id ?? 0} {@const playerName = $currentUser?.name ?? 'Player'} {@const country = $currentUserInfo?.info?.country} {@const levelVal = playerStats && 'level' in playerStats ? playerStats.level : 1} {@const levelProgress = playerStats && 'level_progress' in playerStats ? playerStats.level_progress : 75} {@const rankVal = playerStats?.rank ? playerStats.rank : 102} {@const countryRankVal = playerStats && 'country_rank' in playerStats ? playerStats.country_rank : undefined} {@const ppVal = playerStats?.pp ? playerStats.pp : 4521} {@const playsVal = playerStats?.plays ? Number(playerStats.plays).toLocaleString() : '11,813'} {#if element.variant === 'minimal'}
{#if country} {countryCodeToEmoji(country)} {/if}
{playerName} #{rankVal} · {ppVal}pp
Lv.{levelVal}
{:else if element.variant === 'compact'}
{#if country} {countryCodeToEmoji(country)} {/if}
{playerName} Lv.{levelVal}
#{rankVal} · {ppVal}pp
Accuracy {accuracyVal.toFixed(1)}%
Plays {playsVal}
{:else if element.variant === 'banner' || element.showStats === false || element.height < 140}
{#if country} {countryCodeToEmoji(country)} {/if}
{playerName}
Lv.{levelVal}
{levelProgress}%
{:else}
{#if country} {countryCodeToEmoji(country)} {/if}
{playerName}
Lv.{levelVal}
{levelProgress}%
Rank {#if countryRankVal} #{countryRankVal} {/if}
#{rankVal}
Performance {ppVal}pp
Accuracy {accuracyVal.toFixed(1)}%
Plays {playsVal}
{/if} {:else if element.widget === 'recent-activity'} {@const event = $currentUserInfo?.events?.[0]} {#if element.variant === 'minimal'}
{#if event?.setId} {event.title} {:else}
{/if}
{event?.title ?? 'Space Pirates'} {event?.artist ?? 'Waterflame'} · {event ? formatRelativeTime(event.time) : '5d ago'}
{event?.grade ?? 'S'}
{:else if element.variant === 'compact'}
{#if event?.setId} {event.title} {/if}
Recent Activity {event?.grade ?? 'S'}
{event?.title ?? 'Space Pirates'}
[{event?.version ?? 'Galactic Thieves'}] Rank #{event?.rank ?? 1}
{:else}
{#if event?.setId} {event.title} {/if}
Recent Activity {event?.grade ?? 'S'}
{event?.title ?? 'Space Pirates'} {event?.artist ?? 'Waterflame'}
[{event?.version ?? 'Galactic Thieves'}]
Rank #{event?.rank ?? 1}
{event ? formatRelativeTime(event.time) : '5d ago'} ez-pp.farm
{/if} {:else if element.widget === 'telemetry'}
{$themeGlobalData?.server?.ping ?? 19}ms
{$themeGlobalData?.osu?.stream ?? 'stable'}
{:else if element.widget === 'header'} {#if element.variant === 'minimal'}
EZPPLauncher
{:else if element.variant === 'compact'}
EZPPLauncher {timeGreeting}, {$currentUser?.name ?? 'Guest'}!
{:else}
EZPPLauncher {timeGreeting}, {$currentUser?.name ?? 'Guest'}!
{/if} {:else if element.widget === 'avatar'} {@const shapeClass = element.variant === 'rounded' ? 'rounded-2xl' : element.variant === 'square' ? 'rounded-md' : 'rounded-full'} {@const playerId = $currentUser?.id ?? $currentUserInfo?.info?.id ?? 0} {@const playerName = $currentUser?.name ?? 'Player'} {@const country = $currentUserInfo?.info?.country} {@const level = $currentUserInfo?.stats?.['0']?.level}
{ if (!interactive && playerId) { e.stopPropagation(); void openURL(`https://ez-pp.farm/u/${playerId}`); } }} onkeydown={(e) => { if (!interactive && playerId && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); e.stopPropagation(); void openURL(`https://ez-pp.farm/u/${playerId}`); } }} title={playerName} > {#if element.showOnlineRing !== false}
{/if} {#if element.showCountry !== false && country} {countryCodeToEmoji(country)} {/if} {#if element.showLevel && level !== undefined} Lv.{level} {/if}
{/if}
{/if}
{#if singleSelected && !elementLocked} {#each resizeDirections as direction (direction)} beginResize(event, element, direction)} role="presentation" > {/each} {element.type === 'widget' ? element.widget === 'player-card' ? 'Player Card' : element.widget === 'recent-activity' ? 'Recent Activity' : element.widget === 'launch-button' ? 'Launch Button' : element.widget === 'telemetry' ? 'Telemetry Bar' : element.widget === 'header' ? 'Header Title' : element.widget === 'avatar' ? 'Player Avatar' : 'Widget' : element.type} {/if} {#if selected && selectedIds.length > 1} selected {/if}
{/snippet} {#each visibleElements as element (element.id)} {@render maskedLayer(element, 0)} {/each} {#if hoverGroupFrame} {@const hover = hoverGroupFrame} {@const breadcrumb = themeGroupPath(groups, hover.group.id)} {#if hover.group.id !== selectedGroupId} `${corner.x},${corner.y}`).join(' ')} fill="var(--primary)" fill-opacity="0.02" stroke="var(--primary)" stroke-opacity="0.8" stroke-width="1" stroke-dasharray="5 4" /> {/if} {#if hover.group.id !== selectedGroupId || breadcrumb.length > 1}
event.stopPropagation()} oncontextmenu={(event) => { if (!interactive) return; // Let the event bubble so the editor's ContextMenu.Trigger opens. const target = event.target instanceof Element ? event.target.closest('[data-group-id]') : null; oncontextgroup?.(target?.dataset.groupId ?? hover.group.id, event); }} > {#each breadcrumb as group, index (group.id)} {#if index > 0} {/if} {/each}
{/if} {/if} {#if groupFrame} {@const frame = groupFrame} `${corner.x},${corner.y}`).join(' ')} fill="var(--primary)" fill-opacity="0.06" stroke="var(--primary)" stroke-width="1.5" stroke-dasharray={frame.locked ? '6 4' : undefined} /> {#if !frame.locked} {/if} {#if !frame.locked} {#each frame.corners as corner, index (index)} {/each} {/if} {/if} {#if snapGuides.x !== undefined}
{/if} {#if snapGuides.y !== undefined}
{/if} {#if marquee}
{/if}