Skip to content
Merged
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
46 changes: 0 additions & 46 deletions .claude/settings.local.json

This file was deleted.

3 changes: 0 additions & 3 deletions .example.env

This file was deleted.

6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ tsconfig.tsbuildinfo
# etc
.DS_Store
.idea
.claude/settings.local.json
.turbo/

# extension artifacts
*.crx
*.pem

# compiled
**/tailwind-output.css
4 changes: 0 additions & 4 deletions .turbo/preferences/tui.json

This file was deleted.

10 changes: 7 additions & 3 deletions app/panel/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ const SidePanelContent = () => {
const { display, setDisplay, ActiveScreen } = useSidePanel();

return (
<div className="min-h-screen bg-sidebar p-8 text-sidebarForeground">
<Header display={display} setDisplay={setDisplay} />
<ActiveScreen />
<div className="min-h-screen bg-sidebar text-sidebarForeground">
<div className="sticky top-0 z-50 bg-sidebar px-8 pb-0 pt-8">
<Header display={display} setDisplay={setDisplay} />
</div>
<div className="px-8 pb-8">
<ActiveScreen />
</div>
<ThemeToggle />
</div>
);
Expand Down
127 changes: 88 additions & 39 deletions app/panel/components/Calendar/CalendarCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface CalendarCellProps {
data: CalendarCellData;
isToday?: boolean;
isMuted?: boolean;
compact?: boolean;
onClick?: () => void;
}

Expand All @@ -26,49 +27,97 @@ const getIntensityClass = (totalExp: number): string => {
return "bg-primary/25";
};

const CalendarCell = memo(({ label, data, isToday = false, isMuted = false, onClick }: CalendarCellProps) => {
const hasData = data.hasData;
const MAX_VISIBLE_SKILLS = 4;

return (
<button
type="button"
onClick={onClick}
disabled={!onClick}
className={cn(
"flex aspect-square flex-col items-start overflow-hidden rounded-md border p-1.5 text-left transition-colors",
onClick && "hover:border-primary/50 cursor-pointer",
!onClick && "cursor-default",
isToday && "ring-2 ring-primary",
isMuted && "opacity-40",
hasData ? getIntensityClass(data.totalExp) : "border-border/50",
!hasData && !isMuted && "border-border/30",
)}>
<span
const CalendarCell = memo(
({ label, data, isToday = false, isMuted = false, compact = false, onClick }: CalendarCellProps) => {
const hasData = data.hasData;
const skills = hasData ? Object.entries(data.expBySkill).sort(([, a], [, b]) => b - a) : [];
const visibleSkills = skills.slice(0, MAX_VISIBLE_SKILLS);
const overflowSkills = skills.slice(MAX_VISIBLE_SKILLS);

return (
<button
type="button"
onClick={onClick}
disabled={!onClick}
className={cn(
"text-xs font-medium",
isToday ? "text-primary" : isMuted ? "text-muted-foreground" : "text-foreground",
"flex min-h-[5rem] flex-col items-start overflow-hidden rounded-md border p-1.5 text-left transition-colors",
onClick && "hover:border-primary/50 cursor-pointer",
!onClick && "cursor-default",
isToday && "ring-2 ring-primary",
isMuted && "opacity-40",
hasData ? getIntensityClass(data.totalExp) : "border-border/50",
!hasData && !isMuted && "border-border/30",
)}>
{label}
</span>
{/* Day / Month label */}
<span
className={cn(
"text-xs font-medium",
isToday ? "text-primary" : isMuted ? "text-muted-foreground" : "text-foreground",
)}>
{label}
</span>

{hasData && (
<div className="mt-1 flex w-full flex-1 flex-col text-[10px]">
{/* ── Totals header ── */}
<div className="flex items-center justify-between gap-1">
<span className="text-[9px] font-medium uppercase tracking-wider text-muted-foreground">Totals</span>
{data.netProfit !== 0 && (
<span
className={cn(
"truncate rounded-full px-1.5 text-[9px] font-semibold leading-tight",
data.netProfit > 0 ? "bg-green-500/15 text-green-600" : "bg-red-500/15 text-red-600",
)}>
{data.netProfit > 0 ? "+" : ""}
{formatGP(data.netProfit)} GP
</span>
)}
</div>
<span className="truncate font-semibold text-foreground">{formatExp(data.totalExp)} exp</span>
{data.avgExpPerHour > 0 && (
<span className="truncate text-[9px] text-muted-foreground">
{formatExp(Math.round(data.avgExpPerHour))}/hr
</span>
)}

{/* ── Skills (hidden in compact / month+year view) ── */}
{!compact && skills.length > 0 && (
<>
<div className="border-border/40 mt-1.5 border-t pt-1.5" />
<div className="mb-1 flex flex-wrap gap-1">
{visibleSkills.map(([skill]) => (
<span key={skill} className="truncate rounded bg-muted px-1 py-px text-[8px] text-muted-foreground">
{skill}
</span>
))}
{overflowSkills.length > 0 && (
<span
title={overflowSkills.map(([s]) => s).join(", ")}
className="inline-flex h-3.5 w-3.5 flex-shrink-0 cursor-default items-center justify-center rounded-full bg-muted text-[7px] leading-none text-muted-foreground">
&hellip;
</span>
)}
</div>
</>
)}

{hasData && (
<div className="mt-0.5 flex w-full flex-col gap-0.5 text-[10px]">
<span className="truncate font-semibold text-foreground">{formatExp(data.totalExp)} xp</span>
{data.avgExpPerHour > 0 && (
<span className="truncate text-muted-foreground">{formatExp(Math.round(data.avgExpPerHour))}/hr</span>
)}
{data.netProfit !== 0 && (
<span className={cn("truncate", data.netProfit > 0 ? "text-primary" : "text-destructive")}>
{data.netProfit > 0 ? "+" : ""}
{formatGP(data.netProfit)} gp
</span>
)}
{data.foodUsed > 0 && <span className="truncate text-muted-foreground">Food: {formatGP(data.foodUsed)}</span>}
</div>
)}
</button>
);
});
{/* ── Cost ── */}
{data.foodUsed > 0 && (
<>
<div className="border-border/40 mt-auto border-t pt-1" />
<span className="w-full truncate text-center text-[9px] font-medium text-destructive">
{data.totalDamageReceived.toLocaleString()} HP &middot; {formatGP(data.foodUsed)} GP
</span>
</>
)}
</div>
)}
</button>
);
},
);

CalendarCell.displayName = "CalendarCell";

Expand Down
37 changes: 29 additions & 8 deletions app/panel/components/Calendar/views/DayView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const HourRow = memo(({ hour, data, mainSkill, trendPct }: HourRowProps) => {
<div
className="flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-semibold"
style={EXP_BADGE_STYLE}>
<span>{formatExp(data.totalExp)} xp</span>
<span>{formatExp(data.totalExp)} exp</span>
{trendPct !== undefined && (
<>
<span className="text-[10px] opacity-60">|</span>
Expand Down Expand Up @@ -133,17 +133,38 @@ const HourRow = memo(({ hour, data, mainSkill, trendPct }: HourRowProps) => {
)}

{hasStats && (
<div className="flex flex-wrap gap-x-4 gap-y-1 border-t pt-2 text-xs text-muted-foreground">
{data.totalDropValue > 0 && <span>Total: {formatGP(data.totalDropValue)} GP</span>}
<div className="flex flex-wrap items-center gap-x-3 gap-y-1.5 border-t pt-2 text-xs">
{data.totalDropValue > 0 && (
<span className="text-muted-foreground">
Total Value: <span className="font-medium text-foreground">{formatGP(data.totalDropValue)} GP</span>
</span>
)}
{data.foodUsed > 0 && (
<span className="text-muted-foreground">
Food Cost: <span className="font-medium text-foreground">{formatGP(data.foodUsed)} GP</span>
</span>
)}
{data.netProfit !== 0 && (
<span className={cn(data.netProfit > 0 ? "text-primary" : "text-destructive")}>
Net: {data.netProfit > 0 ? "+" : ""}
<span
className={cn(
"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-semibold",
data.netProfit > 0 ? "bg-green-500/15 text-green-600" : "bg-red-500/15 text-red-600",
)}>
{data.netProfit > 0 ? "+" : ""}
{formatGP(data.netProfit)} GP
</span>
)}
{data.foodUsed > 0 && <span>Food: {formatGP(data.foodUsed)} GP</span>}
{data.totalFights > 0 && <span>Fights: {data.totalFights.toLocaleString()}</span>}
{data.totalSkillingActions > 0 && <span>Actions: {data.totalSkillingActions.toLocaleString()}</span>}
{data.totalFights > 0 && (
<span className="text-muted-foreground">
Fights: <span className="font-medium text-foreground">{data.totalFights.toLocaleString()}</span>
</span>
)}
{data.totalSkillingActions > 0 && (
<span className="text-muted-foreground">
Actions:{" "}
<span className="font-medium text-foreground">{data.totalSkillingActions.toLocaleString()}</span>
</span>
)}
</div>
)}
</CardContent>
Expand Down
1 change: 1 addition & 0 deletions app/panel/components/Calendar/views/MonthView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const MonthView = memo(({ currentDate, getCell, onDrillDown }: MonthViewProps) =
data={getCell(key)}
isToday={isToday}
isMuted={!isCurrentMonth}
compact
onClick={() => onDrillDown("day", date)}
/>
))}
Expand Down
2 changes: 1 addition & 1 deletion app/panel/components/Calendar/views/WeekView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const WeekView = memo(({ currentDate, getCell, onDrillDown }: WeekViewProps) =>
}, [currentDate]);

return (
<div className="grid grid-cols-[repeat(auto-fill,minmax(7rem,1fr))] gap-1.5">
<div className="grid grid-cols-1 gap-1.5 min-[480px]:grid-cols-2 min-[720px]:grid-cols-3 min-[960px]:grid-cols-4">
{days.map(({ date, key, label, isToday }) => (
<CalendarCell
key={key}
Expand Down
1 change: 1 addition & 0 deletions app/panel/components/Calendar/views/YearView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const YearView = memo(({ currentDate, getCell, onDrillDown }: YearViewProps) =>
label={name}
data={cellData}
isToday={isCurrentMonth}
compact
onClick={() => onDrillDown("month", new Date(year, i, 1))}
/>
);
Expand Down
Loading