style: format code with prettier

This commit is contained in:
openhands
2026-07-12 21:07:34 +02:00
parent e85e4d74ea
commit 2e4ed76121
378 changed files with 22404 additions and 20686 deletions
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,8 @@
'use client'
"use client";
import { Check, ChevronsUpDown, Star } from 'lucide-react'
import { useMemo, useState } from 'react'
import { Button } from '@/components/ui/button'
import { Check, ChevronsUpDown, Star } from "lucide-react";
import { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
@@ -10,11 +10,11 @@ import {
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command'
import { Label } from '@/components/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Switch } from '@/components/ui/switch'
import { cn } from '@/lib/utils'
} from "@/components/ui/command";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";
// ── Toggle field ──────────────────────────────────────────────
@@ -25,21 +25,21 @@ export function ToggleField({
onChange,
disabled,
}: {
label: string
label: string;
/** Boolean checked state */
checked?: boolean
checked?: boolean;
/** Number or string (0/1 or '0'/'1') — converted to boolean automatically */
value?: number | string
onChange: (v: boolean) => void
disabled?: boolean
value?: number | string;
onChange: (v: boolean) => void;
disabled?: boolean;
}) {
const isChecked = checked ?? (value === 1 || value === '1')
const isChecked = checked ?? (value === 1 || value === "1");
return (
<div className="flex items-center justify-between gap-2">
<Label className="text-xs">{label}</Label>
<Switch checked={isChecked} onCheckedChange={onChange} disabled={disabled} size="sm" />
</div>
)
);
}
// ── Interaction Type Combobox ──────────────────────────────────
@@ -50,19 +50,19 @@ export function InteractionTypeCombobox({
types,
disabled,
}: {
value: string
onChange: (v: string) => void
types: string[]
disabled?: boolean
value: string;
onChange: (v: string) => void;
types: string[];
disabled?: boolean;
}) {
const [open, setOpen] = useState(false)
const [inputValue, setInputValue] = useState('')
const [open, setOpen] = useState(false);
const [inputValue, setInputValue] = useState("");
const filtered = useMemo(() => {
if (!inputValue.trim()) return types.slice(0, 50)
const q = inputValue.toLowerCase()
return types.filter((t) => t.toLowerCase().includes(q)).slice(0, 50)
}, [types, inputValue])
if (!inputValue.trim()) return types.slice(0, 50);
const q = inputValue.toLowerCase();
return types.filter((t) => t.toLowerCase().includes(q)).slice(0, 50);
}, [types, inputValue]);
return (
<Popover open={open} onOpenChange={setOpen}>
@@ -74,7 +74,7 @@ export function InteractionTypeCombobox({
disabled={disabled}
className="w-full justify-between font-normal h-8 text-sm"
>
<span className="truncate">{value || 'Select type...'}</span>
<span className="truncate">{value || "Select type..."}</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
@@ -92,15 +92,15 @@ export function InteractionTypeCombobox({
type="button"
className="w-full px-2 py-1.5 text-left text-sm hover:bg-accent rounded"
onClick={() => {
onChange(inputValue.trim())
setOpen(false)
setInputValue('')
onChange(inputValue.trim());
setOpen(false);
setInputValue("");
}}
>
Use &quot;{inputValue.trim()}&quot;
</button>
) : (
'No types found.'
"No types found."
)}
</CommandEmpty>
<CommandGroup>
@@ -109,14 +109,12 @@ export function InteractionTypeCombobox({
key={t}
value={t}
onSelect={() => {
onChange(t)
setOpen(false)
setInputValue('')
onChange(t);
setOpen(false);
setInputValue("");
}}
>
<Check
className={cn('mr-2 h-4 w-4', value === t ? 'opacity-100' : 'opacity-0')}
/>
<Check className={cn("mr-2 h-4 w-4", value === t ? "opacity-100" : "opacity-0")} />
<span className="truncate">{t}</span>
</CommandItem>
))}
@@ -125,7 +123,7 @@ export function InteractionTypeCombobox({
</Command>
</PopoverContent>
</Popover>
)
);
}
// ── Move Page Combobox with smart suggestions ─────────────────
@@ -137,35 +135,32 @@ export function MovePageCombobox({
currentPageId,
suggestedPageIds,
}: {
value: number | null
onChange: (id: number | null) => void
pages: { id: number; caption: string }[]
currentPageId: number
value: number | null;
onChange: (id: number | null) => void;
pages: { id: number; caption: string }[];
currentPageId: number;
/** Page IDs to show first as suggestions (from move-suggestions.ts) */
suggestedPageIds?: number[]
suggestedPageIds?: number[];
}) {
const [open, setOpen] = useState(false)
const [open, setOpen] = useState(false);
const options = useMemo(() => pages.filter((p) => p.id !== currentPageId), [pages, currentPageId])
const options = useMemo(() => pages.filter((p) => p.id !== currentPageId), [pages, currentPageId]);
const selectedPage = value ? pages.find((p) => p.id === value) : null
const selectedPage = value ? pages.find((p) => p.id === value) : null;
// Build suggested pages list
const suggested = useMemo(() => {
if (!suggestedPageIds?.length) return []
const pageMap = new Map(options.map((p) => [p.id, p]))
if (!suggestedPageIds?.length) return [];
const pageMap = new Map(options.map((p) => [p.id, p]));
return suggestedPageIds.map((id) => pageMap.get(id)).filter(Boolean) as {
id: number
caption: string
}[]
}, [suggestedPageIds, options])
id: number;
caption: string;
}[];
}, [suggestedPageIds, options]);
// Non-suggested pages (exclude those already in suggestions)
const suggestedSet = useMemo(() => new Set(suggested.map((s) => s.id)), [suggested])
const remaining = useMemo(
() => options.filter((p) => !suggestedSet.has(p.id)),
[options, suggestedSet],
)
const suggestedSet = useMemo(() => new Set(suggested.map((s) => s.id)), [suggested]);
const remaining = useMemo(() => options.filter((p) => !suggestedSet.has(p.id)), [options, suggestedSet]);
return (
<Popover open={open} onOpenChange={setOpen}>
@@ -178,9 +173,7 @@ export function MovePageCombobox({
>
{selectedPage ? (
<span className="truncate">
<span className="text-muted-foreground font-mono text-xs mr-1.5">
#{selectedPage.id}
</span>
<span className="text-muted-foreground font-mono text-xs mr-1.5">#{selectedPage.id}</span>
{selectedPage.caption}
</span>
) : (
@@ -203,20 +196,13 @@ export function MovePageCombobox({
key={`s-${page.id}`}
value={`${page.id} ${page.caption}`}
onSelect={() => {
onChange(page.id)
setOpen(false)
onChange(page.id);
setOpen(false);
}}
>
<Check
className={cn(
'mr-2 h-4 w-4',
value === page.id ? 'opacity-100' : 'opacity-0',
)}
/>
<Check className={cn("mr-2 h-4 w-4", value === page.id ? "opacity-100" : "opacity-0")} />
<Star className="mr-1.5 h-3 w-3 text-[var(--admin-warning)]" />
<span className="text-muted-foreground font-mono text-xs mr-1.5">
#{page.id}
</span>
<span className="text-muted-foreground font-mono text-xs mr-1.5">#{page.id}</span>
<span className="truncate">{page.caption}</span>
</CommandItem>
))}
@@ -224,19 +210,17 @@ export function MovePageCombobox({
)}
{/* All pages */}
<CommandGroup heading={suggested.length > 0 ? 'All pages' : undefined}>
<CommandGroup heading={suggested.length > 0 ? "All pages" : undefined}>
{remaining.map((page) => (
<CommandItem
key={page.id}
value={`${page.id} ${page.caption}`}
onSelect={() => {
onChange(page.id)
setOpen(false)
onChange(page.id);
setOpen(false);
}}
>
<Check
className={cn('mr-2 h-4 w-4', value === page.id ? 'opacity-100' : 'opacity-0')}
/>
<Check className={cn("mr-2 h-4 w-4", value === page.id ? "opacity-100" : "opacity-0")} />
<span className="text-muted-foreground font-mono text-xs mr-1.5">#{page.id}</span>
<span className="truncate">{page.caption}</span>
</CommandItem>
@@ -246,5 +230,5 @@ export function MovePageCombobox({
</Command>
</PopoverContent>
</Popover>
)
);
}
@@ -1,7 +1,7 @@
'use client'
"use client";
import { Image as ImageIcon } from 'lucide-react'
import { getHabboCdnIconUrl, getLocalIconUrl } from '@/lib/furni/classname'
import { Image as ImageIcon } from "lucide-react";
import { getHabboCdnIconUrl, getLocalIconUrl } from "@/lib/furni/classname";
export function FurniIcon({ classname, revision }: { classname: string; revision: number }) {
if (!classname) {
@@ -9,20 +9,20 @@ export function FurniIcon({ classname, revision }: { classname: string; revision
<div className="w-10 h-10 rounded bg-muted flex items-center justify-center">
<ImageIcon className="h-4 w-4 text-muted-foreground" />
</div>
)
);
}
const localUrl = getLocalIconUrl(classname)
const localUrl = getLocalIconUrl(classname);
function handleError(e: React.SyntheticEvent<HTMLImageElement>) {
const img = e.target as HTMLImageElement
const stage = img.dataset.fallback
const img = e.target as HTMLImageElement;
const stage = img.dataset.fallback;
if (!stage && revision > 0) {
img.dataset.fallback = 'cdn'
img.src = getHabboCdnIconUrl(classname, revision)
img.dataset.fallback = "cdn";
img.src = getHabboCdnIconUrl(classname, revision);
} else {
img.dataset.fallback = 'none'
img.style.opacity = '0'
img.dataset.fallback = "none";
img.style.opacity = "0";
}
}
@@ -33,10 +33,10 @@ export function FurniIcon({ classname, revision }: { classname: string; revision
src={localUrl}
alt={classname}
className="max-w-full max-h-full object-contain"
style={{ imageRendering: 'pixelated' }}
style={{ imageRendering: "pixelated" }}
onError={handleError}
loading="lazy"
/>
</div>
)
);
}
@@ -1,4 +1,4 @@
export { CatalogItemsTable } from './catalog-items-table'
export { FurniIcon } from './furni-icon'
export type { BaseItemData, CatalogItemData, InlineEdit } from './types'
export { DEFAULT_NEW_ITEM, getPointsLabel, POINTS_TYPES } from './types'
export { CatalogItemsTable } from "./catalog-items-table";
export { FurniIcon } from "./furni-icon";
export type { BaseItemData, CatalogItemData, InlineEdit } from "./types";
export { DEFAULT_NEW_ITEM, getPointsLabel, POINTS_TYPES } from "./types";
@@ -1,59 +1,53 @@
'use client'
"use client";
import { Loader2, Music } from 'lucide-react'
import { useEffect, useState } from 'react'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Loader2, Music } from "lucide-react";
import { useEffect, useState } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
getCachedSongPickerOptions,
loadSongPickerOptions,
type SoundtrackOption,
} from '@/lib/client-cache/song-picker-cache'
} from "@/lib/client-cache/song-picker-cache";
// Re-export so existing `import { invalidateSongPickerCache } from '.../song-picker'`
// callsites keep working. New code should import from the lib module directly.
export { invalidateSongPickerCache } from '@/lib/client-cache/song-picker-cache'
export { invalidateSongPickerCache } from "@/lib/client-cache/song-picker-cache";
interface Props {
value: number
onChange: (id: number) => void
disabled?: boolean
value: number;
onChange: (id: number) => void;
disabled?: boolean;
}
function formatLength(seconds: number): string {
if (!seconds || seconds <= 0) return ''
const m = Math.floor(seconds / 60)
const s = seconds % 60
return `${m}:${String(s).padStart(2, '0')}`
if (!seconds || seconds <= 0) return "";
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${m}:${String(s).padStart(2, "0")}`;
}
export function SongPicker({ value, onChange, disabled }: Props) {
const initial = getCachedSongPickerOptions()
const [options, setOptions] = useState<SoundtrackOption[]>(initial ?? [])
const [loading, setLoading] = useState(!initial)
const initial = getCachedSongPickerOptions();
const [options, setOptions] = useState<SoundtrackOption[]>(initial ?? []);
const [loading, setLoading] = useState(!initial);
useEffect(() => {
if (getCachedSongPickerOptions()) return
let cancelled = false
if (getCachedSongPickerOptions()) return;
let cancelled = false;
loadSongPickerOptions()
.then((items) => {
if (!cancelled) setOptions(items)
if (!cancelled) setOptions(items);
})
.catch(() => {
if (!cancelled) setOptions([])
if (!cancelled) setOptions([]);
})
.finally(() => {
if (!cancelled) setLoading(false)
})
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true
}
}, [])
cancelled = true;
};
}, []);
return (
<Select
@@ -81,14 +75,12 @@ export function SongPicker({ value, onChange, disabled }: Props) {
<span className="font-medium">{opt.name}</span>
{opt.author && <span className="text-xs text-muted-foreground">— {opt.author}</span>}
{opt.length > 0 && (
<span className="text-xs text-muted-foreground font-mono">
{formatLength(opt.length)}
</span>
<span className="text-xs text-muted-foreground font-mono">{formatLength(opt.length)}</span>
)}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
)
);
}
@@ -1,71 +1,71 @@
export interface BaseItemData {
id: number
spriteId: number
publicName: string
itemName: string
type: string
width: number
length: number
stackHeight: number
allowStack: number
allowSit: number
allowLay: number
allowWalk: number
allowGift: number
allowTrade: number
allowRecycle: number
allowMarketplaceSell: number
allowInventoryStack: number
interactionType: string
interactionModesCount: number
vendingIds: string
customparams: string
effectIdMale: number
effectIdFemale: number
clothingOnWalk: string
id: number;
spriteId: number;
publicName: string;
itemName: string;
type: string;
width: number;
length: number;
stackHeight: number;
allowStack: number;
allowSit: number;
allowLay: number;
allowWalk: number;
allowGift: number;
allowTrade: number;
allowRecycle: number;
allowMarketplaceSell: number;
allowInventoryStack: number;
interactionType: string;
interactionModesCount: number;
vendingIds: string;
customparams: string;
effectIdMale: number;
effectIdFemale: number;
clothingOnWalk: string;
}
export interface CatalogItemData {
id: number
catalogName: string
itemIds: string
costCredits: number
costPoints: number
pointsType: number
amount: number
limitedSells: number
limitedStack: number
orderNumber: number
offerId: number
songId: number
haveOffer: string
clubOnly: string
extradata: string
baseName: string
baseItemName: string
spriteId: number
baseItem: BaseItemData | null
id: number;
catalogName: string;
itemIds: string;
costCredits: number;
costPoints: number;
pointsType: number;
amount: number;
limitedSells: number;
limitedStack: number;
orderNumber: number;
offerId: number;
songId: number;
haveOffer: string;
clubOnly: string;
extradata: string;
baseName: string;
baseItemName: string;
spriteId: number;
baseItem: BaseItemData | null;
}
export interface InlineEdit {
costCredits: number
costPoints: number
pointsType: number
amount: number
costCredits: number;
costPoints: number;
pointsType: number;
amount: number;
}
export const POINTS_TYPES: Record<number, { label: string; color: string }> = {
0: { label: 'Duckets', color: 'text-[var(--admin-info)]' },
5: { label: 'Diamonds', color: 'text-[var(--admin-accent-text)]' },
101: { label: 'Snowflakes', color: 'text-[var(--admin-info)]' },
103: { label: 'Hearts', color: 'text-[var(--admin-error)]' },
104: { label: 'Stars', color: 'text-[var(--admin-warning)]' },
105: { label: 'Shells', color: 'text-[var(--admin-warning)]' },
}
0: { label: "Duckets", color: "text-[var(--admin-info)]" },
5: { label: "Diamonds", color: "text-[var(--admin-accent-text)]" },
101: { label: "Snowflakes", color: "text-[var(--admin-info)]" },
103: { label: "Hearts", color: "text-[var(--admin-error)]" },
104: { label: "Stars", color: "text-[var(--admin-warning)]" },
105: { label: "Shells", color: "text-[var(--admin-warning)]" },
};
export const DEFAULT_NEW_ITEM = {
catalogName: '',
itemIds: '0',
catalogName: "",
itemIds: "0",
costCredits: 0,
costPoints: 0,
pointsType: 0,
@@ -75,11 +75,11 @@ export const DEFAULT_NEW_ITEM = {
orderNumber: 1,
offerId: -1,
songId: 0,
haveOffer: '1',
clubOnly: '0',
extradata: '',
}
haveOffer: "1",
clubOnly: "0",
extradata: "",
};
export function getPointsLabel(type: number) {
return POINTS_TYPES[type] || { label: `Type ${type}`, color: 'theme-text-muted' }
return POINTS_TYPES[type] || { label: `Type ${type}`, color: "theme-text-muted" };
}