+
{ isHidden && }
diff --git a/src/components/catalog/views/catalog-icon/CatalogIconView.tsx b/src/components/catalog/views/catalog-icon/CatalogIconView.tsx
index 0178662..4e5ed5a 100644
--- a/src/components/catalog/views/catalog-icon/CatalogIconView.tsx
+++ b/src/components/catalog/views/catalog-icon/CatalogIconView.tsx
@@ -1,20 +1,20 @@
import { FC, useMemo } from 'react';
import { GetConfigurationValue } from '../../../../api';
-import { LayoutImage } from '../../../../common';
export interface CatalogIconViewProps
{
icon: number;
+ className?: string;
}
export const CatalogIconView: FC
= props =>
{
- const { icon = 0 } = props;
+ const { icon = 0, className = '' } = props;
- const getIconUrl = useMemo(() =>
+ const iconUrl = useMemo(() =>
{
return ((GetConfigurationValue('catalog.asset.icon.url')).replace('%name%', icon.toString()));
}, [ icon ]);
- return ;
+ return
;
};
diff --git a/src/components/catalog/views/catalog-rail/CatalogRailItemView.tsx b/src/components/catalog/views/catalog-rail/CatalogRailItemView.tsx
index 3b1cd21..4d1e38e 100644
--- a/src/components/catalog/views/catalog-rail/CatalogRailItemView.tsx
+++ b/src/components/catalog/views/catalog-rail/CatalogRailItemView.tsx
@@ -19,8 +19,8 @@ export const CatalogRailItemView: FC = props =>
title={ node.localization }
onClick={ onClick }
>
-
-
+
+
{ node.localization }
diff --git a/src/components/catalog/views/favorites/CatalogFavoritesView.tsx b/src/components/catalog/views/favorites/CatalogFavoritesView.tsx
index 676f771..77b02e6 100644
--- a/src/components/catalog/views/favorites/CatalogFavoritesView.tsx
+++ b/src/components/catalog/views/favorites/CatalogFavoritesView.tsx
@@ -121,7 +121,7 @@ export const CatalogFavoritesView: FC = props =>
onClick={ () => { openPageByOfferId(fav.offerId); onClose(); } }
>
{ /* Furni icon */ }
-
+
{ fav.iconUrl
?

: fav.nodeIconId !== null
diff --git a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx
index d31d801..f897cad 100644
--- a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx
+++ b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx
@@ -86,7 +86,7 @@ export const CatalogNavigationItemView: FC
= pro
>
{ adminMode &&
}
-
+
{ node.localization }
diff --git a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetFurniView.tsx b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetFurniView.tsx
index 48e9b84..98604a6 100644
--- a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetFurniView.tsx
+++ b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetFurniView.tsx
@@ -574,19 +574,6 @@ export const InfoStandWidgetFurniView: FC
= props
onClick={ () => setDropdownOpen(!dropdownOpen) }>
{ dropdownOpen ? `${LocalizeText('widget.furni.present.close')} Buildtools` : `${LocalizeText('navigator.roomsettings.doormode.open')} Buildtools` }
-
{ dropdownOpen &&
{ /* Left panel: position + rotation */ }
diff --git a/src/components/toolbar/ToolbarView.tsx b/src/components/toolbar/ToolbarView.tsx
index fbb5ae8..ea9fb16 100644
--- a/src/components/toolbar/ToolbarView.tsx
+++ b/src/components/toolbar/ToolbarView.tsx
@@ -96,8 +96,6 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
CreateLinkEvent('camera/toggle') } /> }
{ isMod &&
CreateLinkEvent('mod-tools/toggle') } /> }
- { isMod &&
- CreateLinkEvent('furni-editor/toggle') } /> }
diff --git a/src/css/index.css b/src/css/index.css
index 6c62fc7..709acdb 100644
--- a/src/css/index.css
+++ b/src/css/index.css
@@ -30,51 +30,45 @@ body {
}
::-webkit-scrollbar {
- width: .5rem
+ width: .625rem;
}
::-webkit-scrollbar:horizontal {
- height: .5rem
+ height: .625rem;
}
::-webkit-scrollbar:not(:horizontal) {
- width: .5rem
+ width: .625rem;
}
-::-webkit-scrollbar-track:horizontal {
- border-bottom: .25rem solid rgba(0, 0, 0, .1)
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, .08);
+ border-radius: .5rem;
}
-::-webkit-scrollbar-track:not(:horizontal) {
- border-right: .25rem solid rgba(0, 0, 0, .1)
+::-webkit-scrollbar-thumb {
+ background: rgba(30, 114, 149, .35);
+ border-radius: .5rem;
+ border: 2px solid transparent;
+ background-clip: padding-box;
}
-::-webkit-scrollbar-thumb:horizontal {
- border-bottom: .25rem solid rgba(30, 114, 149, .4)
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(30, 114, 149, .6);
+ border-radius: .5rem;
+ border: 2px solid transparent;
+ background-clip: padding-box;
}
-::-webkit-scrollbar-thumb:horizontal:hover {
- border-bottom: .25rem solid rgba(30, 114, 149, .8)
-}
-
-::-webkit-scrollbar-thumb:horizontal:active {
- border-bottom: .25rem solid #185D79
-}
-
-::-webkit-scrollbar-thumb:not(:horizontal) {
- border-right: .25rem solid rgba(30, 114, 149, .4)
-}
-
-::-webkit-scrollbar-thumb:not(:horizontal):hover {
- border-right: .25rem solid rgba(30, 114, 149, .8)
-}
-
-::-webkit-scrollbar-thumb:not(:horizontal):active {
- border-right: .25rem solid #185D79
+::-webkit-scrollbar-thumb:active {
+ background: #185D79;
+ border-radius: .5rem;
+ border: 2px solid transparent;
+ background-clip: padding-box;
}
::-webkit-scrollbar-corner {
- background: rgba(0, 0, 0, .1)
+ background: rgba(0, 0, 0, .08);
}
@layer components {
@@ -447,219 +441,7 @@ body {
}
}
-.nitro-avatar-editor-spritesheet {
- background: url('@/assets/images/avatareditor/avatar-editor-spritesheet.png') transparent no-repeat;
-
- &.arrow-left-icon {
- width: 28px;
- height: 21px;
- background-position: -226px -131px;
- }
-
- &.arrow-right-icon {
- width: 28px;
- height: 21px;
- background-position: -226px -162px;
- }
-
- &.ca-icon {
- width: 25px;
- height: 25px;
- background-position: -226px -61px;
-
- &.selected {
- width: 25px;
- height: 25px;
- background-position: -226px -96px;
- }
- }
-
- &.cc-icon {
- width: 31px;
- height: 29px;
- background-position: -145px -5px;
-
- &.selected {
- width: 31px;
- height: 29px;
- background-position: -145px -44px;
- }
- }
-
- &.ch-icon {
- width: 29px;
- height: 24px;
- background-position: -186px -39px;
-
- &.selected {
- width: 29px;
- height: 24px;
- background-position: -186px -73px;
- }
- }
-
- &.clear-icon {
- width: 27px;
- height: 27px;
- background-position: -145px -157px;
- }
-
- &.cp-icon {
- width: 30px;
- height: 24px;
- background-position: -145px -264px;
-
- &.selected {
- width: 30px;
- height: 24px;
- background-position: -186px -5px;
- }
- }
-
-
- &.ea-icon {
- width: 35px;
- height: 16px;
- background-position: -226px -193px;
-
- &.selected {
- width: 35px;
- height: 16px;
- background-position: -226px -219px;
- }
- }
-
- &.fa-icon {
- width: 27px;
- height: 20px;
- background-position: -186px -137px;
-
- &.selected {
- width: 27px;
- height: 20px;
- background-position: -186px -107px;
- }
- }
-
- &.female-icon {
- width: 18px;
- height: 27px;
- background-position: -186px -202px;
-
- &.selected {
- width: 18px;
- height: 27px;
- background-position: -186px -239px;
- }
- }
-
- &.ha-icon {
- width: 25px;
- height: 22px;
- background-position: -226px -245px;
-
- &.selected {
- width: 25px;
- height: 22px;
- background-position: -226px -277px;
- }
- }
-
- &.he-icon {
- width: 31px;
- height: 27px;
- background-position: -145px -83px;
-
- &.selected {
- width: 31px;
- height: 27px;
- background-position: -145px -120px;
- }
- }
-
- &.hr-icon {
- width: 29px;
- height: 25px;
- background-position: -145px -194px;
-
- &.selected {
- width: 29px;
- height: 25px;
- background-position: -145px -229px;
- }
- }
-
- &.lg-icon {
- width: 19px;
- height: 20px;
- background-position: -303px -45px;
-
- &.selected {
- width: 19px;
- height: 20px;
- background-position: -303px -75px;
- }
- }
-
- &.loading-icon {
- width: 21px;
- height: 25px;
- background-position: -186px -167px;
- }
-
-
- &.male-icon {
- width: 21px;
- height: 21px;
- background-position: -186px -276px;
-
- &.selected {
- width: 21px;
- height: 21px;
- background-position: -272px -5px;
- }
- }
-
-
- &.sellable-icon {
- width: 17px;
- height: 15px;
- background-position: -303px -105px;
- }
-
-
- &.sh-icon {
- width: 37px;
- height: 10px;
- background-position: -303px -5px;
-
- &.selected {
- width: 37px;
- height: 10px;
- background-position: -303px -25px;
- }
- }
-
-
- &.spotlight-icon {
- width: 130px;
- height: 305px;
- background-position: -5px -5px;
- }
-
-
- &.wa-icon {
- width: 36px;
- height: 18px;
- background-position: -226px -5px;
-
- &.selected {
- width: 36px;
- height: 18px;
- background-position: -226px -33px;
- }
- }
-}
+/* Avatar editor icons are now rendered as
tags via AvatarEditorIcon.tsx */
.nitro-avatar-editor-wardrobe-figure-preview {
background-color: #677181;
@@ -710,7 +492,7 @@ body {
.category-item {
- height: 40px;
+ height: 32px;
}
.figure-preview-container {
diff --git a/src/hooks/furni-editor/index.ts b/src/hooks/furni-editor/index.ts
deleted file mode 100644
index 47ce6ef..0000000
--- a/src/hooks/furni-editor/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './useFurniEditor';
diff --git a/src/hooks/furni-editor/useFurniEditor.ts b/src/hooks/furni-editor/useFurniEditor.ts
deleted file mode 100644
index e4258e5..0000000
--- a/src/hooks/furni-editor/useFurniEditor.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-import { useCallback, useState } from 'react';
-
-export interface FurniItem
-{
- id: number;
- spriteId: number;
- itemName: string;
- publicName: string;
- type: string;
- width: number;
- length: number;
- stackHeight: number;
- allowStack: boolean;
- allowWalk: boolean;
- allowSit: boolean;
- allowLay: boolean;
- interactionType: string;
- interactionModesCount: number;
-}
-
-export interface FurniDetail extends FurniItem
-{
- allowGift: boolean;
- allowTrade: boolean;
- allowRecycle: boolean;
- allowMarketplaceSell: boolean;
- allowInventoryStack: boolean;
- vendingIds: string;
- customparams: string;
- effectIdMale: number;
- effectIdFemale: number;
- clothingOnWalk: string;
- multiheight: string;
- description: string;
- usageCount: number;
-}
-
-export interface CatalogRef
-{
- id: number;
- catalogName: string;
- costCredits: number;
- costPoints: number;
- pointsType: number;
- pageId: number;
- pageName: string;
-}
-
-const API_BASE = '/api/admin/furni-editor';
-
-async function apiFetch(url: string, options?: RequestInit): Promise
-{
- const res = await fetch(url, { credentials: 'include', ...options });
- const data = await res.json();
-
- if(!res.ok || data.error) throw new Error(data.error || 'API error');
-
- return data;
-}
-
-export const useFurniEditor = () =>
-{
- const [ items, setItems ] = useState([]);
- const [ total, setTotal ] = useState(0);
- const [ page, setPage ] = useState(1);
- const [ loading, setLoading ] = useState(false);
- const [ error, setError ] = useState(null);
- const [ selectedItem, setSelectedItem ] = useState(null);
- const [ catalogItems, setCatalogItems ] = useState([]);
- const [ interactions, setInteractions ] = useState([]);
- const [ furniDataEntry, setFurniDataEntry ] = useState | null>(null);
-
- const clearError = useCallback(() => setError(null), []);
-
- const searchItems = useCallback(async (query: string, type: string, pg: number) =>
- {
- setLoading(true);
- setError(null);
-
- try
- {
- const params = new URLSearchParams({ q: query, limit: '20', page: String(pg) });
-
- if(type) params.set('type', type);
-
- const data = await apiFetch<{ items: FurniItem[]; total: number; page: number }>(`${ API_BASE }?${ params }`);
-
- setItems(data.items);
- setTotal(data.total);
- setPage(data.page);
- }
- catch(e: any)
- {
- setError(e.message);
- }
- finally
- {
- setLoading(false);
- }
- }, []);
-
- const loadDetail = useCallback(async (id: number): Promise =>
- {
- setLoading(true);
- setError(null);
-
- try
- {
- const data = await apiFetch<{ item: FurniDetail; catalogItems: CatalogRef[]; furniDataEntry: Record | null }>(`${ API_BASE }/detail?id=${ id }`);
-
- setSelectedItem(data.item);
- setCatalogItems(data.catalogItems);
- setFurniDataEntry(data.furniDataEntry);
-
- return true;
- }
- catch(e: any)
- {
- setError(e.message);
-
- return false;
- }
- finally
- {
- setLoading(false);
- }
- }, []);
-
- const updateItem = useCallback(async (id: number, fields: Record) =>
- {
- setLoading(true);
- setError(null);
-
- try
- {
- await apiFetch(`${ API_BASE }/update?id=${ id }`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(fields)
- });
-
- return true;
- }
- catch(e: any)
- {
- setError(e.message);
-
- return false;
- }
- finally
- {
- setLoading(false);
- }
- }, []);
-
- const createItem = useCallback(async (fields: Record) =>
- {
- setLoading(true);
- setError(null);
-
- try
- {
- const data = await apiFetch<{ id: number }>(`${ API_BASE }`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(fields)
- });
-
- return data.id;
- }
- catch(e: any)
- {
- setError(e.message);
-
- return null;
- }
- finally
- {
- setLoading(false);
- }
- }, []);
-
- const deleteItem = useCallback(async (id: number) =>
- {
- setLoading(true);
- setError(null);
-
- try
- {
- await apiFetch(`${ API_BASE }/delete?id=${ id }`, { method: 'POST' });
-
- return true;
- }
- catch(e: any)
- {
- setError(e.message);
-
- return false;
- }
- finally
- {
- setLoading(false);
- }
- }, []);
-
- const loadInteractions = useCallback(async () =>
- {
- try
- {
- const data = await apiFetch<{ interactions: Array }>(`${ API_BASE }/interactions`);
-
- setInteractions(data.interactions.map(i => typeof i === 'string' ? i : i.name));
- }
- catch {}
- }, []);
-
- const loadBySpriteId = useCallback(async (spriteId: number): Promise =>
- {
- try
- {
- const data = await apiFetch<{ id: number }>(`${ API_BASE }/by-sprite?spriteId=${ spriteId }`);
-
- return await loadDetail(data.id);
- }
- catch(e: any)
- {
- setError(e.message);
-
- return false;
- }
- }, [ loadDetail ]);
-
- return {
- items, total, page, loading, error, clearError,
- selectedItem, setSelectedItem, catalogItems, furniDataEntry,
- interactions,
- searchItems, loadDetail, loadBySpriteId, updateItem, createItem, deleteItem, loadInteractions
- };
-};