Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c16ac38
feat: Add UnitiyWearablePreview config
cyaiox Jun 24, 2025
887a4db
chore: Update decentraland-ui package
cyaiox Jun 24, 2025
8c79bb6
feat: Add WEARABLE_PREVIEW_URL config
cyaiox Jun 24, 2025
f4d2ced
Merge branch 'master' of github.com:decentraland/builder into feat/us…
RocioCM Feb 27, 2026
740a7c5
Formatting fix
RocioCM Feb 27, 2026
b43fef4
Update wearable-preview url
RocioCM Mar 4, 2026
0301db8
Merge branch 'master' of github.com:decentraland/builder into feat/us…
RocioCM Mar 4, 2026
7894a1b
Refactor repeated code
RocioCM Mar 4, 2026
baaf44a
Update tests
RocioCM Mar 4, 2026
e9ad5e0
Use unity wearable preview in thumbnails
RocioCM Mar 12, 2026
42be5f0
Fix imports
RocioCM Mar 12, 2026
1bb95f4
Fix button style
RocioCM Mar 12, 2026
a7d1c02
Rollback thumbnail wearable preview
RocioCM Mar 16, 2026
67c380f
Fix types
RocioCM Mar 16, 2026
1244c7e
Show emote controls on unity editor
RocioCM Mar 16, 2026
4671c4a
Add new translations
RocioCM Mar 23, 2026
ee19521
Update schemas version
RocioCM Mar 23, 2026
aa479aa
Implement spring bones parsing and UI
RocioCM Mar 24, 2026
a5ccadb
Fix patching for different body shapes
RocioCM Mar 24, 2026
f9e53cc
Merge branch 'master' of github.com:decentraland/builder into feat/us…
RocioCM Mar 25, 2026
04c9a24
Merge branch 'feat/use-unity-wearable-preview' of github.com:decentra…
RocioCM Mar 25, 2026
14de4b9
Remove debug logs
RocioCM Mar 25, 2026
679aabb
Fix unit tests
RocioCM Mar 25, 2026
5bc845f
Fix dev install
RocioCM Mar 25, 2026
b6a7296
Add missing tests
RocioCM Mar 25, 2026
9691916
Fix validations
RocioCM Mar 25, 2026
4d8a7ae
Update setSpringBonesParams arg type
RocioCM Mar 26, 2026
c878871
Update schemas version
RocioCM Mar 26, 2026
84647e5
Update ui2 version
RocioCM Mar 26, 2026
5756ebf
Fix PR comments
RocioCM Mar 26, 2026
017046d
Update dev wearable preview url
RocioCM Mar 26, 2026
74a5e7b
Update dapps conflicting version
RocioCM Mar 27, 2026
2a9ac89
Improve selector implementation
RocioCM Mar 27, 2026
fc6c819
Update gltf extensions parsing & patching
RocioCM Mar 31, 2026
8e79d7a
Improve formatting
RocioCM Mar 31, 2026
475019e
Fix code details
RocioCM Apr 1, 2026
a3c09f4
Rollback emotes dropdown styles & add unity param
RocioCM Apr 2, 2026
9243edb
Remove unused classname
RocioCM Apr 2, 2026
ebac394
Remove baseurl
RocioCM Apr 2, 2026
056eb13
Merge branch 'master' of github.com:decentraland/builder into feat/us…
RocioCM Apr 2, 2026
af501bf
Use babylon for social emotes
RocioCM Apr 2, 2026
364599c
Merge branch 'feat/use-unity-wearable-preview' of github.com:decentra…
RocioCM Apr 2, 2026
ad0fb2b
Add testing url
RocioCM Apr 2, 2026
dd90b7a
Add testing url
RocioCM Apr 2, 2026
b4c1d15
Merge branch 'feat/use-unity-wearable-preview' of github.com:decentra…
RocioCM Apr 2, 2026
d8ee9e6
Update preview
RocioCM Apr 2, 2026
aa64837
Update preview url
RocioCM Apr 2, 2026
31955b4
Remove hitRadius
RocioCM Apr 6, 2026
e7e723a
Fix dependencies
RocioCM Apr 7, 2026
822bad3
Update default spring bones params
RocioCM Apr 7, 2026
5bc9066
Refactor selected item identifier from GLB hash to item ID across the…
RocioCM Apr 9, 2026
564e5a5
Improve UX on mint
RocioCM Apr 9, 2026
23406c1
Merge branch 'master' of github.com:decentraland/builder into feat/sp…
RocioCM Apr 9, 2026
4f88f65
Fix form issues
RocioCM Apr 9, 2026
06061b8
Merge branch 'master' of github.com:decentraland/builder into feat/sp…
RocioCM Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
setSkinColor,
fetchBaseWearablesRequest,
setWearablePreviewController,
setItems
setItems,
pushSpringBoneParams
} from 'modules/editor/actions'
import {
getEmote,
Expand Down Expand Up @@ -124,6 +125,9 @@ const CenterPanelContainer: React.FC<CenterPanelContainerProps> = () => {
params?: Parameters<typeof fetchCollectionItemsRequest>[1]
) => {
dispatch(fetchCollectionItemsRequest(id, params))
},
onPushSpringBoneParams: () => {
dispatch(pushSpringBoneParams())
}
}),
[dispatch]
Expand Down
11 changes: 9 additions & 2 deletions src/components/ItemEditorPage/CenterPanel/CenterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,15 @@ export default class CenterPanel extends React.PureComponent<Props, State> {
}

