diff --git a/src/app.css b/src/app.css index 189342c..0f27bd0 100644 --- a/src/app.css +++ b/src/app.css @@ -95,7 +95,8 @@ .image-drop-input{display:grid;grid-template-columns:64px 1fr auto;align-items:center;gap:10px;min-height:82px;padding:8px;border:1px dashed #4b5152;background:#0b0e0f;cursor:pointer;transition:border-color .15s,background .15s,transform .1s}.image-drop-input:hover,.image-drop-input:focus-visible,.image-drop-input.dragging{border-color:var(--shd-orange);background:#15130e;outline:none}.image-drop-input.dragging{transform:translateY(1px);box-shadow:inset 0 0 0 1px #f6a82155}.image-file-input{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important}.image-upload-preview{display:grid;place-items:center;width:64px;height:64px;border:1px solid #343a3b;background:#070909;color:#62696b;font-size:var(--shd-type-telemetry);text-align:center}.image-upload-preview img{display:block;width:100%;height:100%;object-fit:contain}.image-drop-copy{display:grid;gap:4px;min-width:0}.image-drop-copy b{color:#c6cbcc;font-size:var(--shd-type-telemetry);letter-spacing:1px}.image-drop-copy span{overflow:hidden;color:#747c7e;font-size:var(--shd-type-telemetry);text-overflow:ellipsis;white-space:nowrap}.image-upload-actions{display:flex;gap:5px}.image-upload-actions button{min-width:92px;padding:8px}.image-upload-error{display:block;margin-top:5px;color:var(--shd-danger)}@media(max-width:700px){.image-drop-input{grid-template-columns:56px 1fr}.image-upload-preview{width:56px;height:56px}.image-upload-actions{grid-column:1/-1}.image-upload-actions button{flex:1}} .icon-upload-field .image-drop-input{grid-template-columns:1fr;gap:6px;text-align:center}.icon-upload-field .image-upload-preview{width:100%;height:72px}.icon-upload-field .image-upload-preview img{width:64px;height:64px}.icon-upload-field .image-upload-actions{display:grid;grid-template-columns:1fr 1fr;width:100%}.icon-upload-field .image-upload-actions button{min-width:0;padding:7px 3px;font-size:var(--shd-type-telemetry)} .game-data-render-upload{margin:8px 0;padding:8px;border:1px solid var(--shd-border);background:#0d1011}.game-data-render-upload legend{padding:0 5px;color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.8px}.game-data-render-upload .image-drop-input{grid-template-columns:84px minmax(0,1fr) auto}.game-data-render-upload .image-upload-preview{width:84px;height:84px}.game-data-render-upload .image-upload-preview img{object-fit:cover}.game-data-render-upload .image-drop-copy{justify-self:stretch;text-align:center}.game-data-render-upload .render-remove{display:flex;gap:6px;align-items:center;margin-top:6px;color:var(--shd-danger);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.6px}.game-data-render-upload .render-remove input{width:auto;margin:0;accent-color:var(--shd-danger)}.game-data-render-upload>small{display:block;margin-top:6px;color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);line-height:1.35} -.registry-render-identity{display:flex;align-items:center;gap:8px;min-width:0}.registry-render-identity>div{display:grid;gap:2px;min-width:0}.registry-render{width:42px;height:42px;flex:0 0 auto;object-fit:cover;border:1px solid var(--shd-border);background:#090b0c} +.dashboard-row>.registry-render-identity{display:flex;align-items:center;gap:8px;min-width:0}.registry-render-identity>div{display:grid;gap:2px;min-width:0}.registry-render{width:42px;height:42px;flex:0 0 auto;object-fit:cover;border:1px solid var(--shd-border);background:#090b0c} +.database-item-card header:has(.database-item-render){grid-template-columns:64px 42px minmax(0,1fr) auto}.database-item-render{display:block;width:64px;height:64px;object-fit:cover;border:1px solid var(--shd-border);background:#090b0c} .proposal-submitted,.data-access-banner{display:flex;align-items:center;gap:12px;margin-bottom:9px;padding:9px 11px;border-left:3px solid var(--shd-success);background:#101713;color:#a6cbb0}.proposal-submitted b,.data-access-banner b{font-size:var(--shd-type-telemetry);letter-spacing:1px}.proposal-submitted span,.data-access-banner span{font-size:var(--shd-type-telemetry);color:#7e9d86}.data-access-banner{border-left-color:#657174;background:#111617;color:#aeb7b9}.data-access-banner.contributor{border-left-color:var(--shd-orange);background:#19150e;color:#d8bc87}.data-access-error{margin:-3px 0 9px}.data-readonly form[method=post] button[type=submit],.data-readonly form[method=post] button:not([type]){filter:grayscale(1);opacity:.45}.admin-sidebar nav a.has-pending{grid-template-columns:minmax(0,1fr) 18px 18px}.admin-sidebar nav a.has-pending em{grid-column:2;display:grid;place-items:center;min-width:18px;height:18px;padding:0 5px;border:1px solid #a26c19;background:#211a0e;color:#ffc45d;font-size:var(--shd-type-telemetry);font-style:normal;font-weight:900;text-align:center}.admin-sidebar nav a.has-pending b{grid-column:3;justify-self:end}.admin-sidebar nav a.has-pending:hover em,.admin-sidebar nav a.has-pending.active em{border-color:var(--shd-orange);background:#2b2111;color:#fff1d0}.moderation-row{grid-template-columns:minmax(240px,1.5fr) minmax(140px,.8fr) 150px 80px 75px}.moderation-review{width:min(1120px,100%);max-height:calc(100vh - 32px);overflow:auto}.moderation-metadata{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin:10px 0}.moderation-metadata span{display:grid;gap:4px;padding:8px;border:1px solid var(--shd-border);color:var(--shd-text-muted);font-size:var(--shd-type-telemetry)}.moderation-metadata b{color:var(--shd-text);font-size:var(--shd-type-telemetry)}.moderation-review details{margin:10px 0;border:1px solid var(--shd-border);background:#0b0e0f}.moderation-review summary{padding:9px;color:var(--shd-orange);font-size:var(--shd-type-telemetry);cursor:pointer}.moderation-review pre{max-height:260px;overflow:auto;margin:0;padding:10px;border-top:1px solid var(--shd-border);color:#9ca6a8;font-size:var(--shd-type-telemetry);white-space:pre-wrap}.proposal-change-preview{margin-top:10px}.proposal-preview-head{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;border:1px solid var(--shd-border);background:#0d1011}.proposal-preview-head>div{display:grid;gap:3px}.proposal-preview-head span{font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:1px}.proposal-preview-head small{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry)}.proposal-preview-head>b{color:var(--shd-orange);font-size:var(--shd-type-telemetry)}.proposal-compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:7px}.proposal-editor-preview{min-width:0;border:1px solid var(--shd-border);background:#111415}.proposal-editor-preview.before{border-top:2px solid #596164}.proposal-editor-preview.after{border-top:2px solid var(--shd-orange)}.proposal-editor-preview>header{display:flex;align-items:center;justify-content:space-between;min-height:32px;padding:6px 9px;border-bottom:1px solid var(--shd-border);background:#0d1011}.proposal-editor-preview>header span,.proposal-editor-preview>header b{font-size:var(--shd-type-telemetry);letter-spacing:.9px}.proposal-editor-preview>header b{color:var(--shd-text-muted)}.proposal-editor-preview.after>header b{color:var(--shd-orange)}.proposal-attribute-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;padding:8px}.preview-field{min-width:0;display:grid;align-content:start;gap:5px}.preview-field.identity{grid-column:span 4}.preview-field>span{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);letter-spacing:.8px}.preview-field>strong,.preview-field.range>div{min-height:34px;display:flex;align-items:center;padding:7px 8px;border:1px solid #363b3c;background:#0d0f10;color:var(--shd-text);font-size:var(--shd-type-telemetry);overflow-wrap:anywhere}.preview-field.identity>strong{min-height:43px;font-size:13px}.preview-field.range{grid-column:span 2}.preview-field.range>div{display:grid;grid-template-columns:1fr auto 1fr;gap:6px}.preview-field.range strong:last-child{text-align:right}.preview-field.range i{color:#555d5f;font-size:var(--shd-type-telemetry);font-style:normal}.proposal-empty-state{min-height:180px;display:grid;place-content:center;gap:6px;text-align:center}.proposal-empty-state b{color:var(--shd-orange);font-size:var(--shd-type-telemetry);letter-spacing:1px}.proposal-empty-state span{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry)}.proposal-editor-preview.generic{margin-top:7px}.proposal-field-summary{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:8px}.proposal-field-summary>div{min-width:0;display:grid;align-content:start;gap:5px;padding:8px;border:1px solid #303536;background:#0d0f10}.proposal-field-summary span{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);letter-spacing:.8px}.proposal-field-summary strong{color:var(--shd-text);font-size:var(--shd-type-telemetry);overflow-wrap:anywhere}.proposal-field-summary small{max-height:42px;overflow:hidden;color:#687174;font-size:var(--shd-type-telemetry);overflow-wrap:anywhere}.decision-tabs{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin:10px 0}.decision-tabs button{padding:9px;border:1px solid var(--shd-border);background:#111415;color:var(--shd-text-muted)}.decision-tabs button.active{border-color:var(--shd-orange);color:var(--shd-orange)}@media(max-width:800px){.moderation-row{grid-template-columns:1fr}.moderation-metadata,.proposal-compare-grid{grid-template-columns:1fr}.proposal-field-summary{grid-template-columns:1fr 1fr}}@media(max-width:520px){.proposal-attribute-grid,.proposal-field-summary{grid-template-columns:1fr}.preview-field.identity,.preview-field.range{grid-column:auto}} .account-tabs{width:100%;height:auto;margin:0 0 16px;display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--shd-border)}.account-tabs button,.account-subtabs button{min-width:0;height:34px;padding:0 5px;border:0;border-left:1px solid var(--shd-border);border-bottom:2px solid transparent;background:#0d1011;color:#6f7779;font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.8px;cursor:pointer;transition:var(--shd-transition)}.account-tabs button:first-child,.account-subtabs button:first-child{border-left:0}.account-tabs button:hover,.account-tabs button:focus-visible,.account-subtabs button:hover,.account-subtabs button:focus-visible{color:var(--shd-text);background:#171b1c}.account-tabs button.active,.account-subtabs button.active{border-bottom-color:var(--shd-orange);background:#211a0e;color:var(--shd-orange)}.account-tabs button:active,.account-subtabs button:active{transform:translateY(1px);filter:brightness(.85)}.account-tabs button:focus-visible,.account-subtabs button:focus-visible{position:relative;z-index:1;outline:2px solid var(--shd-orange);outline-offset:2px}.account-panel{min-height:290px}.account-subtabs{display:grid;grid-template-columns:1fr 1fr;margin:0 0 10px;border:1px solid var(--shd-border)}.account-subtabs button{height:31px}.access-subtabs{display:grid;grid-template-columns:repeat(4,1fr);margin:-2px 0 17px;border:1px solid var(--shd-border);background:#0b0e0f}.access-subtabs button{min-width:0;height:42px;display:grid;place-content:center;gap:3px;padding:4px;border:0;border-left:1px solid var(--shd-border);border-bottom:2px solid transparent;background:transparent;color:var(--shd-text-dim);cursor:pointer;transition:var(--shd-transition)}.access-subtabs button:first-child{border-left:0}.access-subtabs button span{font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.7px}.access-subtabs button b{font-size:var(--shd-type-telemetry)}.access-subtabs button:hover,.access-subtabs button:focus-visible{background:#171b1c;color:var(--shd-text)}.access-subtabs button.active{border-bottom-color:var(--shd-orange);background:#211a0e;color:var(--shd-orange)}.access-subtabs button:active{transform:translateY(1px);filter:brightness(.85)}.access-subtabs button:focus-visible{position:relative;z-index:1;outline:2px solid var(--shd-orange);outline-offset:2px}.account-retention{color:var(--shd-text-muted);font:12px/1.55 var(--shd-font-editorial)}.danger-eyebrow{color:var(--shd-danger)} .account-alert.error-alert{background:#261513;border-color:#75443b;border-left-color:#df8069}.account-alert.error-alert b{color:#df8069}.account-alert.error-alert span{color:#d2aaa1} @@ -239,6 +240,8 @@ /* Public talent cards share one readable tier hierarchy in registries and quick views. */ .database-item-card.talent-card>header{min-height:86px} + +@media(max-width:700px){.database-item-card>header:has(.database-item-render){grid-template-columns:52px 42px minmax(0,1fr);gap:8px}.database-item-card>header:has(.database-item-render) .database-item-render{width:52px;height:52px}.database-item-card>header:has(.database-item-render) .database-header-status{grid-column:3;position:static;justify-self:end}} .database-item-card.talent-card h2{font-size:18px;line-height:1.1} .database-item-card.talent-card>header small,.database-item-card.talent-card .database-item-lines span{color:var(--shd-text-muted)} .database-talent-tiers{display:grid;gap:var(--shd-gap-sm);margin:10px 12px 0} diff --git a/src/lib/components/DatabaseItemCard.svelte b/src/lib/components/DatabaseItemCard.svelte index 74f3b5c..c6a1562 100644 --- a/src/lib/components/DatabaseItemCard.svelte +++ b/src/lib/components/DatabaseItemCard.svelte @@ -15,6 +15,7 @@
+ {#if item.renderUrl}{/if}
{#if item.gameIcon}{:else}{/if}
diff --git a/src/lib/database/items.ts b/src/lib/database/items.ts index 6226970..6afac60 100644 --- a/src/lib/database/items.ts +++ b/src/lib/database/items.ts @@ -17,6 +17,7 @@ export type DatabaseItem = { kicker: string; variant?: 'talent'; iconUrl?: string; + renderUrl?: string; gameIcon?: GameIconName; headerBadge?: string; headerMetaIcon?: GameIconName; diff --git a/src/routes/database/[category]/+page.server.ts b/src/routes/database/[category]/+page.server.ts index 5d824df..b9f1583 100644 --- a/src/routes/database/[category]/+page.server.ts +++ b/src/routes/database/[category]/+page.server.ts @@ -3,6 +3,7 @@ import type { DatabaseFact, DatabaseItem } from '$lib/database/items'; import type { GameIconName } from '$lib/game-icons'; import { weaponBaseRange } from '$lib/builder/loadout'; import { gameDataIconUrl, type GameDataIconType } from '$lib/server/game-data-icons'; +import { managedImageUrl } from '$lib/server/managed-images'; import { getDatabase } from '$lib/server/database'; import { getPagination, getRegistrySearch } from '$lib/server/pagination'; import { error } from '@sveltejs/kit'; @@ -146,7 +147,10 @@ export const load: PageServerLoad = async ({ params, url }) => { } const where = conditions.join(' AND '); countSql = `SELECT COUNT(*) AS total FROM gear item WHERE ${where}`; - rowsSql = `SELECT item.* FROM gear item WHERE ${where} + rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at + FROM gear item + LEFT JOIN managed_images render ON render.source_entity_type='gear_render' AND render.source_entity_id=item.id + WHERE ${where} ORDER BY item.slot,item.tier,item.name COLLATE NOCASE LIMIT ? OFFSET ?`; filters = [ { @@ -208,13 +212,14 @@ export const load: PageServerLoad = async ({ params, url }) => { } const where = conditions.join(' AND '); countSql = `SELECT COUNT(*) AS total FROM weapons item WHERE ${where}`; - rowsSql = `SELECT item.*, + rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at, native.damage_min,native.damage_max,native.reference_level, (SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=1) AS talent_1_count, (SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=2) AS talent_2_count, (SELECT COUNT(*) FROM weapon_category_attribute_bonuses bonuses WHERE bonuses.weapon_category=item.category) AS bonus_count FROM weapons item LEFT JOIN weapon_tier_ranges native ON native.weapon_id=item.id AND native.tier=item.native_tier + LEFT JOIN managed_images render ON render.source_entity_type='weapon_render' AND render.source_entity_id=item.id WHERE ${where} ORDER BY item.is_exotic DESC,item.category,item.name COLLATE NOCASE LIMIT ? OFFSET ?`; filters = [ @@ -481,6 +486,7 @@ export const load: PageServerLoad = async ({ params, url }) => { ]; return { id: String(row.id), name: String(row.name), kicker: `${row.slot} // T${row.tier}`, + renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '', gameIcon: gearIcon(row.slot), badges: [{ label: `T${row.tier}` }, { label: `${Number(row.tier) === 0 ? 1 : 2} MOD SLOT${Number(row.tier) === 0 ? '' : 'S'}` }], facts: [ @@ -507,6 +513,7 @@ export const load: PageServerLoad = async ({ params, url }) => { const level50Damage = row.damage_min === null ? null : weaponBaseRange(row.damage_min, row.damage_max, 50); return { id: String(row.id), name: String(row.name), kicker: `${row.category} // ${row.damage_type}`, + renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '', gameIcon: weaponIcon(row.category), headerBadge: `T${row.native_tier}`, headerMetaIcon: weaponIcon(row.damage_type), diff --git a/src/routes/isac/gear/+page.svelte b/src/routes/isac/gear/+page.svelte index 1ddc3dc..6655e35 100644 --- a/src/routes/isac/gear/+page.svelte +++ b/src/routes/isac/gear/+page.svelte @@ -21,6 +21,7 @@ let editing = $state(null); let augmentEditing = $state(null); let handledResultToken = $state(''); + let renderResetToken = $state(0); const compatibleBrands = (ids: string[]) => ids.map((id) => data.brands.find((brand) => brand.id === id)).filter(Boolean); const stat = (id: string) => data.stats.find((item) => item.id === id); const format = (value: number | string, statId: string) => { @@ -63,6 +64,7 @@ } if (form?.gearSaved && form.resultToken && form.resultToken !== handledResultToken) { handledResultToken = form.resultToken; + if (form.gearMode === 'add') renderResetToken += 1; editing = form.gearMode === 'add' ? blank() : null; } }); @@ -79,7 +81,7 @@ changeSlot(values[0])} required/> - +
HPBASE HEALTH
ARBASE ARMOR
diff --git a/src/routes/isac/weapons/+page.svelte b/src/routes/isac/weapons/+page.svelte index 499e52e..fe107bf 100644 --- a/src/routes/isac/weapons/+page.svelte +++ b/src/routes/isac/weapons/+page.svelte @@ -28,6 +28,7 @@ let editing = $state(null); let bonusEditing = $state(null); let handledResultToken = $state(''); + let renderResetToken = $state(0); let openedRequestedWeapon = $state(false); const edit = (weapon: any) => { const tiers = [0, 1, 2].map((tier) => { @@ -94,6 +95,7 @@ } if (form?.weaponSaved && form.resultToken && form.resultToken !== handledResultToken) { handledResultToken = form.resultToken; + if (form.weaponMode === 'add') renderResetToken += 1; editing = form.weaponMode === 'add' ? blank() : null; } if (form?.weaponBonusProfileSaved && form.resultToken && form.resultToken !== handledResultToken) { @@ -147,7 +149,7 @@
- + {#if Number(editing.is_exotic)}
DEDICATED EXOTIC TALENTSELECT ONE TALENT FROM THE EXOTIC WEAPON REGISTRY
{editing.exotic_talent_id ? '1 / 1 ASSIGNED' : 'REQUIRED'}