diff --git a/src/components/admin/studio/import-review.tsx b/src/components/admin/studio/import-review.tsx
index 1e662c730b..c82473fc16 100644
--- a/src/components/admin/studio/import-review.tsx
+++ b/src/components/admin/studio/import-review.tsx
@@ -19,7 +19,7 @@ import type { FurniItem } from "./studio-types";
import { useSourceAssetChecks } from "./use-source-asset-checks";
export function ImportReview({
- items: originalItems,
+ items,
sourceId,
source,
translation,
@@ -31,10 +31,8 @@ export function ImportReview({
source: string;
translation: string;
onCancel: () => void;
- onConfirm: (items: FurniItem[], replaced?: boolean) => void;
+ onConfirm: (items: FurniItem[]) => void;
}) {
- const [items, setItems] = useState(originalItems);
- const [replacements, setReplacements] = useState([]);
const inspection = useFurnitureInspection(
items.map((item) => item.classname),
);
@@ -112,22 +110,6 @@ export function ImportReview({
Selection: {items.length} furniture items
- {replacements.length > 0 && (
-
- Replacements selected: {replacements.join(" · ")}{" "}
- {
- setItems(originalItems);
- setReplacements([]);
- setStep("review");
- setPage(0);
- }}
- >
- Undo replacements
-
-
- )}
{assets.loading && !inspection.loading && (
Checking source files… {assets.items.length} checked
@@ -156,58 +138,10 @@ export function ImportReview({
Source files unavailable: {check.classname}
- This item will be skipped. Choose a reachable alternative below
- to replace it in this import.
+ The source has no downloadable files for this furniture. It
+ cannot be imported until its original .nitro or source files are
+ available.
- {check.alternatives.length === 0 && (
-
- No verified alternative found. Upload the original .nitro from
- the import page if you have it.
-
- )}
- {check.alternatives.map((alternative) => (
-
-
-
{alternative.name}
-
{alternative.classname}
-
- {alternative.type} · Revision {alternative.revision} · ID{" "}
- {alternative.id}
-
-
-
{
- setItems((current) => [
- ...new Map(
- current.map((item) => {
- const next =
- item.classname === check.classname
- ? {
- ...alternative,
- alreadyImported: false,
- nitroExists: false,
- }
- : item;
- return [next.classname, next] as const;
- }),
- ).values(),
- ]);
- setReplacements((current) => [
- ...current,
- `${check.classname} → ${alternative.classname}`,
- ]);
- setStep("review");
- setPage(0);
- }}
- >
- Use this alternative
-
-
- ))}
))}
{inspection.loading ? (
@@ -380,7 +314,7 @@ export function ImportReview({
if (step === "review") {
setStep("confirm");
setPage(0);
- } else onConfirm(ready, replacements.length > 0);
+ } else onConfirm(ready);
}}
>
{step === "review"
diff --git a/src/components/admin/studio/studio-client.tsx b/src/components/admin/studio/studio-client.tsx
index 01540b3261..05d552c1f8 100644
--- a/src/components/admin/studio/studio-client.tsx
+++ b/src/components/admin/studio/studio-client.tsx
@@ -2419,10 +2419,10 @@ export function StudioClient({
: "Off"
}
onCancel={() => setReview(null)}
- onConfirm={(accepted, replaced) => {
+ onConfirm={(accepted) => {
const single = review.single;
setReview(null);
- if (review.cloneAll && !replaced)
+ if (review.cloneAll)
void cloneAllMissing(accepted.map((item) => item.classname));
else if (single && accepted[0]) void importSingle(accepted[0]);
else
diff --git a/src/lib/furni/source-assets.ts b/src/lib/furni/source-assets.ts
index f0399bc0ee..4453c6fcfa 100644
--- a/src/lib/furni/source-assets.ts
+++ b/src/lib/furni/source-assets.ts
@@ -14,27 +14,3 @@ export interface SourceAssetCheck {
revision: number;
alternatives: SourceAssetItem[];
}
-export function relatedFurniture(
- item: SourceAssetItem,
- candidates: SourceAssetItem[],
-) {
- const tail = (name: string) =>
- name.toLowerCase().split("_").slice(-2).join("_");
- const label = (name: string) => (name ?? "").trim().toLowerCase();
- return candidates
- .filter(
- (candidate) =>
- candidate.classname !== item.classname &&
- candidate.type === item.type &&
- ((label(item.name) && label(candidate.name) === label(item.name)) ||
- (item.classname.split("_").length >= 3 &&
- tail(candidate.classname) === tail(item.classname))),
- )
- .sort(
- (a, b) =>
- Number(label(b.name) === label(item.name)) -
- Number(label(a.name) === label(item.name)) ||
- a.classname.localeCompare(b.classname),
- )
- .slice(0, 5);
-}
diff --git a/src/lib/services/furniture-source-assets.test.ts b/src/lib/services/furniture-source-assets.test.ts
index d2324ac68c..1cf05ed9fa 100644
--- a/src/lib/services/furniture-source-assets.test.ts
+++ b/src/lib/services/furniture-source-assets.test.ts
@@ -1,9 +1,6 @@
// @vitest-environment node
import { afterEach, describe, expect, it, vi } from "vitest";
-import {
- relatedFurniture,
- type SourceAssetItem,
-} from "@/lib/furni/source-assets";
+import type { SourceAssetItem } from "@/lib/furni/source-assets";
import { inspectSourceAssets, probeAsset } from "./furniture-source-assets";
const source: SourceAssetItem = {
@@ -23,7 +20,7 @@ const alternative = {
};
afterEach(() => vi.unstubAllGlobals());
describe("source asset preflight", () => {
- it("suggests reachable alternatives for unavailable source files", async () => {
+ it("keeps missing furniture unchanged even when a similar asset is available", async () => {
const fetchMock = vi.fn(
async (url: string) =>
new Response(null, { status: url.includes("/73257/") ? 200 : 404 }),
@@ -31,7 +28,9 @@ describe("source asset preflight", () => {
vi.stubGlobal("fetch", fetchMock);
const result = await inspectSourceAssets(source, [source, alternative]);
expect(result.state).toBe("missing");
- expect(result.alternatives).toEqual([alternative]);
+ expect(result.classname).toBe(source.classname);
+ expect(result.alternatives).toEqual([]);
+ expect(fetchMock).toHaveBeenCalledTimes(1);
});
it("does not describe blocked or unreachable servers as missing files", async () => {
vi.stubGlobal(
@@ -67,28 +66,6 @@ describe("source asset preflight", () => {
);
expect(result.state).toBe("available");
});
- it("bounds suggestions and excludes different furniture types", () => {
- expect(
- relatedFurniture(source, [source, { ...alternative, type: "wallitem" }]),
- ).toEqual([]);
- expect(
- relatedFurniture(
- source,
- Array.from({ length: 20 }, (_, id) => ({
- ...alternative,
- id,
- classname: `f${id}_monkeybzn_pole`,
- })),
- ),
- ).toHaveLength(5);
- });
- it("handles unnamed source entries while finding related assets", () => {
- expect(
- relatedFurniture(source, [
- { ...alternative, name: null as unknown as string },
- ]),
- ).toHaveLength(1);
- });
it("caches a probe to avoid repeating CDN requests", async () => {
const fetchMock = vi.fn(async () => new Response(null, { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
diff --git a/src/lib/services/furniture-source-assets.ts b/src/lib/services/furniture-source-assets.ts
index 90482fac5b..864bc4402b 100644
--- a/src/lib/services/furniture-source-assets.ts
+++ b/src/lib/services/furniture-source-assets.ts
@@ -3,7 +3,6 @@ import type {
SourceAssetCheck,
SourceAssetItem,
} from "@/lib/furni/source-assets";
-import { relatedFurniture } from "@/lib/furni/source-assets";
import type { CloneSource } from "./clone-sources";
import { browserHeaders } from "./import/core/browser-headers";
@@ -78,21 +77,11 @@ export async function inspectSourceAssets(
revision: item.revision || canonical?.revision || 0,
};
const state = await available(effective, source);
- const alternatives: SourceAssetItem[] = [];
- if (state === "missing") {
- for (const candidate of relatedFurniture(
- { ...effective, name: canonical?.name || item.name },
- official,
- )) {
- if ((await available(candidate)) === "available")
- alternatives.push(candidate);
- if (alternatives.length === 3) break;
- }
- }
+
return {
classname: item.classname,
revision: effective.revision,
state,
- alternatives,
+ alternatives: [],
};
}