handleWearablePreviewLoad = () => {
const { wearableController, onSetWearablePreviewController } = this.props
const { wearableController, onSetWearablePreviewController, onPushSpringBoneParams } = this.props

if (!wearableController) {
onSetWearablePreviewController(WearablePreview.createController('wearable-editor'))
}

// Push current spring bone params to the freshly loaded controller via saga
onPushSpringBoneParams()

this.setState({ isLoading: false })

// Run validation once the preview has loaded (item data is ready)
Expand All @@ -278,14 +281,16 @@ export default class CenterPanel extends React.PureComponent<Props, State> {
}

handlePlayEmote = () => {
const { wearableController, isPlayingEmote, visibleItems, onSetAvatarAnimation, onSetItems } = this.props
const { wearableController, isPlayingEmote, visibleItems, onSetAvatarAnimation, onSetItems, onPushSpringBoneParams } = this.props
const newVisibleItems = visibleItems.filter(item => item.type !== ItemType.EMOTE)

if (isPlayingEmote) {
onSetAvatarAnimation(PreviewEmote.IDLE)
onSetItems(newVisibleItems)
} else {
wearableController?.emote.play() as void
// Push spring bone params immediately on emote play start via saga
onPushSpringBoneParams()
}
}

Expand Down Expand Up @@ -467,6 +472,8 @@ export default class CenterPanel extends React.PureComponent<Props, State> {
profile="default"
bodyShape={bodyShape}
emote={emote}
// TODO: remove baseUrl before merging.
baseUrl="https://wearable-preview-git-feat-babylon-spring-bones-decentraland1.vercel.app"
zoom={zoom}
skin={toHex(skinColor)}
eyes={toHex(eyeColor)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type Props = {
onFetchCollectionItems: ActionFunction<typeof fetchCollectionItemsRequest>
onSetWearablePreviewController: (controller: Parameters<typeof setWearablePreviewController>[0]) => void
onSetItems: (items: Parameters<typeof setItems>[0]) => void
onPushSpringBoneParams: () => void
}

export type CenterPanelContainerProps = Record<string, never>
Expand Down
32 changes: 32 additions & 0 deletions src/components/ItemEditorPage/RightPanel/RightPanel.container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { useGetSelectedItemIdFromCurrentUrl } from 'modules/location/hooks'
import { getCollection, hasViewAndEditRights } from 'modules/collection/selectors'
import { isWalletCommitteeMember } from 'modules/committee/selectors'
import { getIsCampaignEnabled, getIsVrmOptOutEnabled, getIsWearableUtilityEnabled } from 'modules/features/selectors'
import { getBodyShape, getBones, getSpringBoneParams, hasSpringBoneChanges as hasSpringBoneChangesSelector } from 'modules/editor/selectors'
import { setSpringBoneParam, addSpringBoneParams, deleteSpringBoneParams, resetSpringBoneParams } from 'modules/editor/actions'
import { SpringBoneParams } from 'modules/editor/types'
import { RightPanelContainerProps } from './RightPanel.types'
import RightPanel from './RightPanel'

Expand Down Expand Up @@ -45,6 +48,10 @@ const RightPanelContainer: React.FC<RightPanelContainerProps> = () => {
})

const itemStatus = useMemo(() => (selectedItemId ? statusByItemId[selectedItemId] : null), [selectedItemId, statusByItemId])
const selectedBodyShape = useSelector((state: RootState) => getBodyShape(state))
const bones = useSelector((state: RootState) => getBones(state))
const springBoneParams = useSelector((state: RootState) => getSpringBoneParams(state))
const hasSpringBoneChanges = useSelector((state: RootState) => hasSpringBoneChangesSelector(state))
const onSaveItem: ActionFunction<typeof saveItemRequest> = useCallback(
(item, contents) => dispatch(saveItemRequest(item, contents)),
[dispatch]
Expand All @@ -53,6 +60,23 @@ const RightPanelContainer: React.FC<RightPanelContainerProps> = () => {
const onDeleteItem: ActionFunction<typeof deleteItemRequest> = useCallback(item => dispatch(deleteItemRequest(item)), [dispatch])
const onOpenModal: ActionFunction<typeof openModal> = useCallback((name, metadata) => dispatch(openModal(name, metadata)), [dispatch])
const onDownload: ActionFunction<typeof downloadItemRequest> = useCallback(itemId => dispatch(downloadItemRequest(itemId)), [dispatch])
const onSpringBoneParamChange: ActionFunction<typeof setSpringBoneParam> = useCallback(
(boneName: string, field: keyof SpringBoneParams, value: SpringBoneParams[typeof field]) =>
dispatch(setSpringBoneParam(boneName, field, value)),
[dispatch]
)
const onAddSpringBoneParams: ActionFunction<typeof addSpringBoneParams> = useCallback(
(boneName: string) => dispatch(addSpringBoneParams(boneName)),
[dispatch]
)
const onDeleteSpringBoneParams: ActionFunction<typeof deleteSpringBoneParams> = useCallback(
(boneName: string) => dispatch(deleteSpringBoneParams(boneName)),
[dispatch]
)
const onResetSpringBoneParams: ActionFunction<typeof resetSpringBoneParams> = useCallback(
() => dispatch(resetSpringBoneParams()),
[dispatch]
)

return (
<RightPanel
Expand All @@ -71,10 +95,18 @@ const RightPanelContainer: React.FC<RightPanelContainerProps> = () => {
campaignName={campaignName}
isVrmOptOutEnabled={isVrmOptOutEnabled}
isWearableUtilityEnabled={isWearableUtilityEnabled}
selectedBodyShape={selectedBodyShape}
bones={bones}
springBoneParams={springBoneParams}
onSaveItem={onSaveItem}
onDeleteItem={onDeleteItem}
onOpenModal={onOpenModal}
onDownload={onDownload}
onSpringBoneParamChange={onSpringBoneParamChange}
onAddSpringBoneParams={onAddSpringBoneParams}
onDeleteSpringBoneParams={onDeleteSpringBoneParams}
hasSpringBoneChanges={hasSpringBoneChanges}
onResetSpringBoneParams={onResetSpringBoneParams}
/>
)
}
Expand Down
1 change: 1 addition & 0 deletions src/components/ItemEditorPage/RightPanel/RightPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
overflow-y: auto;
max-height: calc(100vh - 135px);
flex: 1;
padding-bottom: 48px; /* Space for floating Help button */
}

.dcl.text-area {
Expand Down
36 changes: 31 additions & 5 deletions src/components/ItemEditorPage/RightPanel/RightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import Input from './Input'
import Select from './Select'
import MultiSelect from './MultiSelect'
import Tags from './Tags'
import SpringBonesSection from './SpringBonesSection'
import { Props, State } from './RightPanel.types'
import './RightPanel.css'

Expand Down Expand Up @@ -412,8 +413,9 @@ export default class RightPanel extends React.PureComponent<Props, State> {
}

handleOnSaveItem = async () => {
const { selectedItem, itemStatus, onSaveItem } = this.props
const { name, description, utility, rarity, contents, data, isDirty } = this.state
const { selectedItem, itemStatus, onSaveItem, hasSpringBoneChanges } = this.props
const { name, description, utility, rarity, contents, data, isDirty: isItemDirty } = this.state
const isDirty = isItemDirty || hasSpringBoneChanges

if (isDirty && selectedItem) {
let itemData = data
Expand Down Expand Up @@ -463,6 +465,12 @@ export default class RightPanel extends React.PureComponent<Props, State> {
}
}

handleResetButtonClick = () => {
const { onResetSpringBoneParams } = this.props
this.handleOnResetItem()
onResetSpringBoneParams()
}

handleOpenThumbnailDialog = () => {
const { selectedItem, onOpenModal } = this.props

Expand Down Expand Up @@ -746,9 +754,17 @@ export default class RightPanel extends React.PureComponent<Props, State> {
isCampaignEnabled,
isVrmOptOutEnabled,
campaignName,
campaignTag
campaignTag,
selectedBodyShape,
bones,
springBoneParams,
onSpringBoneParamChange,
onAddSpringBoneParams,
onDeleteSpringBoneParams,
hasSpringBoneChanges
} = this.props
const { name, description, utility, rarity, data, isDirty, hasItem } = this.state
const { name, description, utility, rarity, data, isDirty: isItemDirty, hasItem } = this.state
const isDirty = isItemDirty || hasSpringBoneChanges
const rarities = Rarity.getRarities()
const playModes = getEmotePlayModes()

Expand Down Expand Up @@ -1109,9 +1125,19 @@ export default class RightPanel extends React.PureComponent<Props, State> {
</div>
</Collapsable>
)}
{item?.type === ItemType.WEARABLE && (
<SpringBonesSection
key={`${item?.id}-${selectedBodyShape}`} // Reset the component state when changing the item or body shape
bones={bones}
springBoneParams={springBoneParams}
onParamChange={onSpringBoneParamChange}
onAddSpringBoneParams={onAddSpringBoneParams}
onDeleteSpringBoneParams={onDeleteSpringBoneParams}
/>
)}
<div className="edit-buttons-container">
<div className="edit-buttons">
<Button secondary onClick={this.handleOnResetItem} disabled={!isDirty || isLoading} loading={isLoading}>
<Button secondary onClick={this.handleResetButtonClick} disabled={!isDirty || isLoading} loading={isLoading}>
{t('global.revert')}
</Button>
<NetworkButton
Expand Down
45 changes: 42 additions & 3 deletions src/components/ItemEditorPage/RightPanel/RightPanel.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Dispatch } from 'redux'
import { Rarity } from '@dcl/schemas'
import { BodyShape, Rarity } from '@dcl/schemas'
import {
deleteItemRequest,
DeleteItemRequestAction,
Expand All @@ -10,6 +10,14 @@ import {
} from 'modules/item/actions'
import { Item, SyncStatus, WearableData, EmoteData } from 'modules/item/types'
import { Collection } from 'modules/collection/types'
import {
SetSpringBoneParamAction,
AddSpringBoneParamsAction,
DeleteSpringBoneParamsAction,
ResetSpringBoneParamsAction,
resetSpringBoneParams
} from 'modules/editor/actions'
import { BoneNode, SpringBoneParams } from 'modules/editor/types'
import { openModal, OpenModalAction } from 'decentraland-dapps/dist/modules/modal/actions'

export type Props = {
Expand All @@ -32,6 +40,14 @@ export type Props = {
onDeleteItem: ActionFunction<typeof deleteItemRequest>
onOpenModal: ActionFunction<typeof openModal>
onDownload: ActionFunction<typeof downloadItemRequest>
selectedBodyShape: BodyShape
bones: BoneNode[]
springBoneParams: Record<string, SpringBoneParams>
onSpringBoneParamChange: (boneName: string, field: keyof SpringBoneParams, value: SpringBoneParams[typeof field]) => void
onAddSpringBoneParams: (boneName: string) => void
onDeleteSpringBoneParams: (boneName: string) => void
hasSpringBoneChanges: boolean
onResetSpringBoneParams: ActionFunction<typeof resetSpringBoneParams>
}

export type State = {
Expand Down Expand Up @@ -64,9 +80,32 @@ export type MapStateProps = Pick<
| 'campaignName'
| 'isVrmOptOutEnabled'
| 'isWearableUtilityEnabled'
| 'selectedBodyShape'
| 'bones'
| 'springBoneParams'
| 'hasSpringBoneChanges'
>
export type MapDispatchProps = Pick<
Props,
| 'onSaveItem'
| 'onDeleteItem'
| 'onOpenModal'
| 'onDownload'
| 'onSpringBoneParamChange'
| 'onAddSpringBoneParams'
| 'onDeleteSpringBoneParams'
| 'onResetSpringBoneParams'
>
export type MapDispatch = Dispatch<
| SaveItemRequestAction
| DeleteItemRequestAction
| OpenModalAction
| DownloadItemRequestAction
| SetSpringBoneParamAction
| AddSpringBoneParamsAction
| DeleteSpringBoneParamsAction
| ResetSpringBoneParamsAction
>
export type MapDispatchProps = Pick<Props, 'onSaveItem' | 'onDeleteItem' | 'onOpenModal' | 'onDownload'>
export type MapDispatch = Dispatch<SaveItemRequestAction | DeleteItemRequestAction | OpenModalAction | DownloadItemRequestAction>

// New type for the functional component container
export type RightPanelContainerProps = Omit<Props, keyof MapStateProps | keyof MapDispatchProps>
Loading
Loading