Repository: gradio-app/gradio
File: js/json/shared/JSONNode.svelte (Svelte)

Code before:
		show_indices = false,
		interactive = true,
		ontoggle = () => {}
	}: {
		value: any;
		depth?: number;
		is_root?: boolean;
		is_last_item?: boolean;
		key?: string | number | null;
		open?: boolean;
		theme_mode?: "system" | "light" | "dark";
		show_indices?: boolean;
		interactive?: boolean;
		ontoggle?: (detail: { collapsed: boolean; depth: number }) => void;
	} = $props();

	let root_element = $state<HTMLElement>();
	let collapsed = $state(open ? false : depth >= 3);
	let child_nodes = $state<any[]>([]);

	// Update collapsed when open or depth props change
	$effect(() => {
		if (open) {
			collapsed = false;
		} else if (depth >= 3) {
			collapsed = true;
		}
	});

	function is_collapsible(val: any): boolean {
		return val !== null && (typeof val === "object" || Array.isArray(val));
	}

	async function toggle_collapse(): Promise<void> {
		collapsed = !collapsed;
		await tick();
		ontoggle({ collapsed, depth });
	}

	function get_collapsed_preview(val: any): string {
		if (Array.isArray(val)) return `Array(${val.length})`;
		if (typeof val === "object" && val !== null)
			return `Object(${Object.keys(val).length})`;
		return String(val);
	}

	$effect(() => {
		if (is_collapsible(value)) {
			child_nodes = Object.entries(value);
		} else {
			child_nodes = [];

Review comment (suggestion):
There's a logic discrepancy between the initialization and the $effect. The initialization on line 29 sets `collapsed = open ? false : depth >= 3`, which means when `open` is false and `depth < 3`, `collapsed` should be false.

However, the $effect on lines 33-39 doesn't handle this case - it only sets `collapsed = true` when `depth >= 3`, but doesn't set `collapsed = false` when `depth < 3` and `open` is false. This means if the `depth` prop changes from 3 to 2, `collapsed` won't be updated to false.

Add an `else` clause to handle the case where `open` is false and `depth < 3`: `else { collapsed = false; }`
```suggestion
			collapsed = true;
		} else {
			collapsed = false;
```

Code after:
		show_indices = false,
		interactive = true
	}: {
		value: any;
		depth?: number;
		is_root?: boolean;
		is_last_item?: boolean;
		key?: string | number | null;
		open?: boolean;
		theme_mode?: "system" | "light" | "dark";
		show_indices?: boolean;
		interactive?: boolean;
	} = $props();

	let root_element = $state<HTMLElement>();
	let collapsed = $state(open ? false : depth >= 3);
	let child_nodes = $state<any[]>([]);

	function is_collapsible(val: any): boolean {
		return val !== null && (typeof val === "object" || Array.isArray(val));
	}

	async function toggle_collapse(): Promise<void> {
		collapsed = !collapsed;
		await tick();
	}

	function get_collapsed_preview(val: any): string {
		if (Array.isArray(val)) return `Array(${val.length})`;
		if (typeof val === "object" && val !== null)
			return `Object(${Object.keys(val).length})`;
		return String(val);
	}

	$effect(() => {
		if (is_collapsible(value)) {
			child_nodes = Object.entries(value);
		} else {
			child_nodes = [];
		}
	});

	function updateLineNumbers(): void {
		if (!root_element) return;
		const lines = root_element.querySelectorAll(".line");
		lines.forEach((line, index) => {
			const line_number = line.querySelector(".line-number");
			if (line_number) {
				line_number.setAttribute("data-pseudo-content", (index + 1).toString());
				line_number?.setAttribute(
					"aria-roledescription",