feat: Model Browser — block library with category filter tabs, search, favorites, NAM/IR models

This commit is contained in:
2026-06-12 19:51:19 -04:00
parent f67e6bd8c7
commit 376249172f
3 changed files with 807 additions and 11 deletions
+70 -4
View File
@@ -163,17 +163,37 @@ function VertBracket({ height, direction }) {
}
// ── Split Block Tile ─────────────────────────────
function SplitBlockTile({ block, selected, onSelect, onToggle }) {
function SplitBlockTile({ block, selected, onSelect, onToggle, onModelBrowser }) {
const splitParams = block.params || {};
const splitType = splitParams.splitType || "y";
const st = SPLIT_TYPES[splitType] || SPLIT_TYPES.y;
const enabled = !block.bypassed;
const color = getBlockColor("split");
const longPressRef = useRef(null);
const handleTouchStart = useCallback(() => {
longPressRef.current = setTimeout(() => {
longPressRef.current = null;
onModelBrowser?.(block.id);
}, 500);
}, [block.id, onModelBrowser]);
const handleTouchEnd = useCallback(() => {
if (longPressRef.current) {
clearTimeout(longPressRef.current);
longPressRef.current = null;
}
}, []);
return (
<div
draggable
onClick={() => onSelect(block.id)}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchEnd}
onMouseDown={handleTouchEnd}
onMouseUp={handleTouchEnd}
style={{
minWidth: 130, width: 130, height: 76, borderRadius: 8,
background: `linear-gradient(135deg, ${T.panel}, #1A1520)`,
@@ -237,14 +257,34 @@ function SplitBlockTile({ block, selected, onSelect, onToggle }) {
}
// ── Merge Block Tile ──────────────────────────────
function MergeBlockTile({ block, selected, onSelect, onToggle }) {
function MergeBlockTile({ block, selected, onSelect, onToggle, onModelBrowser }) {
const mergeParams = block.params || {};
const enabled = !block.bypassed;
const longPressRef = useRef(null);
const handleTouchStart = useCallback(() => {
longPressRef.current = setTimeout(() => {
longPressRef.current = null;
onModelBrowser?.(block.id);
}, 500);
}, [block.id, onModelBrowser]);
const handleTouchEnd = useCallback(() => {
if (longPressRef.current) {
clearTimeout(longPressRef.current);
longPressRef.current = null;
}
}, []);
return (
<div
draggable
onClick={() => onSelect(block.id)}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchEnd}
onMouseDown={handleTouchEnd}
onMouseUp={handleTouchEnd}
style={{
minWidth: 130, width: 130, height: 76, borderRadius: 8,
background: `linear-gradient(135deg, #15201A, ${T.panel})`,
@@ -304,14 +344,34 @@ function MergeBlockTile({ block, selected, onSelect, onToggle }) {
}
// ── Regular Block Tile ────────────────────────────
function BlockTile({ block, selected, onSelect, onToggle, pathLabel }) {
function BlockTile({ block, selected, onSelect, onToggle, pathLabel, onModelBrowser }) {
const color = getBlockColor(block.type);
const enabled = !block.bypassed;
const longPressRef = useRef(null);
const handleTouchStart = useCallback(() => {
longPressRef.current = setTimeout(() => {
longPressRef.current = null;
onModelBrowser?.(block.id);
}, 500);
}, [block.id, onModelBrowser]);
const handleTouchEnd = useCallback(() => {
if (longPressRef.current) {
clearTimeout(longPressRef.current);
longPressRef.current = null;
}
}, []);
return (
<div
draggable
onClick={() => onSelect(block.id)}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchEnd}
onMouseDown={handleTouchEnd}
onMouseUp={handleTouchEnd}
style={{
minWidth: 120, width: 120, height: 76, borderRadius: 8,
background: pathLabel === "B" ? "#1A1820" : T.panel,
@@ -435,7 +495,7 @@ function buildChainModel(blocks) {
// ── Block Chain (exported) ─────────────────────────
export default function BlockChain({
blocks, selectedBlockId, onSelectBlock, onToggleBlock,
onReorder, onAddBlock, onRemoveBlock,
onReorder, onAddBlock, onRemoveBlock, onModelBrowser,
}) {
const scrollRef = useRef(null);
const scrollBRef = useRef(null);
@@ -528,6 +588,7 @@ export default function BlockChain({
index={idx}
onSelect={onSelectBlock}
onToggle={onToggleBlock}
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>
@@ -585,6 +646,7 @@ export default function BlockChain({
index={idx}
onSelect={onSelectBlock}
onToggle={onToggleBlock}
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>
@@ -597,6 +659,7 @@ export default function BlockChain({
selected={splitBlock.id === selectedBlockId}
onSelect={onSelectBlock}
onToggle={onToggleBlock}
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>
@@ -611,6 +674,7 @@ export default function BlockChain({
onSelect={onSelectBlock}
onToggle={onToggleBlock}
pathLabel="A"
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>
@@ -626,6 +690,7 @@ export default function BlockChain({
selected={mergeBlock.id === selectedBlockId}
onSelect={onSelectBlock}
onToggle={onToggleBlock}
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>
@@ -639,6 +704,7 @@ export default function BlockChain({
index={idx}
onSelect={onSelectBlock}
onToggle={onToggleBlock}
onModelBrowser={onModelBrowser}
/>
<ChainArrow />
</div>