Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ app.config.timestamp_*.js
*.launch
.settings/

# Temp
gitignore

# System Files
.DS_Store
Thumbs.db

# rasterized svgs
public/og-icons
public/brico-face.png
52 changes: 27 additions & 25 deletions frontend/src/components/GlobalSearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* - Enter navigates to full /search page
*/

import {useNavigate} from "@solidjs/router";
import {A, useNavigate} from "@solidjs/router";
import {TbOutlineSearch as IconSearch} from "solid-icons/tb";
import {createMemo, createSignal, For, onCleanup, onMount, Show} from "solid-js";
import {useIsMobile} from "~/components/ui/sidebar";
Expand Down Expand Up @@ -97,11 +97,15 @@ export default function GlobalSearchInput(props: GlobalSearchInputProps) {
tabButtonRefs.get(tabId)?.scrollIntoView({block: "nearest", inline: "nearest"});
};

const commitSuggestion = (match: ObjectMatch) => {
const closeSuggestions = () => {
setQuery("");
setDropdownOpen(false);
setActiveIndex(-1);
if (inputRef) inputRef.value = "";
};

const commitSuggestion = (match: ObjectMatch) => {
closeSuggestions();
navigate(match.route);
};

Expand Down Expand Up @@ -260,30 +264,28 @@ export default function GlobalSearchInput(props: GlobalSearchInputProps) {
{(match, i) => {
const isActive = () => activeIndex() === i();
return (
<li
role="option"
aria-selected={isActive()}
class={cn(
"px-3 py-1.5 text-sm cursor-pointer transition-colors flex flex-col gap-0.5",
isActive() ? "bg-accent text-accent-foreground" : "hover:bg-accent/50",
)}
onPointerDown={(e) => {
e.preventDefault();
commitSuggestion(match);
}}
onMouseEnter={() => setActiveIndex(i())}
>
<Show when={activeTab() === "all"}>
<span class="text-xs text-muted-foreground">{match.label}</span>
</Show>
<span class="font-medium truncate">
{match.displayName}
<Show when={match.matchField !== "name" && match.matchField !== "id"}>
<span class="text-xs text-muted-foreground ml-2">
({match.matchField}: {match.matchValue.length > 50 ? match.matchValue.slice(0, 50) + "…" : match.matchValue})
</span>
<li role="option" aria-selected={isActive()}>
<A
href={match.route}
class={cn(
"px-3 py-1.5 text-sm cursor-pointer transition-colors flex flex-col gap-0.5",
isActive() ? "bg-accent text-accent-foreground" : "hover:bg-accent/50",
)}
onClick={closeSuggestions}
onMouseEnter={() => setActiveIndex(i())}
>
<Show when={activeTab() === "all"}>
<span class="text-xs text-muted-foreground">{match.label}</span>
</Show>
</span>
<span class="font-medium truncate">
{match.displayName}
<Show when={match.matchField !== "name" && match.matchField !== "id"}>
<span class="text-xs text-muted-foreground ml-2">
({match.matchField}: {match.matchValue.length > 50 ? match.matchValue.slice(0, 50) + "…" : match.matchValue})
</span>
</Show>
</span>
</A>
</li>
);
}}
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/components/data-table/table-faceted-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,13 @@ export function TableFacetedFilter<TData>(props: TableFacetedFilterProps<TData>)
}

return (
<Popover placement="bottom-start" open={popoverOpen()}
onOpenChange={(open) => {
setPopoverOpen(open);
setEditingWithNumberInputs(false);
}}
<Popover
fitViewport={true} slide={true} overlap={true}
placement="bottom-start" open={popoverOpen()}
onOpenChange={(open) => {
setPopoverOpen(open);
setEditingWithNumberInputs(false);
}}
>
<PopoverTrigger
as={Button<"button">} variant="outline" size="sm" onclick={() => setPopoverOpen(!popoverOpen())}
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/fun/BricoLootBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ function rollItemList(list: ItemListDesc): LootResult | null {
if (r <= 0 && poss.items.length > 0) {
const stack = poss.items[Math.floor(Math.random() * poss.items.length)];
if (stack.itemType.tag === ItemType.Cargo.tag) {
const cargo = cargoIdx?.get(stack.itemId);
const cargo = cargoIdx.get(stack.itemId);
return cargo ? { type: "cargo", cargo, quantity: stack.quantity } : null;
}
const item = itemIdx?.get(stack.itemId);
const item = itemIdx.get(stack.itemId);
return item ? { type: "item", item, quantity: stack.quantity } : null;
}
}
Expand Down Expand Up @@ -159,9 +159,9 @@ export const BricoLootBox: Component<BricoLootBoxProps> = (props) => {
setResolvedItemId([targetItemId, targetItemType]);

if (targetListId) {
const list = BitCraftTables.ItemListDesc.indexedBy("id")()?.get(targetListId);
const list = BitCraftTables.ItemListDesc.indexedBy("id")().get(targetListId);
if (list) {
const item = BitCraftTables.ItemDesc.indexedBy("id")()?.get(targetItemId);
const item = BitCraftTables.ItemDesc.indexedBy("id")().get(targetItemId);
const result = rollItemList(list);
if (result && item?.itemListId === targetListId) {
// input was item list itself, just resolve to the rolled item
Expand Down Expand Up @@ -268,7 +268,7 @@ export const BricoLootBox: Component<BricoLootBoxProps> = (props) => {
const cargo = BitCraftTables.CargoDesc.indexedBy("id")().get(id);
return cargo ? {type: "cargo", cargo, quantity: 1} : null;
} else {
const item = BitCraftTables.ItemDesc.indexedBy("id")()?.get(id);
const item = BitCraftTables.ItemDesc.indexedBy("id")().get(id);
return item ? {type: "item", item, quantity: 1} : null;
}
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/shared/GameIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ export const ItemIcon: Component<ItemIconProps> = (props) => {
const { tf2Mode } = useSettings();
const hatIcon = () => {
if (tf2Mode()) {
const eq = BitCraftTables.EquipmentDesc.indexedBy("itemId")?.()?.get(props.item.id);
const eq = BitCraftTables.EquipmentDesc.indexedBy("itemId")().get(props.item.id);
if (eq && eq.slots.some(s => s.tag === "HeadClothing")) {
return `/assets/Hats/${HATS[Math.floor(Math.random() * HATS.length)]}.webp`;
}
Expand Down
28 changes: 14 additions & 14 deletions frontend/src/components/shared/ItemStacks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ import {cn, fixFloat} from "~/lib/utils";
/** Resolve an ItemStack's itemId + itemType to the actual ItemDesc or CargoDesc */
function resolveStack(itemId: number, itemType: ItemType): ItemDesc | CargoDesc | undefined {
if (itemType.tag === ItemType.Item.tag) {
return BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(itemId);
return BitCraftTables.ItemDesc.indexedBy("id")().get(itemId);
} else if (itemType.tag === ItemType.Cargo.tag) {
return BitCraftTables.CargoDesc.indexedBy("id")?.()?.get(itemId);
return BitCraftTables.CargoDesc.indexedBy("id")().get(itemId);
}
return undefined;
}
Expand Down Expand Up @@ -159,7 +159,7 @@ export const ProbItemStackIcon: Component<{
if (!obj || !isItem(obj)) return undefined;
const listId = (obj as ItemDesc).itemListId;
if (!listId) return undefined;
return BitCraftTables.ItemListDesc.indexedBy("id")?.()?.get(listId);
return BitCraftTables.ItemListDesc.indexedBy("id")().get(listId);
});

return (
Expand Down Expand Up @@ -239,18 +239,18 @@ function computeAveragesFlatExpanded(possibilities: ItemListPossibility[], multi
const totalWeight = possibilities.reduce((sum, p) => sum + p.probability, 0);
if (totalWeight === 0) return [];

const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")?.();
const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")?.();
const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")();
const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")();

for (const poss of possibilities) {
const selectProb = (poss.probability / totalWeight) * multiplier;
for (const stack of poss.items) {
// Check if this stack resolves to an inner item list
let handledAsInnerList = false;
if (stack.itemType.tag === ItemType.Item.tag) {
const item = itemIndex?.get(stack.itemId);
const item = itemIndex.get(stack.itemId);
if (item?.itemListId) {
const innerList = listIndex?.get(item.itemListId);
const innerList = listIndex.get(item.itemListId);
if (innerList) {
// Recurse: multiply probability by select chance × stack quantity
const innerAvgs = computeAveragesFlatExpanded(innerList.possibilities, selectProb * stack.quantity);
Expand Down Expand Up @@ -301,14 +301,14 @@ export const ItemListDisplay: Component<{

/** True if any possibility contains an item that resolves to an inner item list */
const hasInnerLists = createMemo(() => {
const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")?.();
const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")?.();
const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")();
const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")();
return sorted().some(poss =>
poss.items.some(stack => {
if (stack.itemType.tag !== ItemType.Item.tag) return false;
const item = itemIndex?.get(stack.itemId);
const item = itemIndex.get(stack.itemId);
if (!item?.itemListId) return false;
return !!listIndex?.get(item.itemListId);
return !!listIndex.get(item.itemListId);
})
);
});
Expand Down Expand Up @@ -508,7 +508,7 @@ export const QuestDropDisplay: Component<{
class?: string;
}> = (props) => {
const questStage = () => props.questDrop.requiredStageId
? BitCraftTables.QuestStageDesc.indexedBy("id")()?.get(props.questDrop.requiredStageId)
? BitCraftTables.QuestStageDesc.indexedBy("id")().get(props.questDrop.requiredStageId)
: undefined;

return (
Expand Down Expand Up @@ -569,9 +569,9 @@ export function expandStack(
// Plain ItemStack — check if it resolves to an item list
const stack = input as ItemStack;
if (stack.itemType.tag === ItemType.Item.tag) {
const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(stack.itemId);
const item = BitCraftTables.ItemDesc.indexedBy("id")().get(stack.itemId);
if (item?.itemListId) {
const list = BitCraftTables.ItemListDesc.indexedBy("id")?.()?.get(item.itemListId);
const list = BitCraftTables.ItemListDesc.indexedBy("id")().get(item.itemListId);
if (list) {
return <ItemListDisplay itemList={list} chances={input.quantity} originalIcon={() => <ItemStackIcon stack={stack} hideSingle/>}/>;
}
Expand Down
22 changes: 11 additions & 11 deletions frontend/src/components/shared/PlaceableGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,8 @@ export function computeExpectedPath(

function aggregateItemCosts(
costs: { itemId: number; itemType: string; quantity: number }[],
itemIndex: Map<number, any> | undefined,
cargoIndex: Map<number, any> | undefined,
itemIndex: Map<number, any>,
cargoIndex: Map<number, any>,
): PathResult["itemCosts"] {
const map = new Map<string, number>();
for (const c of costs) {
Expand All @@ -798,8 +798,8 @@ function aggregateItemCosts(
const [iType, iIdStr] = key.split("-");
const iId = parseInt(iIdStr, 10);
const name = iType === "Cargo"
? cargoIndex?.get(iId)?.name ?? `Cargo #${iId}`
: itemIndex?.get(iId)?.name ?? `Item #${iId}`;
? cargoIndex.get(iId)?.name ?? `Cargo #${iId}`
: itemIndex.get(iId)?.name ?? `Item #${iId}`;
return {itemId: iId, itemType: iType, name, expectedQuantity: qty};
});
}
Expand Down Expand Up @@ -1043,7 +1043,7 @@ export function PlaceableGraph(props: PlaceableGraphProps) {

const renderIcon = () => {
if (node.type === "placeable") {
const desc = () => placeableIndex()?.get(node.gameId);
const desc = () => placeableIndex().get(node.gameId);
return (
<Show when={desc()}>
{(d) => <PlaceableIcon placeable={d()} small noInteract/>}
Expand All @@ -1052,15 +1052,15 @@ export function PlaceableGraph(props: PlaceableGraphProps) {
} else {
const isCargo = node.itemType === "Cargo";
if (isCargo) {
const desc = () => cargoIndex()?.get(node.gameId);
const desc = () => cargoIndex().get(node.gameId);
return (
<Show when={desc()}>
{/* hardcoded to use the same size as square icons */}
{(d) => <CargoIcon cargo={d()} small noInteract class={"max-w-[65px] max-h-[65px]"}/>}
</Show>
);
} else {
const desc = () => itemIndex()?.get(node.gameId);
const desc = () => itemIndex().get(node.gameId);
return (
<Show when={desc()}>
{(d) => <ItemIcon item={d()} small noInteract/>}
Expand Down Expand Up @@ -1285,7 +1285,7 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData {
nodeIds.add(nodeId);

const isCargo = itemType === "Cargo";
const desc = isCargo ? cargoIndex?.get(itemId) : itemIndex?.get(itemId);
const desc = isCargo ? cargoIndex.get(itemId) : itemIndex.get(itemId);
const name = desc?.name ?? `${itemType} #${itemId}`;
const pp = linkPlacement ? placementByItem.get(`${itemType}-${itemId}`) : undefined;

Expand All @@ -1308,7 +1308,7 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData {
if (nodeIds.has(nodeId)) return nodeId;
nodeIds.add(nodeId);

const desc = placeableIndex?.get(plcId);
const desc = placeableIndex.get(plcId);
nodes.push({
id: nodeId,
label: desc?.name ?? `Placeable #${plcId}`,
Expand Down Expand Up @@ -1388,8 +1388,8 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData {
const tooltip = ia.consumedItemStacks.length
? ia.consumedItemStacks.map(s => {
const name = s.itemType.tag === "Cargo"
? cargoIndex?.get(s.itemId)?.name ?? `Cargo #${s.itemId}`
: itemIndex?.get(s.itemId)?.name ?? `Item #${s.itemId}`;
? cargoIndex.get(s.itemId)?.name ?? `Cargo #${s.itemId}`
: itemIndex.get(s.itemId)?.name ?? `Item #${s.itemId}`;
return `${s.quantity}× ${name} per action${effortReq}`;
}).join(", ")
: undefined;
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/shared/RecipeDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const ResourceDepletionIcons: Component<{ resource: ResourceDesc, showLabel: boo
}
const depletionResource = () => {
if (!r?.onDestroyYieldResourceId) return undefined;
return BitCraftTables.ResourceDesc.indexedBy("id")()?.get(r.onDestroyYieldResourceId);
return BitCraftTables.ResourceDesc.indexedBy("id")().get(r.onDestroyYieldResourceId);
}

return (
Expand Down Expand Up @@ -512,7 +512,7 @@ export const renderKnowledgeLockedItem = <T extends {requiredKnowledges: number[
// ─── Placeable Placement Panel ──────────────────────────────────

export const PlacementPanel: Component<{ placement: PlaceablePlacementDesc }> = (props) => {
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.placement.placedPlaceableId);
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.placement.placedPlaceableId);

return (
<RecipeVisual
Expand Down Expand Up @@ -591,7 +591,7 @@ const GrowthOutcomeIcon: Component<{
showPercent: Accessor<boolean>;
onTogglePercent: () => void;
}> = (props) => {
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.placeableId);
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.placeableId);
const pct = () => (props.probability / props.totalWeight) * 100;

return (
Expand All @@ -617,7 +617,7 @@ const GrowthOutcomeIcon: Component<{

export const GrowthPanel: Component<{ growth: PlaceableGrowthDesc }> = (props) => {
const [showPercent, setShowPercent] = createSignal(true);
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.growth.placeableId);
const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.growth.placeableId);
const totalWeight = () => props.growth.outcomes.reduce((sum, o) => sum + o.probability, 0);

return (
Expand Down
Loading