Allow for logos to be used as instance names
This commit is contained in:
1 parent
a1f19e86c6
commit
522e3a5717
10 files changed
+36
-8
No files matched your search
@@ -332,6 +332,7 @@ pub struct InstanceSummary {
|
||||
pub description: String,
|
||||
pub icon_url: Option<String>,
|
||||
pub banner_url: Option<String>,
|
||||
pub logo_url: Option<String>,
|
||||
pub mc_version: String,
|
||||
pub loader: Loader,
|
||||
pub loader_version: Option<String>,
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 @@
|
||||
<span class="chip">{loaderName[inst.loader]} {shortLoaderVersion(inst.mc_version, inst.loader_version)}</span>
|
||||
{#if inst.file_count}<span class="chip"><Package size={12} /> {inst.file_count} mods & files</span>{/if}
|
||||
</div>
|
||||
{#if inst.logo_url}
|
||||
<div class="logo-wrap">
|
||||
<img class="inst-logo" src={abs(inst.logo_url)} alt={inst.name} />
|
||||
</div>
|
||||
{:else}
|
||||
<h1>{inst.name}</h1>
|
||||
{/if}
|
||||
<p class="desc">{inst.description || b.tagline}</p>
|
||||
</section>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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<SqlitePool> {
|
||||
|
||||
@@ -349,6 +349,7 @@ pub struct InstanceInput {
|
||||
description: String,
|
||||
icon_url: Option<String>,
|
||||
banner_url: Option<String>,
|
||||
logo_url: Option<String>,
|
||||
mc_version: String,
|
||||
loader: Loader,
|
||||
loader_version: Option<String>,
|
||||
@@ -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)
|
||||
|
||||
@@ -63,6 +63,7 @@ pub struct InstanceRow {
|
||||
pub description: String,
|
||||
pub icon_url: Option<String>,
|
||||
pub banner_url: Option<String>,
|
||||
pub logo_url: Option<String>,
|
||||
pub mc_version: String,
|
||||
pub loader: String,
|
||||
pub loader_version: Option<String>,
|
||||
@@ -92,6 +93,7 @@ pub struct AdminInstance {
|
||||
pub description: String,
|
||||
pub icon_url: Option<String>,
|
||||
pub banner_url: Option<String>,
|
||||
pub logo_url: Option<String>,
|
||||
pub mc_version: String,
|
||||
pub loader: Loader,
|
||||
pub loader_version: Option<String>,
|
||||
@@ -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,
|
||||
|
||||
@@ -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 @@
|
||||
<div class="main">
|
||||
<div class="hero">
|
||||
<div class="chips"><span>1.21.1</span><span>Fabric</span></div>
|
||||
{#if instanceLogo}
|
||||
<img class="hero-logo" src={instanceLogo} alt={instanceName} />
|
||||
{:else}
|
||||
<h1>{instanceName}</h1>
|
||||
{/if}
|
||||
<p>{b.tagline}</p>
|
||||
<div class="row">
|
||||
<span class="play"><Play size={9} fill="currentColor" /> Play</span>
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
<label class="field">Name<input bind:value={form.name} /></label>
|
||||
<label class="field">Description<textarea bind:value={form.description} rows="3" placeholder="What makes this instance special?"></textarea></label>
|
||||
<ImageInput bind:value={form.icon_url} label="Icon" help="Square image, shown in the launcher sidebar." />
|
||||
<ImageInput bind:value={form.logo_url} label="Logo (optional)" help="Wide logo or wordmark image used in place of the instance title text." />
|
||||
<ImageInput bind:value={form.banner_url} label="Banner / background" help="Wide image shown behind this instance. Overrides the launcher background." />
|
||||
</section>
|
||||
<section class="card col">
|
||||
|
||||
Reference in new issue
Block a user