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
1 change: 1 addition & 0 deletions src/components/CodingChallengeWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const CodingChallengeWrapper: React.FC<CodingChallengeWrapperProps> = ({ pattern
<Instructions
readmes={pattern.readmes}
onClose={handleInstructionsClose}
interviewId={pattern.id}
/>
) : (
<div className="challenge-content">
Expand Down
11 changes: 11 additions & 0 deletions src/components/Instructions.css
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@
/* color: #a0aec0; */
}

.markdown-content a {
color: var(--primary);
text-decoration: underline;
text-underline-offset: 2px;
transition: opacity 0.2s ease;
}

.markdown-content a:hover {
opacity: 0.8;
}

.instructions-footer {
border-top: 1px solid #404040;
padding: 16px 24px;
Expand Down
30 changes: 26 additions & 4 deletions src/components/Instructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,41 @@ import { ScrollArea } from "@/components/ui/scroll-area";
interface InstructionsProps {
readmes: ReadmeFile[];
onClose: () => void;
interviewId?: string;
}

const Instructions: React.FC<InstructionsProps> = ({ readmes, onClose }) => {
const [currentIndex, setCurrentIndex] = useState(0);
const STORAGE_KEY_PREFIX = "instructions-page-";

const Instructions: React.FC<InstructionsProps> = ({ readmes, onClose, interviewId }) => {
// Initialize from localStorage if we have an interviewId
const [currentIndex, setCurrentIndex] = useState(() => {
if (interviewId) {
const saved = localStorage.getItem(`${STORAGE_KEY_PREFIX}${interviewId}`);
if (saved !== null) {
const parsed = parseInt(saved, 10);
// Validate the saved index is within bounds
if (!isNaN(parsed) && parsed >= 0 && parsed < readmes.length) {
return parsed;
}
}
}
return 0;
});
const [visibleTabs, setVisibleTabs] = useState<number[]>([]);
const [overflowTabs, setOverflowTabs] = useState<number[]>([]);
const [showTopShadow, setShowTopShadow] = useState(false);
const [showBottomShadow, setShowBottomShadow] = useState(false);
const tabsListRef = useRef<HTMLDivElement>(null);
const scrollAreaRef = useRef<HTMLDivElement>(null);

const currentReadme = readmes[currentIndex];

// Persist current page to localStorage
useEffect(() => {
if (interviewId) {
localStorage.setItem(`${STORAGE_KEY_PREFIX}${interviewId}`, currentIndex.toString());
}
}, [currentIndex, interviewId]);

// Focus management for modal
useEffect(() => {
const firstTab = document.getElementById('tab-0');
Expand Down Expand Up @@ -274,6 +296,7 @@ const Instructions: React.FC<InstructionsProps> = ({ readmes, onClose }) => {
.replace(/^### (.*$)/gim, "<h3>$1</h3>")
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
.replace(/\*(.*?)\*/g, "<em>$1</em>")
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
.replace(/\n\n/g, "</p><p>");

// Restore inline code
Expand Down Expand Up @@ -352,7 +375,6 @@ const Instructions: React.FC<InstructionsProps> = ({ readmes, onClose }) => {

<ScrollArea
className="h-full"
ref={scrollAreaRef}
onScrollCapture={handleScroll}
>
<main className="px-8 py-2">
Expand Down
20 changes: 18 additions & 2 deletions src/components/InterviewShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const ResizableSidebarContent: React.FC<{
<Sidebar variant="inset" collapsible="offcanvas">
<SidebarContent className="overflow-hidden">
{pattern.readmes ? (
<Instructions readmes={pattern.readmes} onClose={() => {}} />
<Instructions readmes={pattern.readmes} onClose={() => {}} interviewId={pattern.id} />
) : null}
</SidebarContent>
</Sidebar>
Expand All @@ -85,6 +85,7 @@ const ResizableSidebarContent: React.FC<{
key={`instructions-${sidebarWidth}`}
readmes={pattern.readmes}
onClose={() => {}}
interviewId={pattern.id}
/>
) : null}
</SidebarContent>
Expand All @@ -102,6 +103,8 @@ const ResizableSidebarContent: React.FC<{
);
};

const SIDEBAR_OPEN_KEY_PREFIX = "sidebar-open-";

const InterviewShell: React.FC<InterviewShellProps> = ({ pattern, onBack }) => {
const [showInstructions, setShowInstructions] = useState(false);
const [hasViewedInstructions, setHasViewedInstructions] = useState(false);
Expand All @@ -110,6 +113,18 @@ const InterviewShell: React.FC<InterviewShellProps> = ({ pattern, onBack }) => {
const sidebarRef = useRef<HTMLDivElement>(null);
const isResizing = useRef(false);

// Sidebar open state persisted to localStorage
const [sidebarOpen, setSidebarOpen] = useState(() => {
const saved = localStorage.getItem(`${SIDEBAR_OPEN_KEY_PREFIX}${pattern.id}`);
// Default to open (true) if no saved state
return saved === null ? true : saved === "true";
});

const handleSidebarOpenChange = (open: boolean) => {
setSidebarOpen(open);
localStorage.setItem(`${SIDEBAR_OPEN_KEY_PREFIX}${pattern.id}`, String(open));
};

// Resize functionality
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
Expand Down Expand Up @@ -194,7 +209,7 @@ const InterviewShell: React.FC<InterviewShellProps> = ({ pattern, onBack }) => {
</div>
</header>
<div className="flex-1 overflow-y-auto flex flex-col relative">
<SidebarProvider>
<SidebarProvider open={sidebarOpen} onOpenChange={handleSidebarOpenChange}>
<ResizableSidebarContent
pattern={pattern}
sidebarWidth={sidebarWidth}
Expand Down Expand Up @@ -241,6 +256,7 @@ const InterviewShell: React.FC<InterviewShellProps> = ({ pattern, onBack }) => {
<Instructions
readmes={pattern.readmes}
onClose={() => setShowInstructions(false)}
interviewId={pattern.id}
/>
) : isCodingChallenge ? (
<CodingChallengeWrapper pattern={pattern} />
Expand Down