feat(editor): build sidebar + slimmed items panel for standalone editor (#361)
Bring the open-source standalone editor closer to the community editor's v2 sidebar without pulling in any preset/catalog infrastructure. - Add a preset-less Build tab (apps/editor) that mirrors the community Build sidebar: wall, fence, slab, ceiling, roof, stair, elevator, door, window, column, spawn, plus the material-paint panel. Clicking a type activates the raw structure tool drawn with the kind's defaults. - Wire the Build tab into both editor mount points (local + saved scene) and give the left rail proper image icons (Scene/Build/Items/Settings) instead of letter fallbacks. - Gate the ItemsPanel Library/Community/Mine source chips and tag filter rows behind `showSourceFilter` / `showTagFilters` props (default true, so community and external consumers are unchanged). The standalone editor passes both off, leaving plain category tabs + full-width search. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7f49efce68
commit
e450d8b474
@@ -20,6 +20,8 @@ export function ItemsPanel({
|
||||
leadingTile,
|
||||
emptyState,
|
||||
functionTree,
|
||||
showSourceFilter = true,
|
||||
showTagFilters = true,
|
||||
}: {
|
||||
items?: AssetInput[]
|
||||
/** Called when the search query changes (community edition uses this for server-side search) */
|
||||
@@ -41,6 +43,16 @@ export function ItemsPanel({
|
||||
* hierarchical tree browse instead of the legacy hardcoded category tabs.
|
||||
*/
|
||||
functionTree?: FunctionTreeNode[]
|
||||
/**
|
||||
* Library/Community/Mine source chips. The open-source editor has no
|
||||
* uploaded items (only the built-in catalog), so it hides these.
|
||||
*/
|
||||
showSourceFilter?: boolean
|
||||
/**
|
||||
* Placement/functional tag filter chips under the search row. The
|
||||
* open-source editor hides these to keep the panel to plain categories.
|
||||
*/
|
||||
showTagFilters?: boolean
|
||||
}) {
|
||||
// When the embedder supplies a function taxonomy, the hierarchical browse
|
||||
// replaces the legacy `furnishTools` category tabs entirely.
|
||||
@@ -63,6 +75,8 @@ export function ItemsPanel({
|
||||
leadingTile={leadingTile}
|
||||
onSearchChange={onSearchChange}
|
||||
searchResults={searchResults}
|
||||
showSourceFilter={showSourceFilter}
|
||||
showTagFilters={showTagFilters}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -72,12 +86,16 @@ function LegacyItemsPanel({
|
||||
searchResults,
|
||||
leadingTile,
|
||||
emptyState,
|
||||
showSourceFilter = true,
|
||||
showTagFilters = true,
|
||||
}: {
|
||||
items?: AssetInput[]
|
||||
onSearchChange?: (query: string) => void
|
||||
searchResults?: AssetInput[] | null
|
||||
leadingTile?: React.ReactNode
|
||||
emptyState?: React.ReactNode
|
||||
showSourceFilter?: boolean
|
||||
showTagFilters?: boolean
|
||||
}) {
|
||||
const mode = useEditor((s) => s.mode)
|
||||
const catalogCategory = useEditor((s) => s.catalogCategory)
|
||||
@@ -89,8 +107,11 @@ function LegacyItemsPanel({
|
||||
const [activeFunctionalTag, setActiveFunctionalTag] = useState<string | null>(null)
|
||||
// Library / Community / Mine. Default to Library so first-time users see
|
||||
// the curated catalog rather than every uploaded item; clicking the chip
|
||||
// again clears the filter (`null` = show everything).
|
||||
const [activeSource, setActiveSource] = useState<AssetInput['source'] | null>('library')
|
||||
// again clears the filter (`null` = show everything). With the chips hidden
|
||||
// there is nothing to filter by, so start unfiltered.
|
||||
const [activeSource, setActiveSource] = useState<AssetInput['source'] | null>(
|
||||
showSourceFilter ? 'library' : null,
|
||||
)
|
||||
const [search, setSearch] = useState('')
|
||||
const isServerSearch = onSearchChange !== undefined
|
||||
// True when server search is active but results haven't come back yet
|
||||
@@ -152,7 +173,7 @@ function LegacyItemsPanel({
|
||||
const allTags = Array.from(new Set(categoryItems.flatMap((item) => item.tags ?? [])))
|
||||
const placementTags = allTags.filter((t) => PLACEMENT_TAGS.has(t))
|
||||
const functionalTags = allTags.filter((t) => !PLACEMENT_TAGS.has(t))
|
||||
const hasFilters = allTags.length > 1
|
||||
const hasFilters = showTagFilters && allTags.length > 1
|
||||
|
||||
const placementCount = (tag: string | null) =>
|
||||
categoryItems.filter((item) => {
|
||||
@@ -205,9 +226,13 @@ function LegacyItemsPanel({
|
||||
<div className="flex shrink-0 flex-col gap-2 border-border/70 border-b p-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* Search and source filter take 50/50 of the row. `min-w-0` on
|
||||
both sides lets each half shrink to fit when the panel narrows. */}
|
||||
both sides lets each half shrink to fit when the panel narrows.
|
||||
With the source chips hidden, search spans the full row. */}
|
||||
<input
|
||||
className="w-1/2 min-w-0 shrink-0 rounded-lg bg-muted px-2.5 py-1.5 text-xs placeholder:text-muted-foreground focus:outline-none"
|
||||
className={cn(
|
||||
'min-w-0 shrink-0 rounded-lg bg-muted px-2.5 py-1.5 text-xs placeholder:text-muted-foreground focus:outline-none',
|
||||
showSourceFilter ? 'w-1/2' : 'w-full',
|
||||
)}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value)
|
||||
onSearchChange?.(e.target.value)
|
||||
@@ -216,7 +241,7 @@ function LegacyItemsPanel({
|
||||
type="text"
|
||||
value={search}
|
||||
/>
|
||||
{sourceChips.length > 0 && (
|
||||
{showSourceFilter && sourceChips.length > 0 && (
|
||||
<div className="flex w-1/2 min-w-0 shrink-0 rounded-lg bg-muted p-0.5">
|
||||
{sourceChips.map((chip) => {
|
||||
const isActive = activeSource === chip.id
|
||||
|
||||
Reference in New Issue
Block a user