From 522e3a571793f8eed267a549694b398f9706b88f Mon Sep 17 00:00:00 2001 From: SCOPEDD Date: Tue, 29 Sep 2026 01:58:53 -0400 Subject: [PATCH] Allow for logos to be used as instance names --- crates/shared/src/lib.rs | 1 + launcher/src/lib/mock.ts | 6 +++--- launcher/src/lib/types.ts | 1 + launcher/src/pages/Home.svelte | 12 ++++++++++-- panel/server/src/db.rs | 4 ++++ panel/server/src/routes/admin.rs | 9 ++++++--- panel/server/src/store.rs | 4 ++++ panel/web/src/components/LauncherPreview.svelte | 9 +++++++-- panel/web/src/lib/types.ts | 1 + panel/web/src/pages/InstanceEditor.svelte | 1 + 10 files changed, 38 insertions(+), 10 deletions(-) diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 57bd258..7ab023b 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -332,6 +332,7 @@ pub struct InstanceSummary { pub description: String, pub icon_url: Option, pub banner_url: Option, + pub logo_url: Option, pub mc_version: String, pub loader: Loader, pub loader_version: Option, diff --git a/launcher/src/lib/mock.ts b/launcher/src/lib/mock.ts index e5e91fe..944766d 100644 --- a/launcher/src/lib/mock.ts +++ b/launcher/src/lib/mock.ts @@ -63,9 +63,9 @@ const manifest: Manifest = { custom_css: '', }, instances: [ - { id: 'survival-smp', name: 'Survival SMP', description: 'Our main survival world — season 4. Claims, shops & weekend events.', icon_url: null, banner_url: null, mc_version: '1.21.1', loader: 'fabric', loader_version: '0.16.9', revision: 3, server: { name: 'SCOPENET SMP', address: 'play.scopenet.gg', port: 25565, auto_join: true, inject: true }, memory: { min_mb: 2048, max_mb: 6144 }, jvm_args: '', featured: true, source_label: 'Modrinth · Fabulously Optimized 6.2', file_count: 48, total_size: 184_000_000 }, - { id: 'creative', name: 'Creative Build', description: 'Plots, WorldEdit and weekly build contests.', icon_url: null, banner_url: null, mc_version: '1.20.1', loader: 'vanilla', loader_version: null, revision: 1, server: { name: 'Creative', address: 'creative.scopenet.gg', port: 25565, auto_join: false, inject: true }, memory: { min_mb: 1024, max_mb: 4096 }, jvm_args: '', featured: false, source_label: 'Minecraft 1.20.1', file_count: 0, total_size: 0 }, - { id: 'modded', name: 'Create: Above & Beyond', description: 'Factory building with Create and friends.', icon_url: null, banner_url: null, mc_version: '1.20.1', loader: 'forge', loader_version: '1.20.1-47.3.0', revision: 2, server: null, memory: { min_mb: 4096, max_mb: 8192 }, jvm_args: '', featured: false, source_label: 'CurseForge · Create: Above & Beyond 1.4', file_count: 212, total_size: 612_000_000 }, + { id: 'survival-smp', name: 'Survival SMP', description: 'Our main survival world — season 4. Claims, shops & weekend events.', icon_url: null, banner_url: null, logo_url: null, mc_version: '1.21.1', loader: 'fabric', loader_version: '0.16.9', revision: 3, server: { name: 'SCOPENET SMP', address: 'play.scopenet.gg', port: 25565, auto_join: true, inject: true }, memory: { min_mb: 2048, max_mb: 6144 }, jvm_args: '', featured: true, source_label: 'Modrinth · Fabulously Optimized 6.2', file_count: 48, total_size: 184_000_000 }, + { id: 'creative', name: 'Creative Build', description: 'Plots, WorldEdit and weekly build contests.', icon_url: null, banner_url: null, logo_url: null, mc_version: '1.20.1', loader: 'vanilla', loader_version: null, revision: 1, server: { name: 'Creative', address: 'creative.scopenet.gg', port: 25565, auto_join: false, inject: true }, memory: { min_mb: 1024, max_mb: 4096 }, jvm_args: '', featured: false, source_label: 'Minecraft 1.20.1', file_count: 0, total_size: 0 }, + { id: 'modded', name: 'Create: Above & Beyond', description: 'Factory building with Create and friends.', icon_url: null, banner_url: null, logo_url: null, mc_version: '1.20.1', loader: 'forge', loader_version: '1.20.1-47.3.0', revision: 2, server: null, memory: { min_mb: 4096, max_mb: 8192 }, jvm_args: '', featured: false, source_label: 'CurseForge · Create: Above & Beyond 1.4', file_count: 212, total_size: 612_000_000 }, ], }; diff --git a/launcher/src/lib/types.ts b/launcher/src/lib/types.ts index 3db0150..723b464 100644 --- a/launcher/src/lib/types.ts +++ b/launcher/src/lib/types.ts @@ -8,6 +8,7 @@ export interface Instance { description: string; icon_url: string | null; banner_url: string | null; + logo_url: string | null; mc_version: string; loader: Loader; loader_version: string | null; diff --git a/launcher/src/pages/Home.svelte b/launcher/src/pages/Home.svelte index c969ba5..98fe0d5 100644 --- a/launcher/src/pages/Home.svelte +++ b/launcher/src/pages/Home.svelte @@ -4,7 +4,7 @@ import ServerStatus from '../components/ServerStatus.svelte'; import NewsFeed from '../components/NewsFeed.svelte'; import Console from '../components/Console.svelte'; - import { activeAccount, app, instances, selectedInstance } from '../lib/store.svelte'; + import { abs, activeAccount, app, instances, selectedInstance } from '../lib/store.svelte'; import { loaderName, shortLoaderVersion } from '../lib/format'; import { openUrl } from '../lib/tauri'; @@ -26,7 +26,13 @@ {loaderName[inst.loader]} {shortLoaderVersion(inst.mc_version, inst.loader_version)} {#if inst.file_count} {inst.file_count} mods & files{/if} -

{inst.name}

+ {#if inst.logo_url} +
+ +
+ {:else} +

{inst.name}

+ {/if}

{inst.description || b.tagline}

@@ -67,6 +73,8 @@ .greet { font-size: 0.88rem; } .greet strong { color: var(--text); font-weight: 560; } .chips { display: flex; gap: 0.45rem; flex-wrap: wrap; } + .logo-wrap { max-width: 100%; display: flex; align-items: flex-end; min-height: 3rem; margin: 0.25rem 0; } + .inst-logo { max-height: clamp(3.8rem, 8vw, 6.2rem); max-width: min(100%, 30rem); object-fit: contain; object-position: left bottom; filter: drop-shadow(0 4px 16px rgba(0, 0, 0, 0.5)); } h1 { font-size: clamp(2.2rem, 4.4vw, 3.2rem); line-height: 1.05; font-weight: 700; letter-spacing: -0.025em; } .desc { font-size: 0.95rem; line-height: 1.6; color: var(--muted); max-width: 32rem; } .actions { grid-column: 1; grid-row: 2; display: flex; flex-direction: column; gap: 0.8rem; animation: fade 0.3s ease both; } diff --git a/panel/server/src/db.rs b/panel/server/src/db.rs index 0456688..6cd9745 100644 --- a/panel/server/src/db.rs +++ b/panel/server/src/db.rs @@ -215,6 +215,10 @@ const MIGRATIONS: &[&str] = &[ WHEN NEW.uuid <> '' BEGIN INSERT OR IGNORE INTO reserved_usernames VALUES (NEW.username, NEW.uuid); END; "#, + // 5: instance logo url. + r#" + ALTER TABLE instances ADD COLUMN logo_url TEXT; + "#, ]; pub async fn connect(data_dir: &Path) -> Result { diff --git a/panel/server/src/routes/admin.rs b/panel/server/src/routes/admin.rs index fa85e36..340d4f8 100644 --- a/panel/server/src/routes/admin.rs +++ b/panel/server/src/routes/admin.rs @@ -349,6 +349,7 @@ pub struct InstanceInput { description: String, icon_url: Option, banner_url: Option, + logo_url: Option, mc_version: String, loader: Loader, loader_version: Option, @@ -403,15 +404,16 @@ pub async fn create_instance( let now = crate::db::now(); let mem = input.memory.unwrap_or_default(); sqlx::query( - "INSERT INTO instances (id, name, description, icon_url, banner_url, mc_version, loader, loader_version, source_kind, source_label, + "INSERT INTO instances (id, name, description, icon_url, banner_url, logo_url, mc_version, loader, loader_version, source_kind, source_label, visibility, allowed_groups, memory_min, memory_max, jvm_args, server, featured, enabled, sort, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'vanilla', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'vanilla', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(&id) .bind(&input.name) .bind(&input.description) .bind(&input.icon_url) .bind(&input.banner_url) + .bind(&input.logo_url) .bind(&input.mc_version) .bind(input.loader.as_str()) .bind(&input.loader_version) @@ -461,7 +463,7 @@ pub async fn update_instance( // admin explicitly changes them here. let label = if existing.source_kind == "vanilla" { format!("Minecraft {}", input.mc_version) } else { existing.source_label.clone() }; sqlx::query( - "UPDATE instances SET name = ?, description = ?, icon_url = ?, banner_url = ?, mc_version = ?, loader = ?, loader_version = ?, + "UPDATE instances SET name = ?, description = ?, icon_url = ?, banner_url = ?, logo_url = ?, mc_version = ?, loader = ?, loader_version = ?, source_label = ?, visibility = ?, allowed_groups = ?, memory_min = ?, memory_max = ?, jvm_args = ?, server = ?, featured = ?, enabled = ?, sort = ?, revision = revision + 1, updated_at = ? WHERE id = ?", ) @@ -469,6 +471,7 @@ pub async fn update_instance( .bind(&input.description) .bind(input.icon_url.filter(|s| !s.is_empty())) .bind(input.banner_url.filter(|s| !s.is_empty())) + .bind(input.logo_url.filter(|s| !s.is_empty())) .bind(&input.mc_version) .bind(input.loader.as_str()) .bind(&input.loader_version) diff --git a/panel/server/src/store.rs b/panel/server/src/store.rs index e93f15f..99663be 100644 --- a/panel/server/src/store.rs +++ b/panel/server/src/store.rs @@ -63,6 +63,7 @@ pub struct InstanceRow { pub description: String, pub icon_url: Option, pub banner_url: Option, + pub logo_url: Option, pub mc_version: String, pub loader: String, pub loader_version: Option, @@ -92,6 +93,7 @@ pub struct AdminInstance { pub description: String, pub icon_url: Option, pub banner_url: Option, + pub logo_url: Option, pub mc_version: String, pub loader: Loader, pub loader_version: Option, @@ -123,6 +125,7 @@ impl InstanceRow { description: self.description.clone(), icon_url: self.icon_url.clone(), banner_url: self.banner_url.clone(), + logo_url: self.logo_url.clone(), mc_version: self.mc_version.clone(), loader: Loader::parse(&self.loader).unwrap_or_default(), loader_version: self.loader_version.clone(), @@ -154,6 +157,7 @@ impl InstanceRow { description: a.description, icon_url: a.icon_url, banner_url: a.banner_url, + logo_url: a.logo_url, mc_version: a.mc_version, loader: a.loader, loader_version: a.loader_version, diff --git a/panel/web/src/components/LauncherPreview.svelte b/panel/web/src/components/LauncherPreview.svelte index 8613e46..cb6cda4 100644 --- a/panel/web/src/components/LauncherPreview.svelte +++ b/panel/web/src/components/LauncherPreview.svelte @@ -4,7 +4,7 @@ import { Play, Settings } from '@lucide/svelte'; import type { Branding } from '../lib/types'; - let { b, instanceName = 'Survival SMP' }: { b: Branding; instanceName?: string } = $props(); + let { b, instanceName = 'Survival SMP', instanceLogo = null }: { b: Branding; instanceName?: string; instanceLogo?: string | null } = $props(); const fontFamily = $derived(`'${b.font} Variable', '${b.font}', 'Inter Variable', sans-serif`); const media = $derived(b.background.kind !== 'gradient' && b.background.url ? b.background.url : null); @@ -53,7 +53,11 @@
1.21.1Fabric
-

{instanceName}

+ {#if instanceLogo} + + {:else} +

{instanceName}

+ {/if}

{b.tagline}

Play @@ -103,6 +107,7 @@ .avatar { width: 55%; aspect-ratio: 1; border-radius: 25%; background: #3fb97c; } .main { position: absolute; left: 6.2%; top: 5.6%; right: 0; bottom: 0; padding: 4% 4%; z-index: 1; display: flex; gap: 6%; } .hero { flex: 1; align-self: flex-end; display: flex; flex-direction: column; gap: 0.6em; } + .hero-logo { max-height: 2.8em; max-width: 80%; object-fit: contain; object-position: left bottom; } .hero h1 { font-size: 3.1em; line-height: 1.05; margin: 0; letter-spacing: -0.025em; font-weight: 700; } .hero p { color: var(--mut); font-size: 0.95em; margin: 0; } .chips { display: flex; gap: 0.4em; } diff --git a/panel/web/src/lib/types.ts b/panel/web/src/lib/types.ts index 704728b..8dc8a0b 100644 --- a/panel/web/src/lib/types.ts +++ b/panel/web/src/lib/types.ts @@ -7,6 +7,7 @@ export interface Instance { description: string; icon_url: string | null; banner_url: string | null; + logo_url: string | null; mc_version: string; loader: string; loader_version: string | null; diff --git a/panel/web/src/pages/InstanceEditor.svelte b/panel/web/src/pages/InstanceEditor.svelte index 5b0821e..d90efce 100644 --- a/panel/web/src/pages/InstanceEditor.svelte +++ b/panel/web/src/pages/InstanceEditor.svelte @@ -251,6 +251,7 @@ +