{#if selectedView === 'trash'} Your trash {:else} {selectedTag === 'all' ? 'Filter by tag' : `#${selectedTag}`} {#if tagOptions.length > 0} {tagOptions.length} {/if} {#if tagsLoading} {:else} {/if}
{#if tagSearch} {/if}
{#if filteredTagOptions.length === 0}

{tagOptions.length === 0 ? 'No tags are in use yet.' : 'No tags match your search.'}

{:else} {#each filteredTagOptions as option (option.tag)} {/each} {/if}
{#if canModerate} {#snippet trigger(props)} {/snippet} {/if} {/if}
{ if (!value) return; handleSortChange(parseWorkshopSort(value)); }} > {SORT_OPTIONS.find((option) => option.id === selectedSort)?.label ?? 'Best match'} {#each SORT_OPTIONS as option (option.id)} {/each}
{#snippet trigger(props)} {/snippet}
{#if canModerate} {#snippet trigger(props)} {/snippet} {/if} {#if $currentUser} {#snippet trigger(props)} {/snippet} {/if} {#snippet trigger(props)} {/snippet}
{#if loading && themes.length === 0}

Connecting to EZPPFarm Community Workshop...

{:else if error && themes.length === 0}

Workshop Offline or Unreachable

{error}. Ensure the themes microservice container is running.

{:else if themes.length === 0}

No community themes found matching your search.

{:else}
{#each themes as theme (theme.id)} {@const localTheme = localThemeMap.get(theme.id.toLowerCase()) || localThemeMap.get(theme.name.toLowerCase())} {@const isInstalled = Boolean(localTheme)} {@const isActive = Boolean( localTheme && $active_custom_theme?.folder_name === localTheme.folder_name )} {@const isDownloading = activeThemeDownloads.has(theme.id)} {@const downloadPercent = themeDownloadProgress.get(theme.id) ?? 0} {@const isDeleted = Boolean(theme.deletedAt)} {@const hasUpdate = Boolean( !isDeleted && localTheme && localTheme.version !== theme.version )} {@const owned = isOwnedByCurrentUser(theme)} {@const isLegacy = theme.apiVersion === '1.0' || Boolean(theme.tags?.includes('legacy'))} {@const canReport = Boolean($currentUser) && !owned && !theme.isHidden && !theme.isQuarantined} {@const canFork = canReport && !isLegacy && theme.allowForking !== false} {@const showOverflowMenu = !isDeleted && (owned || canModerate || canReport)}
{theme.name} { // Fallback to default theme preview if server preview not loaded (e.currentTarget as HTMLImageElement).src = DefaultThemePreview; }} />
{#if theme.apiVersion === '1.0' || theme.tags?.includes('legacy')} {#snippet trigger(props)} Legacy v1 {/snippet} {:else if theme.hasExecutableCode} {#snippet trigger(props)} Svelte Code {/snippet} {/if} {#if theme.reviewStatus === 'pending'} {#snippet trigger(props)} In review {/snippet} {:else if theme.reviewStatus === 'rejected'} {#snippet trigger(props)} Rejected {/snippet} {/if} {#if theme.isHidden} {#snippet trigger(props)} Hidden {/snippet} {/if} {#if theme.isQuarantined} {#snippet trigger(props)} Taken down {/snippet} {/if} {#if isDeleted} {#snippet trigger(props)} Trash {/snippet} {/if}
v{theme.version}
{theme.author.name} { (e.currentTarget as HTMLElement).style.display = 'none'; }} /> {theme.author.name} {#if theme.author.role === 'owner'} {#snippet trigger(props)} Owner {/snippet} {:else if theme.author.role === 'moderator'} {#snippet trigger(props)} Moderator {/snippet} {:else if theme.author.role === 'supporter' || (!theme.author.role && theme.author.donor)} {#snippet trigger(props)} Supporter {/snippet} {/if}
{formatBytes(theme.fileSize)}

{theme.name}

{#if isDeleted} Deleted {:else} {#snippet trigger(props)} {/snippet} {/if}
{#if theme.description}

{theme.description}

{/if} {#if theme.tags && theme.tags.length > 0}
{#each theme.tags.slice(0, 3) as tag (tag)} {/each}
{/if} {#if theme.isHidden || theme.isQuarantined}
{#if theme.isHidden}

Hidden by {theme.hiddenByName ?? 'staff'} {#if theme.hiddenAt} · {formatModerationDate(theme.hiddenAt)} {/if}

{#if theme.hiddenReason}

{theme.hiddenReason}

{/if} {/if} {#if theme.isQuarantined}

Taken down by {theme.quarantinedByName ?? 'staff'} {#if theme.quarantinedAt} · {formatModerationDate(theme.quarantinedAt)} {/if}

{#if theme.quarantineReason}

{theme.quarantineReason}

{/if} {/if}
{/if} {#if theme.forkedFrom} {@const forkedFrom = theme.forkedFrom} {#snippet trigger(props)} {/snippet} {/if}
{theme.downloadCount} {#if isInstalled && !isActive} Installed {/if}
{#if isDownloading}
{downloadPercent}%
{:else if deletingId === theme.id} Deleting {:else if forkingId === theme.id} Forking {:else if moderatingId === theme.id} Updating {:else if isDeleted} {#if owned} {:else} Deleted {/if} {:else} {#if owned && !isLegacy && isInstalled} {:else if isActive} In use {:else if isInstalled && hasUpdate} {:else if isInstalled} {:else} {/if} {#if showOverflowMenu} {#snippet child({ props })} {/snippet} {#if owned} openTagEditor(theme)} > Edit tags {#if !isLegacy && isInstalled && hasUpdate} handleRefreshOwnedTheme(theme)} > Refresh installed copy {/if} {:else if canReport || canFork} {#if canReport} openReportDialog(theme)} > Report theme {/if} {#if canFork} handleForkTheme(theme)} > Fork theme {/if} {/if} {#if canModerate} {#if owned || canReport} {/if} {#if !isLegacy} handleToggleVisibility(theme)} > {#if theme.isHidden} Restore visibility {:else} Hide from workshop {/if} {/if} handleToggleQuarantine(theme)} disabled={quarantineId === theme.id} > {#if quarantineId === theme.id} Updating... {:else} {theme.isQuarantined ? 'Lift takedown' : 'Take down theme'} {/if} void openThemeHistory(theme.id, theme.name)} > Moderation history {/if} {#if owned} handleDeleteTheme(theme)} > Move to trash {/if} {/if} {/if}
{/each}
{#if page < totalPages}
{/if} {/if}
Moderation Themes awaiting review, open reports and recent staff activity.
{#snippet trigger(props)} {/snippet} {#snippet trigger(props)} {/snippet} {#snippet trigger(props)} {/snippet}
{#if moderationError}
{moderationError}
{/if}
{ if (!value) return; moderationStatus = value as ThemeReportStatus | 'all'; handleModerationFilterChange(); }} > {REPORT_STATUS_OPTIONS.find((option) => option.id === moderationStatus)?.label ?? 'Open reports'} {#each REPORT_STATUS_OPTIONS as option (option.id)} {/each} { if (!value) return; moderationCategory = value as ThemeReportCategory | 'all'; handleModerationFilterChange(); }} > {REPORT_CATEGORIES.find((category) => category.id === moderationCategory)?.label ?? 'All categories'} {#each REPORT_CATEGORIES as category (category.id)} {/each} { if (event.key === 'Enter') handleModerationFilterChange(); }} /> { if (event.key === 'Enter') handleModerationFilterChange(); }} /> { if (moderationFrom === value) return; moderationFrom = value; handleModerationFilterChange(); }} /> { if (moderationTo === value) return; moderationTo = value; handleModerationFilterChange(); }} />
{#if selectedReportIds.length > 0}
{selectedReportIds.length} report(s) selected
{/if} {#if moderationLoading && moderationReports.length === 0 && moderationEvents.length === 0}
Loading moderation data...
{:else}

Review queue {reviewTotal}

{#if reviewQueue.length === 0}

No themes are waiting for review.

{:else}
{#each reviewQueue as theme (theme.id)}

{theme.name}

by {theme.author.name} · v{theme.version} · {formatModerationDate(theme.createdAt)}

{#if theme.description}

{theme.description}

{/if}
{/each}
{/if}

Reports {moderationTotal}

{#if selectableReports.length > 0} {/if} {#if moderationReports.length === 0}

No reports match these filters.

{:else}
{#each moderationReports as report (report.id)}
{#if report.status === 'open' && !report.themeDeleted} toggleReportSelection(report.id)} aria-label={`Select report for ${report.themeName}`} /> {/if}

{report.themeName}

{reportCategoryLabel(report.category)} {#if report.themeHidden} Hidden {/if} {#if report.themeQuarantined} Taken down {/if} {#if report.themeDeleted} Trash {/if}

by {report.themeAuthorName} · reported by {report.reporterName}

{formatModerationDate(report.createdAt)}

{report.reason}

{#if report.status !== 'open'}

{report.status === 'resolved' ? 'Resolved' : 'Dismissed'} by {report.resolvedByName ?? 'Unknown'} {#if report.resolvedAt} · {formatModerationDate(report.resolvedAt)} {/if}

{#if report.resolutionNote}

Note: {report.resolutionNote}

{/if} {/if}
{#if report.status === 'open'} {/if}
{/each}
{#if moderationTotalPages > 1}
Page {moderationPage} of {moderationTotalPages}
{/if} {/if}

Recent activity

{ if (!value) return; moderationEventAction = value; handleAuditFilterChange(); }} > {AUDIT_ACTIONS.find((action) => action.id === moderationEventAction)?.label ?? 'All actions'} {#each AUDIT_ACTIONS as action (action.id)} {/each} { if (event.key === 'Enter') handleAuditFilterChange(); }} />
{#if moderationEvents.length === 0}

No moderation events yet.

{:else}
{#each moderationEvents as event (event.id)}

{event.actorName} · {moderationActionLabel(event.action)} · {event.themeName}

{formatModerationDate(event.createdAt)}
{#if event.reason} {#snippet trigger(props)}

{event.reason}

{/snippet}
{/if}
{/each}
{#if moderationEventsPage < moderationEventsTotalPages}
{/if} {/if}
{/if}
{#if announcementOpen}
{/if} {#if sourceDialogOpen}
{/if} {#if tagEditorTarget}
{/if} {#if quarantineTarget}
{/if} {#if reportTarget}
{/if} {#if historyOpen && historyTarget}
{/if} { if (!open) closeRestoreDialog(); }} > Restore theme? Restore {restoreTarget?.name} to the workshop? It will return with its existing visibility and moderation state. {#if trashActionError}

{trashActionError}

{/if}
{ if (!open) closePermanentDeleteDialog(); }} > Permanently delete theme? Permanently delete {purgeTarget?.name}? This removes the theme package, preview, likes, downloads and reports from the server. This cannot be undone. Installed copies on computers are not affected. {#if trashActionError}

{trashActionError}

{/if}