From 92623fde0250bf1e43cf8d9f6dea15165fdc8f7d Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Sat, 9 May 2026 16:25:28 +0200 Subject: [PATCH 1/7] fix(security): bump python-jose 3.3.0 to 3.4.0 (CVE-2024-33663, CVE-2024-33664) Resolves Dependabot alerts #10, #11, #15, #16. --- services/ai/pyproject.toml | 2 +- services/ai/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/ai/pyproject.toml b/services/ai/pyproject.toml index d59e3ff..3c8a939 100644 --- a/services/ai/pyproject.toml +++ b/services/ai/pyproject.toml @@ -29,7 +29,7 @@ dependencies = [ "pydantic>=2.5.0", "pydantic-settings>=2.1.0", # Auth & security - "python-jose==3.3.0", + "python-jose==3.4.0", "passlib==1.7.4", "bcrypt==4.1.1", "zxcvbn==4.4.28", diff --git a/services/ai/requirements.txt b/services/ai/requirements.txt index 8806981..808fd25 100644 --- a/services/ai/requirements.txt +++ b/services/ai/requirements.txt @@ -3,7 +3,7 @@ uvicorn[standard]>=0.30.0 sqlalchemy==2.0.23 pydantic>=2.5.0 pydantic-settings>=2.1.0 -python-jose==3.3.0 +python-jose==3.4.0 passlib==1.7.4 bcrypt==4.1.1 python-multipart>=0.0.9 From 506d68a2287d1182444a884fcb17f0823065faf2 Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Sat, 9 May 2026 16:25:36 +0200 Subject: [PATCH 2/7] fix(security): bump python-dotenv 1.0.0 to 1.2.2 (alert #22) Resolves Dependabot alert #22. --- services/ai/pyproject.toml | 2 +- services/ai/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/ai/pyproject.toml b/services/ai/pyproject.toml index 3c8a939..cc17f8e 100644 --- a/services/ai/pyproject.toml +++ b/services/ai/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ # Database "sqlalchemy==2.0.23", "psycopg2-binary==2.9.9", - "python-dotenv>=1.0.0", + "python-dotenv>=1.2.2", # Validation & settings "pydantic>=2.5.0", "pydantic-settings>=2.1.0", diff --git a/services/ai/requirements.txt b/services/ai/requirements.txt index 808fd25..9bc5f70 100644 --- a/services/ai/requirements.txt +++ b/services/ai/requirements.txt @@ -10,7 +10,7 @@ python-multipart>=0.0.9 httpx>=0.27.0 pytest==7.4.3 pytest-asyncio==0.21.1 -python-dotenv==1.0.0 +python-dotenv==1.2.2 psycopg2-binary==2.9.9 # Security zxcvbn==4.4.28 From 7c8dd321adce726017d3f518df28ec4f126aadb6 Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Sat, 9 May 2026 16:25:44 +0200 Subject: [PATCH 3/7] fix(security): bump pytest 7.4.3 to 9.0.3 and pytest-asyncio to 0.24.0 (alert #18) Resolves Dependabot alert #18. pytest-asyncio must be co-bumped because 0.21.x declares pytest<9; asyncio_mode=auto preserved. --- services/ai/pyproject.toml | 4 ++-- services/ai/requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/ai/pyproject.toml b/services/ai/pyproject.toml index cc17f8e..9855d3f 100644 --- a/services/ai/pyproject.toml +++ b/services/ai/pyproject.toml @@ -50,8 +50,8 @@ dependencies = [ [project.optional-dependencies] dev = [ - "pytest==7.4.3", - "pytest-asyncio==0.21.1", + "pytest==9.0.3", + "pytest-asyncio==0.24.0", "pytest-cov>=4.1.0", "mypy>=1.7.0", ] diff --git a/services/ai/requirements.txt b/services/ai/requirements.txt index 9bc5f70..6e474a0 100644 --- a/services/ai/requirements.txt +++ b/services/ai/requirements.txt @@ -8,8 +8,8 @@ passlib==1.7.4 bcrypt==4.1.1 python-multipart>=0.0.9 httpx>=0.27.0 -pytest==7.4.3 -pytest-asyncio==0.21.1 +pytest==9.0.3 +pytest-asyncio==0.24.0 python-dotenv==1.2.2 psycopg2-binary==2.9.9 # Security From eff1549a6cecfee0200e822acc2d1bb2ea1ab35b Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Sat, 9 May 2026 16:26:52 +0200 Subject: [PATCH 4/7] fix(security): bump next 14 to 15.5.15 and eslint-config-next to match (alerts #1-#5) Resolves Dependabot alerts #1, #2, #3, #4, #5. next 15.5.15 patches DoS, HTTP smuggling, and cache exhaustion CVEs. --- apps/web/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 8d07bcb..cefe622 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -14,7 +14,7 @@ "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"" }, "dependencies": { - "next": "^14.0.0", + "next": "^15.5.15", "next-auth": "^4.24.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -32,7 +32,7 @@ "@typescript-eslint/parser": "^6.13.0", "autoprefixer": "^10.4.0", "eslint": "^8.55.0", - "eslint-config-next": "^14.0.0", + "eslint-config-next": "^15.5.15", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.0", "eslint-plugin-react-hooks": "^4.6.0", From 7c74c02128cd1d8432edb948810a75ce973a9aca Mon Sep 17 00:00:00 2001 From: Luca Visconti Date: Mon, 11 May 2026 10:38:00 +0200 Subject: [PATCH 5/7] Enhance accessibility and UI consistency - Added ARIA attributes to the progress bar in the StudyPage for better accessibility. - Capitalized the "done" label in LessonNav for consistency in UI text. - Improved loading state accessibility in OutputPane by adding aria-labels to loading indicators and input fields. --- .../__tests__/integration/auth-flow.test.tsx | 20 +- .../__tests__/integration/study-flow.test.tsx | 24 +- apps/web/__tests__/unit/OutputPane.test.tsx | 6 +- apps/web/__tests__/unit/login.test.tsx | 8 +- apps/web/__tests__/unit/signup.test.tsx | 52 +- apps/web/coverage/clover.xml | 1583 ++++++-- apps/web/coverage/coverage-final.json | 85 +- apps/web/coverage/lcov-report/index.html | 228 +- .../lcov-report/src/app/(auth)/index.html | 10 +- .../src/app/(auth)/layout.tsx.html | 52 +- .../src/app/(auth)/login/index.html | 32 +- .../src/app/(auth)/login/page.tsx.html | 212 +- .../src/app/(auth)/signup/index.html | 42 +- .../src/app/(auth)/signup/page.tsx.html | 331 +- .../src/app/api/auth/[...nextauth]/index.html | 2 +- .../app/api/auth/[...nextauth]/route.ts.html | 2 +- .../app/courses/[courseId]/debug/index.html | 116 + .../courses/[courseId]/debug/page.tsx.html | 802 ++++ .../documents/[documentId]/preview/index.html | 18 +- .../[documentId]/preview/page.tsx.html | 978 ++++- .../src/app/courses/[courseId]/index.html | 28 +- .../app/courses/[courseId]/layout.tsx.html | 195 +- .../src/app/courses/[courseId]/page.tsx.html | 1050 ++++- .../app/courses/[courseId]/study/index.html | 34 +- .../courses/[courseId]/study/page.tsx.html | 491 ++- .../src/app/courses/error.tsx.html | 166 + .../lcov-report/src/app/courses/index.html | 48 +- .../src/app/courses/layout.tsx.html | 124 + .../lcov-report/src/app/courses/page.tsx.html | 533 ++- .../src/app/dashboard/error.tsx.html | 172 + .../lcov-report/src/app/dashboard/index.html | 23 +- .../src/app/dashboard/page.tsx.html | 16 +- .../lcov-report/src/app/error.tsx.html | 172 + .../coverage/lcov-report/src/app/index.html | 27 +- .../lcov-report/src/app/layout.tsx.html | 38 +- .../lcov-report/src/app/page.tsx.html | 2 +- .../components/courses/CourseCard.tsx.html | 220 +- .../courses/CreateCourseModal.tsx.html | 472 +++ .../courses/ProcessingIndicator.tsx.html | 21 +- .../src/components/courses/index.html | 30 +- .../DocumentProcessingPanel.tsx.html | 46 +- .../components/documents/DocumentRow.tsx.html | 231 +- .../documents/DocumentUpload.tsx.html | 1401 ++++++- .../src/components/documents/index.html | 26 +- .../providers/AuthProvider.tsx.html | 2 +- .../providers/SessionErrorGuard.tsx.html | 133 + .../src/components/providers/index.html | 27 +- .../components/study/CitationCard.tsx.html | 265 ++ .../components/study/ContentChunks.tsx.html | 589 +++ .../src/components/study/LessonNav.tsx.html | 394 ++ .../src/components/study/OutputPane.tsx.html | 1682 +++++++- .../src/components/study/SourcePane.tsx.html | 291 +- .../src/components/study/SplitPane.tsx.html | 148 +- .../components/study/StudyActionBar.tsx.html | 466 +++ .../src/components/study/StudyOutput.tsx.html | 931 +++++ .../src/components/study/index.html | 137 +- .../src/components/ui/BadgeDisplay.tsx.html | 175 + .../src/components/ui/BrandMark.tsx.html | 175 + .../src/components/ui/ErrorBoundary.tsx.html | 268 ++ .../src/components/ui/ProgressBar.tsx.html | 220 + .../src/components/ui/Toast.tsx.html | 352 ++ .../src/components/ui/TopBar.tsx.html | 508 +++ .../lcov-report/src/components/ui/index.html | 191 + apps/web/coverage/lcov-report/src/index.html | 2 +- .../coverage/lcov-report/src/lib/api.ts.html | 33 +- .../lcov-report/src/lib/api/adapters.ts.html | 13 +- .../lcov-report/src/lib/api/courses.ts.html | 21 +- .../lcov-report/src/lib/api/documents.ts.html | 222 +- .../lcov-report/src/lib/api/index.html | 20 +- .../lcov-report/src/lib/api/index.ts.html | 2 +- .../lcov-report/src/lib/auth/config.ts.html | 152 +- .../lcov-report/src/lib/auth/index.html | 18 +- .../coverage/lcov-report/src/lib/index.html | 30 +- .../src/lib/middleware/auth-guard.ts.html | 2 +- .../lcov-report/src/lib/middleware/index.html | 2 +- .../lcov-report/src/lib/services/chat.ts.html | 172 + .../src/lib/services/courses.ts.html | 56 +- .../src/lib/services/debug.ts.html | 259 ++ .../src/lib/services/documents.ts.html | 2 +- .../lcov-report/src/lib/services/index.html | 80 +- .../src/lib/services/progress.ts.html | 391 ++ .../src/lib/services/study.ts.html | 139 + .../lcov-report/src/middleware.ts.html | 2 +- apps/web/coverage/lcov.info | 3527 +++++++++++++---- .../src/app/courses/[courseId]/study/page.tsx | 4 + apps/web/src/components/study/LessonNav.tsx | 2 +- apps/web/src/components/study/OutputPane.tsx | 4 +- 87 files changed, 19495 insertions(+), 2783 deletions(-) create mode 100644 apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/index.html create mode 100644 apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/app/courses/error.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/app/courses/layout.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/app/dashboard/error.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/app/error.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/courses/CreateCourseModal.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/providers/SessionErrorGuard.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/study/CitationCard.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/study/StudyActionBar.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/BadgeDisplay.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html create mode 100644 apps/web/coverage/lcov-report/src/components/ui/index.html create mode 100644 apps/web/coverage/lcov-report/src/lib/services/chat.ts.html create mode 100644 apps/web/coverage/lcov-report/src/lib/services/debug.ts.html create mode 100644 apps/web/coverage/lcov-report/src/lib/services/progress.ts.html create mode 100644 apps/web/coverage/lcov-report/src/lib/services/study.ts.html diff --git a/apps/web/__tests__/integration/auth-flow.test.tsx b/apps/web/__tests__/integration/auth-flow.test.tsx index 8e156c1..d234b3c 100644 --- a/apps/web/__tests__/integration/auth-flow.test.tsx +++ b/apps/web/__tests__/integration/auth-flow.test.tsx @@ -78,8 +78,8 @@ describe('Authentication Flow Integration', () => { // Fill out registration form await userEvent.type(screen.getByLabelText(/display name/i), 'New User'); await userEvent.type(screen.getByLabelText(/email/i), 'newuser@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!'); // Submit form fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -101,14 +101,14 @@ describe('Authentication Flow Integration', () => { 'credentials', expect.objectContaining({ email: 'newuser@example.com', - password: 'SecurePass123', + password: 'SecurePass123!', }) ); }); // Verify redirect await waitFor(() => { - expect(mockRouter.push).toHaveBeenCalledWith('/dashboard'); + expect(mockRouter.push).toHaveBeenCalledWith('/courses'); }); }); @@ -138,7 +138,7 @@ describe('Authentication Flow Integration', () => { // Verify redirect await waitFor(() => { - expect(mockRouter.push).toHaveBeenCalledWith('/dashboard'); + expect(mockRouter.push).toHaveBeenCalledWith('/courses'); }); }); }); @@ -147,7 +147,7 @@ describe('Authentication Flow Integration', () => { it('handles login failure gracefully', async () => { (signIn as jest.Mock).mockResolvedValue({ ok: false, - error: 'Invalid email or password', + error: 'CredentialsSignin', }); render(); @@ -177,8 +177,8 @@ describe('Authentication Flow Integration', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'duplicate@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -196,8 +196,8 @@ describe('Authentication Flow Integration', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'SecurePass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'SecurePass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); diff --git a/apps/web/__tests__/integration/study-flow.test.tsx b/apps/web/__tests__/integration/study-flow.test.tsx index 7c1ef0a..c2c8bb0 100644 --- a/apps/web/__tests__/integration/study-flow.test.tsx +++ b/apps/web/__tests__/integration/study-flow.test.tsx @@ -4,9 +4,18 @@ import userEvent from '@testing-library/user-event'; import { useSession } from 'next-auth/react'; import { useParams } from 'next/navigation'; -// scrollIntoView not implemented in jsdom +// scrollIntoView and matchMedia not implemented in jsdom beforeAll(() => { window.HTMLElement.prototype.scrollIntoView = jest.fn(); + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query: string) => ({ + matches: false, + media: query, + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + })), + }); }); // ── Module mocks ──────────────────────────────────────────────────────────── @@ -18,6 +27,7 @@ jest.mock('next-auth/react', () => ({ jest.mock('next/navigation', () => ({ useParams: jest.fn(), useRouter: jest.fn(() => ({ push: jest.fn() })), + useSearchParams: jest.fn(() => ({ get: jest.fn().mockReturnValue(null) })), })); jest.mock('@/lib/services/courses', () => ({ @@ -36,6 +46,7 @@ jest.mock('@/lib/services/documents', () => ({ jest.mock('@/lib/api/documents', () => ({ getDocumentPreviewView: jest.fn(), + getDocumentListRows: jest.fn(), })); jest.mock('@/lib/services/chat', () => ({ @@ -47,7 +58,7 @@ jest.mock('@/lib/services/chat', () => ({ import { getCourse, getCourseLessons } from '@/lib/services/courses'; import { getCourseProgress, markLessonComplete } from '@/lib/services/progress'; import { getDocuments } from '@/lib/services/documents'; -import { getDocumentPreviewView } from '@/lib/api/documents'; +import { getDocumentPreviewView, getDocumentListRows } from '@/lib/api/documents'; import { sendChatMessage } from '@/lib/services/chat'; // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -79,6 +90,7 @@ function setupMocks() { (getCourseLessons as jest.Mock).mockResolvedValue(LESSONS); (getCourseProgress as jest.Mock).mockResolvedValue(PROGRESS); (getDocuments as jest.Mock).mockResolvedValue([]); + (getDocumentListRows as jest.Mock).mockResolvedValue([]); (getDocumentPreviewView as jest.Mock).mockResolvedValue({ id: 'doc-1', filename: 'guide.pdf', @@ -191,7 +203,7 @@ describe('Study Flow Integration', () => { filename: 'bitcoin-guide.pdf', extractedTextPreview: null, pageCount: 5, - sections: [{ title: 'What is Bitcoin?' }], + sections: ['What is Bitcoin?'], sampleChunks: [ { text: 'Bitcoin is a decentralized digital currency.', section: 'What is Bitcoin?' }, { text: 'Transactions are verified by network nodes.', section: 'What is Bitcoin?' }, @@ -218,7 +230,7 @@ describe('Study Flow Integration', () => { filename: 'guide.pdf', extractedTextPreview: null, pageCount: 2, - sections: [{ title: 'Overview' }, { title: 'Key Concepts' }], + sections: ['Overview', 'Key Concepts'], sampleChunks: [{ text: 'Some chunk text.' }], }); @@ -313,7 +325,7 @@ describe('Study Flow Integration', () => { await waitFor(() => { expect(screen.getByText(/proof of work is the consensus mechanism/i)).toBeInTheDocument(); - expect(screen.getByText('Relevance: 88%')).toBeInTheDocument(); + expect(screen.getByText(/88%/)).toBeInTheDocument(); }); }); }); @@ -383,7 +395,7 @@ describe('Study Flow Integration', () => { fireEvent.click(screen.getByRole('button', { name: /mark as complete/i })); await waitFor(() => { - expect(screen.getByText(/new badge earned/i)).toBeInTheDocument(); + expect(screen.getByText(/badge earned/i)).toBeInTheDocument(); expect(screen.getByText('First Steps')).toBeInTheDocument(); }); }); diff --git a/apps/web/__tests__/unit/OutputPane.test.tsx b/apps/web/__tests__/unit/OutputPane.test.tsx index de1a91e..7457ce8 100644 --- a/apps/web/__tests__/unit/OutputPane.test.tsx +++ b/apps/web/__tests__/unit/OutputPane.test.tsx @@ -46,7 +46,7 @@ describe('OutputPane', () => { it('shows the empty-state prompt when no messages', () => { render(); - expect(screen.getByText(/ask me anything/i)).toBeInTheDocument(); + expect(screen.getByText(/type a topic/i)).toBeInTheDocument(); }); it('shows lesson-specific placeholder when a lesson is selected', () => { @@ -57,7 +57,7 @@ describe('OutputPane', () => { /> ); const textarea = screen.getByRole('textbox', { name: /message input/i }); - expect(textarea).toHaveAttribute('placeholder', 'Ask about "How Mining Works"…'); + expect(textarea).toHaveAttribute('placeholder', 'Ask about "How Mining Works" or pick an action above…'); }); }); @@ -178,7 +178,7 @@ describe('OutputPane', () => { fireEvent.click(screen.getByRole('button', { name: /send message/i })); await waitFor(() => { - expect(screen.getByText('Relevance: 92%')).toBeInTheDocument(); + expect(screen.getByText(/92%/)).toBeInTheDocument(); }); }); diff --git a/apps/web/__tests__/unit/login.test.tsx b/apps/web/__tests__/unit/login.test.tsx index 3749091..2331c4c 100644 --- a/apps/web/__tests__/unit/login.test.tsx +++ b/apps/web/__tests__/unit/login.test.tsx @@ -122,7 +122,7 @@ describe('LoginPage', () => { email: 'test@example.com', password: 'Password123', redirect: false, - callbackUrl: '/dashboard', + callbackUrl: '/courses', }); }); }); @@ -137,7 +137,7 @@ describe('LoginPage', () => { fireEvent.click(screen.getByRole('button', { name: /sign in/i })); await waitFor(() => { - expect(mockRouter.push).toHaveBeenCalledWith('/dashboard'); + expect(mockRouter.push).toHaveBeenCalledWith('/courses'); expect(mockRouter.refresh).toHaveBeenCalled(); }); }); @@ -167,7 +167,7 @@ describe('LoginPage', () => { }); it('displays error message on failed login', async () => { - (signIn as jest.Mock).mockResolvedValue({ ok: false, error: 'Invalid credentials' }); + (signIn as jest.Mock).mockResolvedValue({ ok: false, error: 'CredentialsSignin' }); render(); @@ -176,7 +176,7 @@ describe('LoginPage', () => { fireEvent.click(screen.getByRole('button', { name: /sign in/i })); await waitFor(() => { - expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument(); + expect(screen.getByText(/invalid email or password/i)).toBeInTheDocument(); }); }); diff --git a/apps/web/__tests__/unit/signup.test.tsx b/apps/web/__tests__/unit/signup.test.tsx index 9d8fc2f..d626ed7 100644 --- a/apps/web/__tests__/unit/signup.test.tsx +++ b/apps/web/__tests__/unit/signup.test.tsx @@ -39,7 +39,7 @@ describe('SignupPage', () => { it('renders signup form with all elements', () => { render(); - expect(screen.getByRole('heading', { name: /create your account/i })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /create account/i })).toBeInTheDocument(); expect(screen.getByLabelText(/display name/i)).toBeInTheDocument(); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument(); @@ -51,7 +51,7 @@ describe('SignupPage', () => { it('shows password requirements hint', () => { render(); - expect(screen.getByText(/min 8 characters/i)).toBeInTheDocument(); + expect(screen.getByText(/min 12/i)).toBeInTheDocument(); }); }); @@ -59,8 +59,8 @@ describe('SignupPage', () => { it('shows error when email is empty', async () => { render(); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -73,8 +73,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'invalid-email'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -93,7 +93,7 @@ describe('SignupPage', () => { fireEvent.click(screen.getByRole('button', { name: /create account/i })); await waitFor(() => { - expect(screen.getByText(/at least 8 characters/i)).toBeInTheDocument(); + expect(screen.getByText(/at least 12 characters/i)).toBeInTheDocument(); }); }); @@ -143,7 +143,7 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); await userEvent.type(screen.getByLabelText(/confirm password/i), 'DifferentPass456'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -157,7 +157,7 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -179,8 +179,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -205,8 +205,8 @@ describe('SignupPage', () => { await userEvent.type(screen.getByLabelText(/display name/i), 'Test User'); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -218,7 +218,7 @@ describe('SignupPage', () => { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'test@example.com', - password: 'ValidPass123', + password: 'ValidPass123!', display_name: 'Test User', }), }) @@ -239,8 +239,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -249,7 +249,7 @@ describe('SignupPage', () => { 'credentials', expect.objectContaining({ email: 'test@example.com', - password: 'ValidPass123', + password: 'ValidPass123!', }) ); }); @@ -268,13 +268,13 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); await waitFor(() => { - expect(mockRouter.push).toHaveBeenCalledWith('/dashboard'); + expect(mockRouter.push).toHaveBeenCalledWith('/courses'); }); }); @@ -288,8 +288,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'existing@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -308,8 +308,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); @@ -326,8 +326,8 @@ describe('SignupPage', () => { render(); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); - await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123'); - await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123'); + await userEvent.type(screen.getByLabelText(/^password$/i), 'ValidPass123!'); + await userEvent.type(screen.getByLabelText(/confirm password/i), 'ValidPass123!'); fireEvent.click(screen.getByRole('button', { name: /create account/i })); diff --git a/apps/web/coverage/clover.xml b/apps/web/coverage/clover.xml index 6224420..59ff110 100644 --- a/apps/web/coverage/clover.xml +++ b/apps/web/coverage/clover.xml @@ -1,24 +1,33 @@ - - - + + + - + - - - - + + + + + + + + + + + + + - + @@ -36,133 +45,146 @@ - - - - + + + + + - - - - - - - + + + + + + + + + - - - - + - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + - + @@ -171,154 +193,379 @@ - - - + + + + + + + + + + + + + + + + - - - - - - - - - + - + + + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + + + + + + - - + - - - - + + + + + + + + + + + - - - + + + + + + + + + - - - + + + - - - - + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - + @@ -346,218 +593,602 @@ - - - + + + - + + + + - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - + + + + + + + - - + + - - - + + - - - - - - - - - + + + + + + + + + + - - + + - + - + - - + + - - - + - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + @@ -567,33 +1198,33 @@ - - - - - - + + + + + + + - - + - - - - - + + + + + + - - - - + + + + - @@ -601,60 +1232,87 @@ - + - + + - + - - + - - + + - - - - - - - - - - - + + + + + + + + + + - + + - + + - - - - - + + - - + + + + + + - - - + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -663,41 +1321,47 @@ - - - - - + + + + + - - - + + + + + - - - - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + + + + + + + + - + @@ -718,20 +1382,46 @@ - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + - + + + + - + @@ -755,6 +1445,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/coverage/coverage-final.json b/apps/web/coverage/coverage-final.json index 1bcce70..5cfd81f 100644 --- a/apps/web/coverage/coverage-final.json +++ b/apps/web/coverage/coverage-final.json @@ -1,33 +1,54 @@ -{"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/middleware.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/middleware.ts","statementMap":{"0":{"start":{"line":5,"column":36},"end":{"line":5,"column":50}},"1":{"start":{"line":5,"column":9},"end":{"line":5,"column":27}},"2":{"start":{"line":5,"column":50},"end":{"line":5,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0},"f":{},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"1":{"start":{"line":5,"column":13},"end":{"line":5,"column":21}},"2":{"start":{"line":2,"column":7},"end":{"line":2,"column":null}},"3":{"start":{"line":3,"column":29},"end":{"line":3,"column":null}},"4":{"start":{"line":5,"column":34},"end":{"line":9,"column":null}}},"fnMap":{"0":{"name":"RootLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"loc":{"start":{"line":11,"column":78},"end":{"line":19,"column":1}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/page.tsx","statementMap":{"0":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":8,"column":17},"end":{"line":8,"column":null}},"5":{"start":{"line":9,"column":21},"end":{"line":9,"column":null}},"6":{"start":{"line":11,"column":2},"end":{"line":17,"column":null}},"7":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"8":{"start":{"line":13,"column":6},"end":{"line":13,"column":null}},"9":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"10":{"start":{"line":15,"column":6},"end":{"line":15,"column":null}},"11":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}}},"fnMap":{"0":{"name":"Home","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"loc":{"start":{"line":7,"column":24},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":null}},"loc":{"start":{"line":11,"column":12},"end":{"line":17,"column":5}}}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":2},"end":{"line":27,"column":null}}]},"3":{"loc":{"start":{"line":19,"column":6},"end":{"line":19,"column":60}},"type":"binary-expr","locations":[{"start":{"line":19,"column":6},"end":{"line":19,"column":30}},{"start":{"line":19,"column":30},"end":{"line":19,"column":60}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}}},"fnMap":{"0":{"name":"AuthLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":35}},"loc":{"start":{"line":11,"column":64},"end":{"line":23,"column":null}}}},"branchMap":{},"s":{"0":0},"f":{"0":0},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx","statementMap":{"0":{"start":{"line":18,"column":24},"end":{"line":18,"column":null}},"1":{"start":{"line":7,"column":23},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":17},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":43},"end":{"line":9,"column":null}},"4":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"5":{"start":{"line":19,"column":17},"end":{"line":19,"column":null}},"6":{"start":{"line":20,"column":23},"end":{"line":20,"column":null}},"7":{"start":{"line":21,"column":22},"end":{"line":21,"column":null}},"8":{"start":{"line":22,"column":21},"end":{"line":22,"column":null}},"9":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"10":{"start":{"line":25,"column":34},"end":{"line":25,"column":null}},"11":{"start":{"line":26,"column":36},"end":{"line":26,"column":null}},"12":{"start":{"line":27,"column":30},"end":{"line":27,"column":null}},"13":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"14":{"start":{"line":36,"column":23},"end":{"line":53,"column":null}},"15":{"start":{"line":37,"column":34},"end":{"line":37,"column":null}},"16":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"17":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"18":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"19":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"20":{"start":{"line":47,"column":4},"end":{"line":49,"column":null}},"21":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"22":{"start":{"line":51,"column":4},"end":{"line":51,"column":null}},"23":{"start":{"line":52,"column":4},"end":{"line":52,"column":null}},"24":{"start":{"line":58,"column":23},"end":{"line":90,"column":null}},"25":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"26":{"start":{"line":62,"column":4},"end":{"line":62,"column":null}},"27":{"start":{"line":65,"column":4},"end":{"line":67,"column":null}},"28":{"start":{"line":66,"column":6},"end":{"line":66,"column":null}},"29":{"start":{"line":69,"column":4},"end":{"line":69,"column":null}},"30":{"start":{"line":71,"column":4},"end":{"line":89,"column":null}},"31":{"start":{"line":72,"column":21},"end":{"line":77,"column":null}},"32":{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},"33":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"34":{"start":{"line":81,"column":13},"end":{"line":84,"column":null}},"35":{"start":{"line":82,"column":8},"end":{"line":82,"column":null}},"36":{"start":{"line":83,"column":8},"end":{"line":83,"column":null}},"37":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"38":{"start":{"line":88,"column":6},"end":{"line":88,"column":null}},"39":{"start":{"line":124,"column":31},"end":{"line":124,"column":null}},"40":{"start":{"line":153,"column":31},"end":{"line":153,"column":null}}},"fnMap":{"0":{"name":"LoginPage","decl":{"start":{"line":18,"column":24},"end":{"line":18,"column":null}},"loc":{"start":{"line":18,"column":24},"end":{"line":229,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":36,"column":23},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":23},"end":{"line":53,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":58,"column":23},"end":{"line":58,"column":30}},"loc":{"start":{"line":58,"column":30},"end":{"line":90,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":124,"column":24},"end":{"line":124,"column":25}},"loc":{"start":{"line":124,"column":31},"end":{"line":124,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":153,"column":24},"end":{"line":153,"column":25}},"loc":{"start":{"line":153,"column":31},"end":{"line":153,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":22},"end":{"line":21,"column":null}},"type":"binary-expr","locations":[{"start":{"line":21,"column":22},"end":{"line":21,"column":57}},{"start":{"line":21,"column":57},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"type":"cond-expr","locations":[{"start":{"line":31,"column":38},"end":{"line":31,"column":89}},{"start":{"line":31,"column":89},"end":{"line":31,"column":null}}]},"2":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"4":{"loc":{"start":{"line":47,"column":4},"end":{"line":49,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":49,"column":null}}]},"5":{"loc":{"start":{"line":65,"column":4},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":4},"end":{"line":67,"column":null}}]},"6":{"loc":{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},"type":"if","locations":[{"start":{"line":79,"column":6},"end":{"line":84,"column":null}},{"start":{"line":81,"column":13},"end":{"line":84,"column":null}}]},"7":{"loc":{"start":{"line":81,"column":13},"end":{"line":84,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":13},"end":{"line":84,"column":null}}]},"8":{"loc":{"start":{"line":97,"column":7},"end":{"line":97,"column":null}},"type":"binary-expr","locations":[{"start":{"line":97,"column":7},"end":{"line":97,"column":null}}]},"9":{"loc":{"start":{"line":104,"column":7},"end":{"line":104,"column":21}},"type":"binary-expr","locations":[{"start":{"line":104,"column":7},"end":{"line":104,"column":21}}]},"10":{"loc":{"start":{"line":126,"column":16},"end":{"line":126,"column":null}},"type":"cond-expr","locations":[{"start":{"line":126,"column":31},"end":{"line":126,"column":50}},{"start":{"line":126,"column":50},"end":{"line":126,"column":null}}]},"11":{"loc":{"start":{"line":129,"column":28},"end":{"line":129,"column":null}},"type":"cond-expr","locations":[{"start":{"line":129,"column":43},"end":{"line":129,"column":52}},{"start":{"line":129,"column":52},"end":{"line":129,"column":null}}]},"12":{"loc":{"start":{"line":130,"column":32},"end":{"line":130,"column":null}},"type":"cond-expr","locations":[{"start":{"line":130,"column":47},"end":{"line":130,"column":63}},{"start":{"line":130,"column":63},"end":{"line":130,"column":null}}]},"13":{"loc":{"start":{"line":133,"column":11},"end":{"line":133,"column":23}},"type":"binary-expr","locations":[{"start":{"line":133,"column":11},"end":{"line":133,"column":23}}]},"14":{"loc":{"start":{"line":155,"column":16},"end":{"line":155,"column":null}},"type":"cond-expr","locations":[{"start":{"line":155,"column":34},"end":{"line":155,"column":53}},{"start":{"line":155,"column":53},"end":{"line":155,"column":null}}]},"15":{"loc":{"start":{"line":158,"column":28},"end":{"line":158,"column":null}},"type":"cond-expr","locations":[{"start":{"line":158,"column":46},"end":{"line":158,"column":55}},{"start":{"line":158,"column":55},"end":{"line":158,"column":null}}]},"16":{"loc":{"start":{"line":159,"column":32},"end":{"line":159,"column":null}},"type":"cond-expr","locations":[{"start":{"line":159,"column":50},"end":{"line":159,"column":69}},{"start":{"line":159,"column":69},"end":{"line":159,"column":null}}]},"17":{"loc":{"start":{"line":162,"column":11},"end":{"line":162,"column":26}},"type":"binary-expr","locations":[{"start":{"line":162,"column":11},"end":{"line":162,"column":26}}]},"18":{"loc":{"start":{"line":176,"column":13},"end":{"line":201,"column":null}},"type":"cond-expr","locations":[{"start":{"line":177,"column":14},"end":{"line":201,"column":null}},{"start":{"line":201,"column":14},"end":{"line":201,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":327,"6":327,"7":327,"8":327,"9":327,"10":325,"11":325,"12":325,"13":325,"14":325,"15":15,"16":15,"17":3,"18":12,"19":2,"20":15,"21":6,"22":15,"23":15,"24":325,"25":15,"26":15,"27":15,"28":6,"29":9,"30":9,"31":9,"32":8,"33":2,"34":6,"35":6,"36":6,"37":0,"38":8,"39":179,"40":106},"f":{"0":327,"1":15,"2":15,"3":179,"4":106},"b":{"0":[327,262],"1":[36,289],"2":[3,12],"3":[2],"4":[6],"5":[6],"6":[2,6],"7":[6],"8":[325],"9":[325],"10":[6,319],"11":[6,319],"12":[6,319],"13":[325],"14":[7,318],"15":[7,318],"16":[7,318],"17":[325],"18":[9,316]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx","statementMap":{"0":{"start":{"line":23,"column":24},"end":{"line":23,"column":null}},"1":{"start":{"line":7,"column":17},"end":{"line":7,"column":null}},"2":{"start":{"line":8,"column":26},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":23},"end":{"line":9,"column":null}},"4":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"5":{"start":{"line":13,"column":16},"end":{"line":13,"column":null}},"6":{"start":{"line":24,"column":17},"end":{"line":24,"column":null}},"7":{"start":{"line":26,"column":28},"end":{"line":26,"column":null}},"8":{"start":{"line":27,"column":34},"end":{"line":27,"column":null}},"9":{"start":{"line":28,"column":48},"end":{"line":28,"column":null}},"10":{"start":{"line":29,"column":40},"end":{"line":29,"column":null}},"11":{"start":{"line":30,"column":36},"end":{"line":30,"column":null}},"12":{"start":{"line":31,"column":30},"end":{"line":31,"column":null}},"13":{"start":{"line":36,"column":23},"end":{"line":73,"column":null}},"14":{"start":{"line":37,"column":34},"end":{"line":37,"column":null}},"15":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"16":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"17":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"18":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"19":{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},"20":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"21":{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},"22":{"start":{"line":50,"column":6},"end":{"line":50,"column":null}},"23":{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},"24":{"start":{"line":52,"column":6},"end":{"line":52,"column":null}},"25":{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},"26":{"start":{"line":54,"column":6},"end":{"line":54,"column":null}},"27":{"start":{"line":55,"column":11},"end":{"line":57,"column":null}},"28":{"start":{"line":56,"column":6},"end":{"line":56,"column":null}},"29":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"30":{"start":{"line":61,"column":6},"end":{"line":61,"column":null}},"31":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"32":{"start":{"line":63,"column":6},"end":{"line":63,"column":null}},"33":{"start":{"line":67,"column":4},"end":{"line":69,"column":null}},"34":{"start":{"line":68,"column":6},"end":{"line":68,"column":null}},"35":{"start":{"line":71,"column":4},"end":{"line":71,"column":null}},"36":{"start":{"line":72,"column":4},"end":{"line":72,"column":null}},"37":{"start":{"line":78,"column":23},"end":{"line":138,"column":null}},"38":{"start":{"line":79,"column":4},"end":{"line":79,"column":null}},"39":{"start":{"line":82,"column":4},"end":{"line":82,"column":null}},"40":{"start":{"line":85,"column":4},"end":{"line":87,"column":null}},"41":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"42":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}},"43":{"start":{"line":91,"column":4},"end":{"line":137,"column":null}},"44":{"start":{"line":93,"column":31},"end":{"line":103,"column":null}},"45":{"start":{"line":105,"column":6},"end":{"line":116,"column":null}},"46":{"start":{"line":106,"column":22},"end":{"line":106,"column":null}},"47":{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},"48":{"start":{"line":109,"column":10},"end":{"line":109,"column":null}},"49":{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},"50":{"start":{"line":111,"column":10},"end":{"line":111,"column":null}},"51":{"start":{"line":113,"column":10},"end":{"line":113,"column":null}},"52":{"start":{"line":115,"column":8},"end":{"line":115,"column":null}},"53":{"start":{"line":119,"column":21},"end":{"line":124,"column":null}},"54":{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},"55":{"start":{"line":128,"column":8},"end":{"line":128,"column":null}},"56":{"start":{"line":129,"column":13},"end":{"line":132,"column":null}},"57":{"start":{"line":130,"column":8},"end":{"line":130,"column":null}},"58":{"start":{"line":131,"column":8},"end":{"line":131,"column":null}},"59":{"start":{"line":134,"column":6},"end":{"line":134,"column":null}},"60":{"start":{"line":136,"column":6},"end":{"line":136,"column":null}},"61":{"start":{"line":164,"column":31},"end":{"line":164,"column":null}},"62":{"start":{"line":193,"column":31},"end":{"line":193,"column":null}},"63":{"start":{"line":222,"column":31},"end":{"line":222,"column":null}},"64":{"start":{"line":255,"column":31},"end":{"line":255,"column":null}}},"fnMap":{"0":{"name":"SignupPage","decl":{"start":{"line":23,"column":24},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":24},"end":{"line":331,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":36,"column":23},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":23},"end":{"line":73,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":78,"column":23},"end":{"line":78,"column":30}},"loc":{"start":{"line":78,"column":30},"end":{"line":138,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":164,"column":24},"end":{"line":164,"column":25}},"loc":{"start":{"line":164,"column":31},"end":{"line":164,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":193,"column":24},"end":{"line":193,"column":25}},"loc":{"start":{"line":193,"column":31},"end":{"line":193,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":222,"column":24},"end":{"line":222,"column":25}},"loc":{"start":{"line":222,"column":31},"end":{"line":222,"column":null}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":255,"column":24},"end":{"line":255,"column":25}},"loc":{"start":{"line":255,"column":31},"end":{"line":255,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":16},"end":{"line":13,"column":null}},"type":"binary-expr","locations":[{"start":{"line":13,"column":16},"end":{"line":13,"column":47}},{"start":{"line":13,"column":51},"end":{"line":13,"column":null}}]},"1":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":57,"column":null}},{"start":{"line":49,"column":11},"end":{"line":57,"column":null}}]},"4":{"loc":{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":11},"end":{"line":57,"column":null}},{"start":{"line":51,"column":11},"end":{"line":57,"column":null}}]},"5":{"loc":{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":51,"column":11},"end":{"line":57,"column":null}},{"start":{"line":53,"column":11},"end":{"line":57,"column":null}}]},"6":{"loc":{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":53,"column":11},"end":{"line":57,"column":null}},{"start":{"line":55,"column":11},"end":{"line":57,"column":null}}]},"7":{"loc":{"start":{"line":55,"column":11},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":11},"end":{"line":57,"column":null}}]},"8":{"loc":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"9":{"loc":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"10":{"loc":{"start":{"line":67,"column":4},"end":{"line":69,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":4},"end":{"line":69,"column":null}}]},"11":{"loc":{"start":{"line":67,"column":8},"end":{"line":67,"column":47}},"type":"binary-expr","locations":[{"start":{"line":67,"column":8},"end":{"line":67,"column":23}},{"start":{"line":67,"column":23},"end":{"line":67,"column":47}}]},"12":{"loc":{"start":{"line":85,"column":4},"end":{"line":87,"column":null}},"type":"if","locations":[{"start":{"line":85,"column":4},"end":{"line":87,"column":null}}]},"13":{"loc":{"start":{"line":101,"column":24},"end":{"line":101,"column":null}},"type":"binary-expr","locations":[{"start":{"line":101,"column":24},"end":{"line":101,"column":39}},{"start":{"line":101,"column":39},"end":{"line":101,"column":null}}]},"14":{"loc":{"start":{"line":105,"column":6},"end":{"line":116,"column":null}},"type":"if","locations":[{"start":{"line":105,"column":6},"end":{"line":116,"column":null}}]},"15":{"loc":{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":108,"column":8},"end":{"line":114,"column":null}},{"start":{"line":110,"column":15},"end":{"line":114,"column":null}}]},"16":{"loc":{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":110,"column":15},"end":{"line":114,"column":null}},{"start":{"line":112,"column":15},"end":{"line":114,"column":null}}]},"17":{"loc":{"start":{"line":111,"column":31},"end":{"line":111,"column":68}},"type":"binary-expr","locations":[{"start":{"line":111,"column":31},"end":{"line":111,"column":43}},{"start":{"line":111,"column":47},"end":{"line":111,"column":68}}]},"18":{"loc":{"start":{"line":113,"column":31},"end":{"line":113,"column":69}},"type":"binary-expr","locations":[{"start":{"line":113,"column":31},"end":{"line":113,"column":43}},{"start":{"line":113,"column":47},"end":{"line":113,"column":69}}]},"19":{"loc":{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},"type":"if","locations":[{"start":{"line":126,"column":6},"end":{"line":132,"column":null}},{"start":{"line":129,"column":13},"end":{"line":132,"column":null}}]},"20":{"loc":{"start":{"line":129,"column":13},"end":{"line":132,"column":null}},"type":"if","locations":[{"start":{"line":129,"column":13},"end":{"line":132,"column":null}}]},"21":{"loc":{"start":{"line":145,"column":7},"end":{"line":145,"column":21}},"type":"binary-expr","locations":[{"start":{"line":145,"column":7},"end":{"line":145,"column":21}}]},"22":{"loc":{"start":{"line":166,"column":16},"end":{"line":166,"column":null}},"type":"cond-expr","locations":[{"start":{"line":166,"column":37},"end":{"line":166,"column":56}},{"start":{"line":166,"column":56},"end":{"line":166,"column":null}}]},"23":{"loc":{"start":{"line":169,"column":28},"end":{"line":169,"column":null}},"type":"cond-expr","locations":[{"start":{"line":169,"column":49},"end":{"line":169,"column":58}},{"start":{"line":169,"column":58},"end":{"line":169,"column":null}}]},"24":{"loc":{"start":{"line":170,"column":32},"end":{"line":170,"column":null}},"type":"cond-expr","locations":[{"start":{"line":170,"column":53},"end":{"line":170,"column":75}},{"start":{"line":170,"column":75},"end":{"line":170,"column":null}}]},"25":{"loc":{"start":{"line":173,"column":11},"end":{"line":173,"column":29}},"type":"binary-expr","locations":[{"start":{"line":173,"column":11},"end":{"line":173,"column":29}}]},"26":{"loc":{"start":{"line":195,"column":16},"end":{"line":195,"column":null}},"type":"cond-expr","locations":[{"start":{"line":195,"column":31},"end":{"line":195,"column":50}},{"start":{"line":195,"column":50},"end":{"line":195,"column":null}}]},"27":{"loc":{"start":{"line":198,"column":28},"end":{"line":198,"column":null}},"type":"cond-expr","locations":[{"start":{"line":198,"column":43},"end":{"line":198,"column":52}},{"start":{"line":198,"column":52},"end":{"line":198,"column":null}}]},"28":{"loc":{"start":{"line":199,"column":32},"end":{"line":199,"column":null}},"type":"cond-expr","locations":[{"start":{"line":199,"column":47},"end":{"line":199,"column":63}},{"start":{"line":199,"column":63},"end":{"line":199,"column":null}}]},"29":{"loc":{"start":{"line":202,"column":11},"end":{"line":202,"column":23}},"type":"binary-expr","locations":[{"start":{"line":202,"column":11},"end":{"line":202,"column":23}}]},"30":{"loc":{"start":{"line":224,"column":16},"end":{"line":224,"column":null}},"type":"cond-expr","locations":[{"start":{"line":224,"column":34},"end":{"line":224,"column":53}},{"start":{"line":224,"column":53},"end":{"line":224,"column":null}}]},"31":{"loc":{"start":{"line":227,"column":28},"end":{"line":227,"column":null}},"type":"cond-expr","locations":[{"start":{"line":227,"column":46},"end":{"line":227,"column":55}},{"start":{"line":227,"column":55},"end":{"line":227,"column":null}}]},"32":{"loc":{"start":{"line":228,"column":32},"end":{"line":228,"column":null}},"type":"cond-expr","locations":[{"start":{"line":228,"column":50},"end":{"line":228,"column":69}},{"start":{"line":228,"column":69},"end":{"line":228,"column":null}}]},"33":{"loc":{"start":{"line":232,"column":12},"end":{"line":236,"column":13}},"type":"cond-expr","locations":[{"start":{"line":232,"column":12},"end":{"line":236,"column":13}}]},"34":{"loc":{"start":{"line":257,"column":16},"end":{"line":257,"column":null}},"type":"cond-expr","locations":[{"start":{"line":257,"column":41},"end":{"line":257,"column":60}},{"start":{"line":257,"column":60},"end":{"line":257,"column":null}}]},"35":{"loc":{"start":{"line":260,"column":28},"end":{"line":260,"column":null}},"type":"cond-expr","locations":[{"start":{"line":260,"column":53},"end":{"line":260,"column":62}},{"start":{"line":260,"column":62},"end":{"line":260,"column":null}}]},"36":{"loc":{"start":{"line":261,"column":32},"end":{"line":261,"column":null}},"type":"cond-expr","locations":[{"start":{"line":261,"column":57},"end":{"line":261,"column":83}},{"start":{"line":261,"column":83},"end":{"line":261,"column":null}}]},"37":{"loc":{"start":{"line":264,"column":11},"end":{"line":264,"column":33}},"type":"binary-expr","locations":[{"start":{"line":264,"column":11},"end":{"line":264,"column":33}}]},"38":{"loc":{"start":{"line":278,"column":13},"end":{"line":303,"column":null}},"type":"cond-expr","locations":[{"start":{"line":279,"column":14},"end":{"line":303,"column":null}},{"start":{"line":303,"column":14},"end":{"line":303,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":774,"7":774,"8":772,"9":772,"10":772,"11":772,"12":772,"13":772,"14":18,"15":18,"16":1,"17":17,"18":1,"19":18,"20":0,"21":18,"22":1,"23":17,"24":1,"25":16,"26":1,"27":15,"28":1,"29":18,"30":1,"31":17,"32":1,"33":18,"34":0,"35":18,"36":18,"37":772,"38":18,"39":18,"40":18,"41":8,"42":10,"43":10,"44":10,"45":8,"46":3,"47":3,"48":2,"49":1,"50":0,"51":1,"52":3,"53":5,"54":5,"55":0,"56":5,"57":5,"58":5,"59":1,"60":9,"61":17,"62":281,"63":217,"64":209},"f":{"0":774,"1":18,"2":18,"3":17,"4":281,"5":217,"6":209},"b":{"0":[2,2],"1":[1,17],"2":[1],"3":[0,18],"4":[1,17],"5":[1,16],"6":[1,15],"7":[1],"8":[1,17],"9":[1],"10":[0],"11":[18,2],"12":[8],"13":[10,8],"14":[3],"15":[2,1],"16":[0,1],"17":[0,0],"18":[1,0],"19":[0,5],"20":[5],"21":[772],"22":[0,772],"23":[0,772],"24":[0,772],"25":[772],"26":[4,768],"27":[4,768],"28":[4,768],"29":[772],"30":[4,768],"31":[4,768],"32":[4,768],"33":[4],"34":[2,770],"35":[2,770],"36":[2,770],"37":[772],"38":[10,762]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts","statementMap":{"0":{"start":{"line":9,"column":9},"end":{"line":9,"column":20}},"1":{"start":{"line":9,"column":25},"end":{"line":9,"column":36}},"2":{"start":{"line":4,"column":21},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":28},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":16},"end":{"line":7,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx","statementMap":{"0":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":27},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":27},"end":{"line":8,"column":null}},"7":{"start":{"line":11,"column":36},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":32},"end":{"line":13,"column":null}},"10":{"start":{"line":14,"column":32},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":28},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":2},"end":{"line":36,"column":null}},"13":{"start":{"line":18,"column":4},"end":{"line":21,"column":null}},"14":{"start":{"line":19,"column":6},"end":{"line":19,"column":null}},"15":{"start":{"line":20,"column":6},"end":{"line":20,"column":null}},"16":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"17":{"start":{"line":22,"column":36},"end":{"line":22,"column":null}},"18":{"start":{"line":25,"column":6},"end":{"line":32,"column":null}},"19":{"start":{"line":26,"column":21},"end":{"line":26,"column":null}},"20":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"21":{"start":{"line":29,"column":8},"end":{"line":29,"column":null}},"22":{"start":{"line":31,"column":8},"end":{"line":31,"column":null}},"23":{"start":{"line":35,"column":4},"end":{"line":35,"column":null}},"24":{"start":{"line":38,"column":2},"end":{"line":60,"column":null}},"25":{"start":{"line":49,"column":14},"end":{"line":49,"column":27}},"26":{"start":{"line":80,"column":29},"end":{"line":80,"column":null}},"27":{"start":{"line":97,"column":14},"end":{"line":97,"column":42}}},"fnMap":{"0":{"name":"CoursesPage","decl":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"loc":{"start":{"line":10,"column":24},"end":{"line":104,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"loc":{"start":{"line":17,"column":12},"end":{"line":36,"column":5}}},"2":{"name":"fetchCourses","decl":{"start":{"line":24,"column":19},"end":{"line":24,"column":null}},"loc":{"start":{"line":24,"column":19},"end":{"line":33,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":27},"end":{"line":48,"column":28}},"loc":{"start":{"line":49,"column":14},"end":{"line":49,"column":27}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":80,"column":23},"end":{"line":80,"column":29}},"loc":{"start":{"line":80,"column":29},"end":{"line":80,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":96,"column":25},"end":{"line":96,"column":26}},"loc":{"start":{"line":97,"column":14},"end":{"line":97,"column":42}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":4},"end":{"line":21,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":22,"column":null}}]},"2":{"loc":{"start":{"line":29,"column":17},"end":{"line":29,"column":null}},"type":"cond-expr","locations":[{"start":{"line":29,"column":40},"end":{"line":29,"column":51}},{"start":{"line":29,"column":54},"end":{"line":29,"column":null}}]},"3":{"loc":{"start":{"line":38,"column":2},"end":{"line":60,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":2},"end":{"line":60,"column":null}}]},"4":{"loc":{"start":{"line":38,"column":6},"end":{"line":38,"column":71}},"type":"binary-expr","locations":[{"start":{"line":38,"column":6},"end":{"line":38,"column":31}},{"start":{"line":38,"column":31},"end":{"line":38,"column":61}},{"start":{"line":38,"column":61},"end":{"line":38,"column":71}}]},"5":{"loc":{"start":{"line":77,"column":10},"end":{"line":86,"column":20}},"type":"cond-expr","locations":[{"start":{"line":77,"column":10},"end":{"line":86,"column":20}}]},"6":{"loc":{"start":{"line":87,"column":10},"end":{"line":95,"column":11}},"type":"cond-expr","locations":[{"start":{"line":87,"column":10},"end":{"line":95,"column":11}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0],"4":[0,0,0],"5":[0],"6":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":37}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":39},"end":{"line":4,"column":null}},"3":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"4":{"start":{"line":13,"column":19},"end":{"line":13,"column":null}},"5":{"start":{"line":14,"column":19},"end":{"line":14,"column":34}},"6":{"start":{"line":16,"column":19},"end":{"line":19,"column":null}},"7":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"8":{"start":{"line":44,"column":14},"end":{"line":45,"column":null}}},"fnMap":{"0":{"name":"CourseLayout","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":37}},"loc":{"start":{"line":11,"column":68},"end":{"line":62,"column":null}}},"1":{"name":"isActive","decl":{"start":{"line":21,"column":11},"end":{"line":21,"column":20}},"loc":{"start":{"line":21,"column":49},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":43,"column":26},"end":{"line":43,"column":27}},"loc":{"start":{"line":44,"column":14},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":11},"end":{"line":22,"column":null}},"type":"cond-expr","locations":[{"start":{"line":22,"column":19},"end":{"line":22,"column":39}},{"start":{"line":22,"column":39},"end":{"line":22,"column":null}}]},"1":{"loc":{"start":{"line":48,"column":18},"end":{"line":50,"column":null}},"type":"cond-expr","locations":[{"start":{"line":49,"column":22},"end":{"line":49,"column":null}},{"start":{"line":50,"column":22},"end":{"line":50,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx","statementMap":{"0":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":70},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":29},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":31},"end":{"line":8,"column":null}},"7":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":19},"end":{"line":13,"column":34}},"10":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":30},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"14":{"start":{"line":19,"column":32},"end":{"line":19,"column":null}},"15":{"start":{"line":20,"column":28},"end":{"line":20,"column":null}},"16":{"start":{"line":21,"column":44},"end":{"line":21,"column":null}},"17":{"start":{"line":23,"column":27},"end":{"line":25,"column":null}},"18":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"19":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"20":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"21":{"start":{"line":31,"column":2},"end":{"line":48,"column":null}},"22":{"start":{"line":33,"column":6},"end":{"line":44,"column":null}},"23":{"start":{"line":34,"column":42},"end":{"line":37,"column":null}},"24":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"25":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"26":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"27":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"28":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"29":{"start":{"line":47,"column":18},"end":{"line":47,"column":null}},"30":{"start":{"line":50,"column":2},"end":{"line":71,"column":null}},"31":{"start":{"line":60,"column":16},"end":{"line":60,"column":29}},"32":{"start":{"line":73,"column":2},"end":{"line":87,"column":null}},"33":{"start":{"line":79,"column":27},"end":{"line":79,"column":null}},"34":{"start":{"line":116,"column":20},"end":{"line":116,"column":40}}},"fnMap":{"0":{"name":"CourseWorkspacePage","decl":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"loc":{"start":{"line":10,"column":24},"end":{"line":157,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":23,"column":39},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":39},"end":{"line":25,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":24,"column":21},"end":{"line":24,"column":22}},"loc":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}}},"3":{"name":"handleViewPreview","decl":{"start":{"line":27,"column":11},"end":{"line":27,"column":29}},"loc":{"start":{"line":27,"column":47},"end":{"line":29,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"loc":{"start":{"line":31,"column":12},"end":{"line":48,"column":5}}},"5":{"name":"load","decl":{"start":{"line":32,"column":19},"end":{"line":32,"column":null}},"loc":{"start":{"line":32,"column":19},"end":{"line":45,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":59,"column":29},"end":{"line":59,"column":30}},"loc":{"start":{"line":60,"column":16},"end":{"line":60,"column":29}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":79,"column":21},"end":{"line":79,"column":27}},"loc":{"start":{"line":79,"column":27},"end":{"line":79,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":115,"column":31},"end":{"line":115,"column":32}},"loc":{"start":{"line":116,"column":20},"end":{"line":116,"column":40}}}},"branchMap":{"0":{"loc":{"start":{"line":41,"column":17},"end":{"line":41,"column":null}},"type":"cond-expr","locations":[{"start":{"line":41,"column":40},"end":{"line":41,"column":51}},{"start":{"line":41,"column":54},"end":{"line":41,"column":null}}]},"1":{"loc":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":47,"column":null}}]},"2":{"loc":{"start":{"line":50,"column":2},"end":{"line":71,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":2},"end":{"line":71,"column":null}}]},"3":{"loc":{"start":{"line":73,"column":2},"end":{"line":87,"column":null}},"type":"if","locations":[{"start":{"line":73,"column":2},"end":{"line":87,"column":null}}]},"4":{"loc":{"start":{"line":73,"column":6},"end":{"line":73,"column":24}},"type":"binary-expr","locations":[{"start":{"line":73,"column":6},"end":{"line":73,"column":15}},{"start":{"line":73,"column":15},"end":{"line":73,"column":24}}]},"5":{"loc":{"start":{"line":77,"column":47},"end":{"line":77,"column":null}},"type":"binary-expr","locations":[{"start":{"line":77,"column":47},"end":{"line":77,"column":56}},{"start":{"line":77,"column":56},"end":{"line":77,"column":null}}]},"6":{"loc":{"start":{"line":93,"column":9},"end":{"line":93,"column":27}},"type":"binary-expr","locations":[{"start":{"line":93,"column":9},"end":{"line":93,"column":27}}]},"7":{"loc":{"start":{"line":107,"column":16},"end":{"line":114,"column":17}},"type":"cond-expr","locations":[{"start":{"line":107,"column":16},"end":{"line":114,"column":17}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0],"4":[0,0],"5":[0,0],"6":[0],"7":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx","statementMap":{"0":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":39},"end":{"line":6,"column":null}},"5":{"start":{"line":10,"column":17},"end":{"line":10,"column":null}},"6":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"7":{"start":{"line":12,"column":19},"end":{"line":12,"column":34}},"8":{"start":{"line":13,"column":21},"end":{"line":13,"column":38}},"9":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"10":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"11":{"start":{"line":17,"column":32},"end":{"line":17,"column":null}},"12":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"13":{"start":{"line":19,"column":28},"end":{"line":19,"column":null}},"14":{"start":{"line":21,"column":15},"end":{"line":31,"column":null}},"15":{"start":{"line":22,"column":4},"end":{"line":30,"column":null}},"16":{"start":{"line":23,"column":6},"end":{"line":23,"column":null}},"17":{"start":{"line":24,"column":19},"end":{"line":24,"column":null}},"18":{"start":{"line":25,"column":6},"end":{"line":25,"column":null}},"19":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"20":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"21":{"start":{"line":33,"column":2},"end":{"line":35,"column":null}},"22":{"start":{"line":34,"column":4},"end":{"line":34,"column":null}},"23":{"start":{"line":37,"column":2},"end":{"line":47,"column":null}},"24":{"start":{"line":49,"column":2},"end":{"line":71,"column":null}},"25":{"start":{"line":62,"column":29},"end":{"line":62,"column":null}},"26":{"start":{"line":78,"column":25},"end":{"line":78,"column":null}},"27":{"start":{"line":120,"column":18},"end":{"line":120,"column":31}},"28":{"start":{"line":143,"column":18},"end":{"line":143,"column":31}}},"fnMap":{"0":{"name":"DocumentPreviewPage","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":24},"end":{"line":159,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":21,"column":27},"end":{"line":21,"column":null}},"loc":{"start":{"line":21,"column":27},"end":{"line":31,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":33,"column":12},"end":{"line":33,"column":null}},"loc":{"start":{"line":33,"column":12},"end":{"line":35,"column":5}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":62,"column":23},"end":{"line":62,"column":29}},"loc":{"start":{"line":62,"column":29},"end":{"line":62,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":78,"column":19},"end":{"line":78,"column":25}},"loc":{"start":{"line":78,"column":25},"end":{"line":78,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":119,"column":38},"end":{"line":119,"column":39}},"loc":{"start":{"line":120,"column":18},"end":{"line":120,"column":31}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":142,"column":42},"end":{"line":142,"column":43}},"loc":{"start":{"line":143,"column":18},"end":{"line":143,"column":31}}}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":15},"end":{"line":27,"column":null}},"type":"cond-expr","locations":[{"start":{"line":27,"column":38},"end":{"line":27,"column":49}},{"start":{"line":27,"column":52},"end":{"line":27,"column":null}}]},"1":{"loc":{"start":{"line":37,"column":2},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":47,"column":null}}]},"2":{"loc":{"start":{"line":49,"column":2},"end":{"line":71,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":71,"column":null}}]},"3":{"loc":{"start":{"line":49,"column":6},"end":{"line":49,"column":25}},"type":"binary-expr","locations":[{"start":{"line":49,"column":6},"end":{"line":49,"column":15}},{"start":{"line":49,"column":15},"end":{"line":49,"column":25}}]},"4":{"loc":{"start":{"line":53,"column":47},"end":{"line":53,"column":null}},"type":"binary-expr","locations":[{"start":{"line":53,"column":47},"end":{"line":53,"column":56}},{"start":{"line":53,"column":56},"end":{"line":53,"column":null}}]},"5":{"loc":{"start":{"line":88,"column":11},"end":{"line":88,"column":null}},"type":"binary-expr","locations":[{"start":{"line":88,"column":11},"end":{"line":88,"column":null}}]},"6":{"loc":{"start":{"line":89,"column":74},"end":{"line":89,"column":null}},"type":"cond-expr","locations":[{"start":{"line":89,"column":100},"end":{"line":89,"column":106}},{"start":{"line":89,"column":106},"end":{"line":89,"column":null}}]},"7":{"loc":{"start":{"line":102,"column":14},"end":{"line":106,"column":15}},"type":"cond-expr","locations":[{"start":{"line":102,"column":14},"end":{"line":106,"column":15}}]},"8":{"loc":{"start":{"line":118,"column":14},"end":{"line":129,"column":15}},"type":"cond-expr","locations":[{"start":{"line":118,"column":14},"end":{"line":129,"column":15}}]},"9":{"loc":{"start":{"line":117,"column":13},"end":{"line":117,"column":null}},"type":"binary-expr","locations":[{"start":{"line":117,"column":13},"end":{"line":117,"column":29}},{"start":{"line":117,"column":33},"end":{"line":117,"column":null}}]},"10":{"loc":{"start":{"line":141,"column":14},"end":{"line":152,"column":15}},"type":"cond-expr","locations":[{"start":{"line":141,"column":14},"end":{"line":152,"column":15}}]},"11":{"loc":{"start":{"line":140,"column":13},"end":{"line":140,"column":null}},"type":"binary-expr","locations":[{"start":{"line":140,"column":13},"end":{"line":140,"column":33}},{"start":{"line":140,"column":37},"end":{"line":140,"column":null}}]},"12":{"loc":{"start":{"line":146,"column":23},"end":{"line":146,"column":null}},"type":"cond-expr","locations":[{"start":{"line":146,"column":51},"end":{"line":146,"column":59}},{"start":{"line":146,"column":59},"end":{"line":146,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0],"6":[0,0],"7":[0],"8":[0],"9":[0,0],"10":[0],"11":[0,0],"12":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx","statementMap":{"0":{"start":{"line":11,"column":24},"end":{"line":11,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":39},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":26},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":27},"end":{"line":8,"column":null}},"7":{"start":{"line":9,"column":27},"end":{"line":9,"column":null}},"8":{"start":{"line":12,"column":17},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":19},"end":{"line":13,"column":34}},"10":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"11":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"12":{"start":{"line":17,"column":30},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":32},"end":{"line":18,"column":null}},"14":{"start":{"line":20,"column":2},"end":{"line":32,"column":null}},"15":{"start":{"line":22,"column":6},"end":{"line":29,"column":null}},"16":{"start":{"line":23,"column":21},"end":{"line":23,"column":null}},"17":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"18":{"start":{"line":28,"column":8},"end":{"line":28,"column":null}},"19":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"20":{"start":{"line":31,"column":18},"end":{"line":31,"column":null}},"21":{"start":{"line":34,"column":2},"end":{"line":40,"column":null}}},"fnMap":{"0":{"name":"StudyPage","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":null}},"loc":{"start":{"line":11,"column":24},"end":{"line":51,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"loc":{"start":{"line":20,"column":12},"end":{"line":32,"column":5}}},"2":{"name":"load","decl":{"start":{"line":21,"column":19},"end":{"line":21,"column":null}},"loc":{"start":{"line":21,"column":19},"end":{"line":30,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":31,"column":4},"end":{"line":31,"column":null}}]},"1":{"loc":{"start":{"line":34,"column":2},"end":{"line":40,"column":null}},"type":"if","locations":[{"start":{"line":34,"column":2},"end":{"line":40,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0],"1":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx","statementMap":{"0":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"6":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"7":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":32},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":46},"end":{"line":13,"column":null}},"10":{"start":{"line":15,"column":2},"end":{"line":30,"column":null}},"11":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"12":{"start":{"line":16,"column":36},"end":{"line":16,"column":null}},"13":{"start":{"line":19,"column":6},"end":{"line":26,"column":null}},"14":{"start":{"line":20,"column":21},"end":{"line":20,"column":null}},"15":{"start":{"line":21,"column":8},"end":{"line":21,"column":null}},"16":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"17":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"18":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"19":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"20":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"22":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}},"23":{"start":{"line":49,"column":4},"end":{"line":49,"column":null}}},"fnMap":{"0":{"name":"DashboardPage","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":24},"end":{"line":141,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"loc":{"start":{"line":15,"column":12},"end":{"line":30,"column":5}}},"2":{"name":"fetchCourses","decl":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"loc":{"start":{"line":18,"column":19},"end":{"line":27,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":24},"end":{"line":48,"column":null}},"loc":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":2},"end":{"line":41,"column":null}}]},"2":{"loc":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":43,"column":2},"end":{"line":46,"column":null}}]},"3":{"loc":{"start":{"line":72,"column":23},"end":{"line":72,"column":82}},"type":"binary-expr","locations":[{"start":{"line":72,"column":23},"end":{"line":72,"column":61}},{"start":{"line":72,"column":61},"end":{"line":72,"column":82}}]},"4":{"loc":{"start":{"line":87,"column":21},"end":{"line":87,"column":null}},"type":"binary-expr","locations":[{"start":{"line":87,"column":21},"end":{"line":87,"column":60}},{"start":{"line":87,"column":60},"end":{"line":87,"column":null}}]},"5":{"loc":{"start":{"line":93,"column":21},"end":{"line":93,"column":null}},"type":"binary-expr","locations":[{"start":{"line":93,"column":21},"end":{"line":93,"column":53}},{"start":{"line":93,"column":53},"end":{"line":93,"column":null}}]},"6":{"loc":{"start":{"line":106,"column":21},"end":{"line":109,"column":36}},"type":"cond-expr","locations":[{"start":{"line":107,"column":22},"end":{"line":109,"column":30}},{"start":{"line":109,"column":22},"end":{"line":109,"column":36}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx","statementMap":{"0":{"start":{"line":10,"column":16},"end":{"line":10,"column":27}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}}},"fnMap":{"0":{"name":"CourseCard","decl":{"start":{"line":10,"column":16},"end":{"line":10,"column":27}},"loc":{"start":{"line":10,"column":54},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":13},"end":{"line":22,"column":31}},"type":"binary-expr","locations":[{"start":{"line":22,"column":13},"end":{"line":22,"column":31}}]}},"s":{"0":0,"1":0},"f":{"0":0},"b":{"0":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/DocumentList.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/DocumentList.tsx","statementMap":{"0":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"1":{"start":{"line":3,"column":57},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":36},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":40},"end":{"line":7,"column":null}},"5":{"start":{"line":16,"column":30},"end":{"line":16,"column":null}},"6":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"7":{"start":{"line":19,"column":20},"end":{"line":19,"column":null}},"8":{"start":{"line":20,"column":2},"end":{"line":20,"column":null}},"9":{"start":{"line":20,"column":27},"end":{"line":20,"column":null}},"10":{"start":{"line":21,"column":2},"end":{"line":21,"column":null}},"11":{"start":{"line":25,"column":2},"end":{"line":34,"column":null}},"12":{"start":{"line":26,"column":4},"end":{"line":31,"column":null}},"13":{"start":{"line":33,"column":4},"end":{"line":33,"column":null}},"14":{"start":{"line":43,"column":36},"end":{"line":43,"column":null}},"15":{"start":{"line":44,"column":32},"end":{"line":44,"column":null}},"16":{"start":{"line":45,"column":28},"end":{"line":45,"column":null}},"17":{"start":{"line":46,"column":38},"end":{"line":46,"column":null}},"18":{"start":{"line":47,"column":38},"end":{"line":47,"column":null}},"19":{"start":{"line":48,"column":18},"end":{"line":48,"column":null}},"20":{"start":{"line":50,"column":25},"end":{"line":69,"column":null}},"21":{"start":{"line":52,"column":6},"end":{"line":67,"column":null}},"22":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"23":{"start":{"line":53,"column":21},"end":{"line":53,"column":null}},"24":{"start":{"line":54,"column":21},"end":{"line":54,"column":null}},"25":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"26":{"start":{"line":57,"column":24},"end":{"line":57,"column":null}},"27":{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},"28":{"start":{"line":59,"column":10},"end":{"line":59,"column":null}},"29":{"start":{"line":60,"column":10},"end":{"line":60,"column":null}},"30":{"start":{"line":61,"column":15},"end":{"line":63,"column":null}},"31":{"start":{"line":62,"column":10},"end":{"line":62,"column":null}},"32":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"33":{"start":{"line":66,"column":8},"end":{"line":66,"column":null}},"34":{"start":{"line":72,"column":2},"end":{"line":74,"column":null}},"35":{"start":{"line":73,"column":4},"end":{"line":73,"column":null}},"36":{"start":{"line":76,"column":2},"end":{"line":84,"column":null}},"37":{"start":{"line":77,"column":26},"end":{"line":77,"column":null}},"38":{"start":{"line":77,"column":48},"end":{"line":77,"column":61}},"39":{"start":{"line":78,"column":4},"end":{"line":80,"column":null}},"40":{"start":{"line":79,"column":6},"end":{"line":79,"column":null}},"41":{"start":{"line":79,"column":42},"end":{"line":79,"column":64}},"42":{"start":{"line":81,"column":4},"end":{"line":83,"column":null}},"43":{"start":{"line":82,"column":6},"end":{"line":82,"column":null}},"44":{"start":{"line":82,"column":27},"end":{"line":82,"column":null}},"45":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"46":{"start":{"line":88,"column":4},"end":{"line":88,"column":null}},"47":{"start":{"line":92,"column":4},"end":{"line":92,"column":null}},"48":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}},"49":{"start":{"line":95,"column":2},"end":{"line":109,"column":null}},"50":{"start":{"line":99,"column":10},"end":{"line":99,"column":23}},"51":{"start":{"line":111,"column":2},"end":{"line":123,"column":null}},"52":{"start":{"line":116,"column":25},"end":{"line":116,"column":null}},"53":{"start":{"line":125,"column":2},"end":{"line":145,"column":null}},"54":{"start":{"line":178,"column":29},"end":{"line":178,"column":50}},"55":{"start":{"line":179,"column":10},"end":{"line":180,"column":29}},"56":{"start":{"line":183,"column":31},"end":{"line":183,"column":null}}},"fnMap":{"0":{"name":"formatFileSize","decl":{"start":{"line":18,"column":9},"end":{"line":18,"column":24}},"loc":{"start":{"line":18,"column":37},"end":{"line":22,"column":null}}},"1":{"name":"formatTime","decl":{"start":{"line":24,"column":9},"end":{"line":24,"column":20}},"loc":{"start":{"line":24,"column":31},"end":{"line":35,"column":null}}},"2":{"name":"DocumentList","decl":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"loc":{"start":{"line":42,"column":20},"end":{"line":241,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":51,"column":4},"end":{"line":51,"column":11}},"loc":{"start":{"line":51,"column":25},"end":{"line":68,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":72,"column":12},"end":{"line":72,"column":null}},"loc":{"start":{"line":72,"column":12},"end":{"line":74,"column":5}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":76,"column":12},"end":{"line":76,"column":null}},"loc":{"start":{"line":76,"column":12},"end":{"line":84,"column":5}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":77,"column":41},"end":{"line":77,"column":42}},"loc":{"start":{"line":77,"column":48},"end":{"line":77,"column":61}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":79,"column":36},"end":{"line":79,"column":42}},"loc":{"start":{"line":79,"column":42},"end":{"line":79,"column":64}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":81,"column":11},"end":{"line":81,"column":null}},"loc":{"start":{"line":81,"column":11},"end":{"line":83,"column":null}}},"9":{"name":"handleRefresh","decl":{"start":{"line":86,"column":11},"end":{"line":86,"column":null}},"loc":{"start":{"line":86,"column":11},"end":{"line":89,"column":null}}},"10":{"name":"toggleExpand","decl":{"start":{"line":91,"column":11},"end":{"line":91,"column":24}},"loc":{"start":{"line":91,"column":34},"end":{"line":93,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":92,"column":18},"end":{"line":92,"column":19}},"loc":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":98,"column":23},"end":{"line":98,"column":24}},"loc":{"start":{"line":99,"column":10},"end":{"line":99,"column":23}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":116,"column":19},"end":{"line":116,"column":25}},"loc":{"start":{"line":116,"column":25},"end":{"line":116,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":177,"column":23},"end":{"line":177,"column":24}},"loc":{"start":{"line":177,"column":24},"end":{"line":237,"column":null}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":183,"column":25},"end":{"line":183,"column":31}},"loc":{"start":{"line":183,"column":31},"end":{"line":183,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":2},"end":{"line":19,"column":null}}]},"1":{"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":2},"end":{"line":20,"column":null}}]},"2":{"loc":{"start":{"line":40,"column":2},"end":{"line":40,"column":16}},"type":"default-arg","locations":[{"start":{"line":40,"column":15},"end":{"line":40,"column":16}}]},"3":{"loc":{"start":{"line":51,"column":11},"end":{"line":51,"column":25}},"type":"default-arg","locations":[{"start":{"line":51,"column":20},"end":{"line":51,"column":25}}]},"4":{"loc":{"start":{"line":53,"column":8},"end":{"line":53,"column":null}},"type":"if","locations":[{"start":{"line":53,"column":8},"end":{"line":53,"column":null}}]},"5":{"loc":{"start":{"line":57,"column":24},"end":{"line":57,"column":null}},"type":"cond-expr","locations":[{"start":{"line":57,"column":47},"end":{"line":57,"column":58}},{"start":{"line":57,"column":61},"end":{"line":57,"column":null}}]},"6":{"loc":{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":58,"column":8},"end":{"line":63,"column":null}},{"start":{"line":61,"column":15},"end":{"line":63,"column":null}}]},"7":{"loc":{"start":{"line":58,"column":12},"end":{"line":58,"column":96}},"type":"binary-expr","locations":[{"start":{"line":58,"column":12},"end":{"line":58,"column":56}},{"start":{"line":58,"column":56},"end":{"line":58,"column":96}}]},"8":{"loc":{"start":{"line":61,"column":15},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":15},"end":{"line":63,"column":null}}]},"9":{"loc":{"start":{"line":78,"column":4},"end":{"line":80,"column":null}},"type":"if","locations":[{"start":{"line":78,"column":4},"end":{"line":80,"column":null}}]},"10":{"loc":{"start":{"line":82,"column":6},"end":{"line":82,"column":null}},"type":"if","locations":[{"start":{"line":82,"column":6},"end":{"line":82,"column":null}}]},"11":{"loc":{"start":{"line":92,"column":29},"end":{"line":92,"column":null}},"type":"cond-expr","locations":[{"start":{"line":92,"column":43},"end":{"line":92,"column":50}},{"start":{"line":92,"column":50},"end":{"line":92,"column":null}}]},"12":{"loc":{"start":{"line":95,"column":2},"end":{"line":109,"column":null}},"type":"if","locations":[{"start":{"line":95,"column":2},"end":{"line":109,"column":null}}]},"13":{"loc":{"start":{"line":111,"column":2},"end":{"line":123,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":2},"end":{"line":123,"column":null}}]},"14":{"loc":{"start":{"line":125,"column":2},"end":{"line":145,"column":null}},"type":"if","locations":[{"start":{"line":125,"column":2},"end":{"line":145,"column":null}}]},"15":{"loc":{"start":{"line":151,"column":38},"end":{"line":151,"column":null}},"type":"cond-expr","locations":[{"start":{"line":151,"column":63},"end":{"line":151,"column":69}},{"start":{"line":151,"column":69},"end":{"line":151,"column":null}}]},"16":{"loc":{"start":{"line":160,"column":38},"end":{"line":160,"column":71}},"type":"cond-expr","locations":[{"start":{"line":160,"column":51},"end":{"line":160,"column":68}},{"start":{"line":160,"column":68},"end":{"line":160,"column":71}}]},"17":{"loc":{"start":{"line":172,"column":11},"end":{"line":172,"column":null}},"type":"cond-expr","locations":[{"start":{"line":172,"column":24},"end":{"line":172,"column":42}},{"start":{"line":172,"column":42},"end":{"line":172,"column":null}}]},"18":{"loc":{"start":{"line":190,"column":96},"end":{"line":190,"column":126}},"type":"cond-expr","locations":[{"start":{"line":190,"column":109},"end":{"line":190,"column":123}},{"start":{"line":190,"column":123},"end":{"line":190,"column":126}}]},"19":{"loc":{"start":{"line":216,"column":21},"end":{"line":216,"column":47}},"type":"binary-expr","locations":[{"start":{"line":216,"column":21},"end":{"line":216,"column":47}},{"start":{"line":216,"column":47},"end":{"line":216,"column":63}}]},"20":{"loc":{"start":{"line":226,"column":15},"end":{"line":226,"column":null}},"type":"binary-expr","locations":[{"start":{"line":226,"column":15},"end":{"line":226,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0],"9":[0],"10":[0],"11":[0,0],"12":[0],"13":[0],"14":[0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx","statementMap":{"0":{"start":{"line":49,"column":16},"end":{"line":49,"column":36}},"1":{"start":{"line":11,"column":104},"end":{"line":36,"column":null}},"2":{"start":{"line":38,"column":54},"end":{"line":47,"column":null}},"3":{"start":{"line":50,"column":17},"end":{"line":50,"column":38}},"4":{"start":{"line":51,"column":21},"end":{"line":51,"column":null}}},"fnMap":{"0":{"name":"ProcessingIndicator","decl":{"start":{"line":49,"column":16},"end":{"line":49,"column":36}},"loc":{"start":{"line":49,"column":95},"end":{"line":64,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":49,"column":53},"end":{"line":49,"column":67}},"type":"default-arg","locations":[{"start":{"line":49,"column":65},"end":{"line":49,"column":67}}]},"1":{"loc":{"start":{"line":51,"column":21},"end":{"line":51,"column":null}},"type":"cond-expr","locations":[{"start":{"line":51,"column":70},"end":{"line":51,"column":89}},{"start":{"line":51,"column":92},"end":{"line":51,"column":null}}]},"2":{"loc":{"start":{"line":51,"column":21},"end":{"line":51,"column":70}},"type":"binary-expr","locations":[{"start":{"line":51,"column":21},"end":{"line":51,"column":30}},{"start":{"line":51,"column":30},"end":{"line":51,"column":50}},{"start":{"line":51,"column":50},"end":{"line":51,"column":70}}]},"3":{"loc":{"start":{"line":59,"column":7},"end":{"line":59,"column":21}},"type":"binary-expr","locations":[{"start":{"line":59,"column":7},"end":{"line":59,"column":21}},{"start":{"line":59,"column":21},"end":{"line":59,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{"0":[0],"1":[0,0],"2":[0,0,0],"3":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx","statementMap":{"0":{"start":{"line":22,"column":16},"end":{"line":22,"column":40}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":38},"end":{"line":4,"column":null}},"3":{"start":{"line":27,"column":30},"end":{"line":27,"column":null}},"4":{"start":{"line":28,"column":32},"end":{"line":28,"column":null}},"5":{"start":{"line":29,"column":28},"end":{"line":29,"column":null}},"6":{"start":{"line":31,"column":15},"end":{"line":41,"column":null}},"7":{"start":{"line":32,"column":4},"end":{"line":40,"column":null}},"8":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"9":{"start":{"line":34,"column":19},"end":{"line":34,"column":null}},"10":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"11":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"12":{"start":{"line":39,"column":6},"end":{"line":39,"column":null}},"13":{"start":{"line":43,"column":2},"end":{"line":45,"column":null}},"14":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"15":{"start":{"line":47,"column":2},"end":{"line":55,"column":null}},"16":{"start":{"line":57,"column":2},"end":{"line":66,"column":null}},"17":{"start":{"line":68,"column":2},"end":{"line":68,"column":null}},"18":{"start":{"line":68,"column":15},"end":{"line":68,"column":null}},"19":{"start":{"line":97,"column":25},"end":{"line":97,"column":null}}},"fnMap":{"0":{"name":"DetailRow","decl":{"start":{"line":13,"column":9},"end":{"line":13,"column":19}},"loc":{"start":{"line":13,"column":78},"end":{"line":20,"column":null}}},"1":{"name":"DocumentProcessingPanel","decl":{"start":{"line":22,"column":16},"end":{"line":22,"column":40}},"loc":{"start":{"line":26,"column":31},"end":{"line":108,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":31,"column":27},"end":{"line":31,"column":null}},"loc":{"start":{"line":31,"column":27},"end":{"line":41,"column":5}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"loc":{"start":{"line":43,"column":12},"end":{"line":45,"column":5}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":97,"column":19},"end":{"line":97,"column":25}},"loc":{"start":{"line":97,"column":25},"end":{"line":97,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":56},"end":{"line":17,"column":65}},"type":"binary-expr","locations":[{"start":{"line":17,"column":56},"end":{"line":17,"column":65}}]},"1":{"loc":{"start":{"line":37,"column":15},"end":{"line":37,"column":null}},"type":"cond-expr","locations":[{"start":{"line":37,"column":38},"end":{"line":37,"column":49}},{"start":{"line":37,"column":52},"end":{"line":37,"column":null}}]},"2":{"loc":{"start":{"line":47,"column":2},"end":{"line":55,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":2},"end":{"line":55,"column":null}}]},"3":{"loc":{"start":{"line":57,"column":2},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":57,"column":2},"end":{"line":66,"column":null}}]},"4":{"loc":{"start":{"line":68,"column":2},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":68,"column":2},"end":{"line":68,"column":null}}]},"5":{"loc":{"start":{"line":79,"column":9},"end":{"line":79,"column":28}},"type":"binary-expr","locations":[{"start":{"line":79,"column":9},"end":{"line":79,"column":28}}]},"6":{"loc":{"start":{"line":85,"column":9},"end":{"line":85,"column":34}},"type":"binary-expr","locations":[{"start":{"line":85,"column":9},"end":{"line":85,"column":34}}]},"7":{"loc":{"start":{"line":95,"column":7},"end":{"line":95,"column":24}},"type":"binary-expr","locations":[{"start":{"line":95,"column":7},"end":{"line":95,"column":24}},{"start":{"line":95,"column":24},"end":{"line":95,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0],"7":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx","statementMap":{"0":{"start":{"line":20,"column":16},"end":{"line":20,"column":28}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":null}},"2":{"start":{"line":5,"column":31},"end":{"line":5,"column":null}},"3":{"start":{"line":6,"column":36},"end":{"line":6,"column":null}},"4":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"5":{"start":{"line":15,"column":20},"end":{"line":15,"column":null}},"6":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"7":{"start":{"line":16,"column":27},"end":{"line":16,"column":null}},"8":{"start":{"line":17,"column":2},"end":{"line":17,"column":null}},"9":{"start":{"line":21,"column":34},"end":{"line":21,"column":null}},"10":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"11":{"start":{"line":24,"column":47},"end":{"line":24,"column":null}},"12":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"13":{"start":{"line":26,"column":4},"end":{"line":31,"column":null}},"14":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"15":{"start":{"line":28,"column":6},"end":{"line":28,"column":null}},"16":{"start":{"line":30,"column":6},"end":{"line":30,"column":null}}},"fnMap":{"0":{"name":"formatFileSize","decl":{"start":{"line":14,"column":9},"end":{"line":14,"column":24}},"loc":{"start":{"line":14,"column":37},"end":{"line":18,"column":null}}},"1":{"name":"DocumentRow","decl":{"start":{"line":20,"column":16},"end":{"line":20,"column":28}},"loc":{"start":{"line":20,"column":87},"end":{"line":58,"column":null}}},"2":{"name":"handleDelete","decl":{"start":{"line":23,"column":17},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":17},"end":{"line":32,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":2},"end":{"line":15,"column":null}}]},"1":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":2},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":4},"end":{"line":24,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0],"1":[0],"2":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx","statementMap":{"0":{"start":{"line":18,"column":16},"end":{"line":18,"column":31}},"1":{"start":{"line":3,"column":46},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":58},"end":{"line":4,"column":null}},"3":{"start":{"line":19,"column":26},"end":{"line":19,"column":null}},"4":{"start":{"line":20,"column":38},"end":{"line":20,"column":null}},"5":{"start":{"line":21,"column":19},"end":{"line":21,"column":null}},"6":{"start":{"line":23,"column":22},"end":{"line":69,"column":null}},"7":{"start":{"line":25,"column":24},"end":{"line":25,"column":null}},"8":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"9":{"start":{"line":26,"column":34},"end":{"line":26,"column":null}},"10":{"start":{"line":28,"column":35},"end":{"line":31,"column":null}},"11":{"start":{"line":28,"column":60},"end":{"line":31,"column":null}},"12":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"13":{"start":{"line":33,"column":24},"end":{"line":33,"column":null}},"14":{"start":{"line":35,"column":6},"end":{"line":67,"column":null}},"15":{"start":{"line":35,"column":19},"end":{"line":35,"column":22}},"16":{"start":{"line":36,"column":21},"end":{"line":36,"column":33}},"17":{"start":{"line":37,"column":25},"end":{"line":37,"column":null}},"18":{"start":{"line":39,"column":8},"end":{"line":66,"column":null}},"19":{"start":{"line":40,"column":22},"end":{"line":40,"column":null}},"20":{"start":{"line":42,"column":10},"end":{"line":46,"column":null}},"21":{"start":{"line":43,"column":28},"end":{"line":43,"column":null}},"22":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"23":{"start":{"line":44,"column":35},"end":{"line":44,"column":null}},"24":{"start":{"line":45,"column":12},"end":{"line":45,"column":null}},"25":{"start":{"line":48,"column":10},"end":{"line":48,"column":null}},"26":{"start":{"line":50,"column":10},"end":{"line":54,"column":null}},"27":{"start":{"line":51,"column":28},"end":{"line":51,"column":null}},"28":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"29":{"start":{"line":52,"column":35},"end":{"line":52,"column":null}},"30":{"start":{"line":53,"column":12},"end":{"line":53,"column":null}},"31":{"start":{"line":56,"column":10},"end":{"line":56,"column":null}},"32":{"start":{"line":58,"column":26},"end":{"line":58,"column":null}},"33":{"start":{"line":59,"column":10},"end":{"line":64,"column":null}},"34":{"start":{"line":60,"column":28},"end":{"line":60,"column":null}},"35":{"start":{"line":61,"column":12},"end":{"line":62,"column":null}},"36":{"start":{"line":62,"column":14},"end":{"line":62,"column":null}},"37":{"start":{"line":63,"column":12},"end":{"line":63,"column":null}},"38":{"start":{"line":65,"column":10},"end":{"line":65,"column":null}},"39":{"start":{"line":72,"column":17},"end":{"line":80,"column":null}},"40":{"start":{"line":74,"column":6},"end":{"line":74,"column":null}},"41":{"start":{"line":75,"column":6},"end":{"line":75,"column":null}},"42":{"start":{"line":76,"column":6},"end":{"line":78,"column":null}},"43":{"start":{"line":77,"column":8},"end":{"line":77,"column":null}},"44":{"start":{"line":83,"column":21},"end":{"line":83,"column":null}},"45":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}},"46":{"start":{"line":84,"column":20},"end":{"line":84,"column":null}},"47":{"start":{"line":84,"column":39},"end":{"line":84,"column":null}},"48":{"start":{"line":90,"column":10},"end":{"line":90,"column":null}},"49":{"start":{"line":91,"column":10},"end":{"line":91,"column":null}},"50":{"start":{"line":93,"column":27},"end":{"line":93,"column":null}},"51":{"start":{"line":95,"column":23},"end":{"line":95,"column":null}},"52":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"53":{"start":{"line":109,"column":32},"end":{"line":109,"column":null}},"54":{"start":{"line":110,"column":12},"end":{"line":110,"column":null}},"55":{"start":{"line":125,"column":12},"end":{"line":125,"column":25}},"56":{"start":{"line":140,"column":12},"end":{"line":140,"column":25}}},"fnMap":{"0":{"name":"DocumentUpload","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":31}},"loc":{"start":{"line":18,"column":95},"end":{"line":152,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":11}},"loc":{"start":{"line":24,"column":11},"end":{"line":68,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":28,"column":49},"end":{"line":28,"column":50}},"loc":{"start":{"line":28,"column":60},"end":{"line":31,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":33,"column":14},"end":{"line":33,"column":15}},"loc":{"start":{"line":33,"column":24},"end":{"line":33,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":42,"column":18},"end":{"line":42,"column":19}},"loc":{"start":{"line":42,"column":19},"end":{"line":46,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":50,"column":18},"end":{"line":50,"column":19}},"loc":{"start":{"line":50,"column":19},"end":{"line":54,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":59,"column":18},"end":{"line":59,"column":19}},"loc":{"start":{"line":59,"column":19},"end":{"line":64,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":73,"column":4},"end":{"line":73,"column":5}},"loc":{"start":{"line":73,"column":5},"end":{"line":79,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":83,"column":33},"end":{"line":83,"column":34}},"loc":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":84,"column":32},"end":{"line":84,"column":33}},"loc":{"start":{"line":84,"column":39},"end":{"line":84,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":89,"column":20},"end":{"line":89,"column":21}},"loc":{"start":{"line":89,"column":21},"end":{"line":92,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":93,"column":21},"end":{"line":93,"column":27}},"loc":{"start":{"line":93,"column":27},"end":{"line":93,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":95,"column":17},"end":{"line":95,"column":23}},"loc":{"start":{"line":95,"column":23},"end":{"line":95,"column":null}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":108,"column":20},"end":{"line":108,"column":21}},"loc":{"start":{"line":108,"column":21},"end":{"line":111,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":124,"column":26},"end":{"line":124,"column":27}},"loc":{"start":{"line":125,"column":12},"end":{"line":125,"column":25}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":139,"column":25},"end":{"line":139,"column":26}},"loc":{"start":{"line":140,"column":12},"end":{"line":140,"column":25}}}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":26,"column":6},"end":{"line":26,"column":null}}]},"1":{"loc":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":44,"column":12},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":52,"column":12},"end":{"line":52,"column":null}},"type":"if","locations":[{"start":{"line":52,"column":12},"end":{"line":52,"column":null}}]},"3":{"loc":{"start":{"line":58,"column":26},"end":{"line":58,"column":null}},"type":"cond-expr","locations":[{"start":{"line":58,"column":49},"end":{"line":58,"column":60}},{"start":{"line":58,"column":63},"end":{"line":58,"column":null}}]},"4":{"loc":{"start":{"line":61,"column":12},"end":{"line":62,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":12},"end":{"line":62,"column":null}}]},"5":{"loc":{"start":{"line":76,"column":6},"end":{"line":78,"column":null}},"type":"if","locations":[{"start":{"line":76,"column":6},"end":{"line":78,"column":null}}]},"6":{"loc":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}},"type":"binary-expr","locations":[{"start":{"line":83,"column":40},"end":{"line":83,"column":70}},{"start":{"line":83,"column":70},"end":{"line":83,"column":null}}]},"7":{"loc":{"start":{"line":97,"column":10},"end":{"line":99,"column":null}},"type":"cond-expr","locations":[{"start":{"line":98,"column":14},"end":{"line":98,"column":null}},{"start":{"line":99,"column":14},"end":{"line":99,"column":null}}]},"8":{"loc":{"start":{"line":109,"column":12},"end":{"line":109,"column":null}},"type":"if","locations":[{"start":{"line":109,"column":12},"end":{"line":109,"column":null}}]},"9":{"loc":{"start":{"line":122,"column":7},"end":{"line":122,"column":null}},"type":"binary-expr","locations":[{"start":{"line":122,"column":7},"end":{"line":122,"column":null}}]},"10":{"loc":{"start":{"line":137,"column":7},"end":{"line":137,"column":null}},"type":"binary-expr","locations":[{"start":{"line":137,"column":7},"end":{"line":137,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0],"5":[0],"6":[0,0],"7":[0,0],"8":[0],"9":[0],"10":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx","statementMap":{"0":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"1":{"start":{"line":22,"column":0},"end":{"line":22,"column":15}},"2":{"start":{"line":7,"column":32},"end":{"line":7,"column":null}},"3":{"start":{"line":22,"column":15},"end":{"line":22,"column":28}}},"fnMap":{"0":{"name":"AuthProvider","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"loc":{"start":{"line":18,"column":60},"end":{"line":20,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx","statementMap":{"0":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}}},"fnMap":{"0":{"name":"OutputPane","decl":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}},"loc":{"start":{"line":3,"column":16},"end":{"line":38,"column":null}}}},"branchMap":{},"s":{"0":0},"f":{"0":0},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":7,"column":27}}},"fnMap":{"0":{"name":"SourcePane","decl":{"start":{"line":7,"column":16},"end":{"line":7,"column":27}},"loc":{"start":{"line":7,"column":59},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":9},"end":{"line":14,"column":null}},"type":"binary-expr","locations":[{"start":{"line":14,"column":9},"end":{"line":14,"column":null}}]}},"s":{"0":0},"f":{"0":0},"b":{"0":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx","statementMap":{"0":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"1":{"start":{"line":3,"column":62},"end":{"line":3,"column":null}},"2":{"start":{"line":20,"column":40},"end":{"line":20,"column":null}},"3":{"start":{"line":21,"column":23},"end":{"line":21,"column":null}},"4":{"start":{"line":22,"column":19},"end":{"line":22,"column":null}},"5":{"start":{"line":24,"column":22},"end":{"line":49,"column":null}},"6":{"start":{"line":26,"column":6},"end":{"line":26,"column":null}},"7":{"start":{"line":27,"column":6},"end":{"line":27,"column":null}},"8":{"start":{"line":29,"column":26},"end":{"line":34,"column":null}},"9":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"10":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}},"11":{"start":{"line":31,"column":21},"end":{"line":31,"column":null}},"12":{"start":{"line":32,"column":24},"end":{"line":32,"column":null}},"13":{"start":{"line":33,"column":8},"end":{"line":33,"column":null}},"14":{"start":{"line":36,"column":24},"end":{"line":42,"column":null}},"15":{"start":{"line":37,"column":8},"end":{"line":37,"column":null}},"16":{"start":{"line":38,"column":8},"end":{"line":38,"column":null}},"17":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"18":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"19":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"20":{"start":{"line":44,"column":6},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":6},"end":{"line":45,"column":null}},"22":{"start":{"line":46,"column":6},"end":{"line":46,"column":null}},"23":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"SplitPane","decl":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"loc":{"start":{"line":19,"column":17},"end":{"line":71,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":4},"end":{"line":25,"column":5}},"loc":{"start":{"line":25,"column":5},"end":{"line":48,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":29,"column":26},"end":{"line":29,"column":27}},"loc":{"start":{"line":29,"column":27},"end":{"line":34,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":36,"column":24},"end":{"line":36,"column":null}},"loc":{"start":{"line":36,"column":24},"end":{"line":42,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":25}},"type":"default-arg","locations":[{"start":{"line":16,"column":23},"end":{"line":16,"column":25}}]},"1":{"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":21}},"type":"default-arg","locations":[{"start":{"line":17,"column":19},"end":{"line":17,"column":21}}]},"2":{"loc":{"start":{"line":18,"column":2},"end":{"line":18,"column":21}},"type":"default-arg","locations":[{"start":{"line":18,"column":19},"end":{"line":18,"column":21}}]},"3":{"loc":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":30,"column":8},"end":{"line":30,"column":null}}]},"4":{"loc":{"start":{"line":30,"column":12},"end":{"line":30,"column":54}},"type":"binary-expr","locations":[{"start":{"line":30,"column":12},"end":{"line":30,"column":29}},{"start":{"line":30,"column":33},"end":{"line":30,"column":54}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api.ts","statementMap":{"0":{"start":{"line":7,"column":13},"end":{"line":7,"column":21}},"1":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"2":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"4":{"start":{"line":9,"column":11},"end":{"line":9,"column":25}},"5":{"start":{"line":11,"column":11},"end":{"line":11,"column":44}},"6":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"7":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"8":{"start":{"line":25,"column":22},"end":{"line":25,"column":null}},"9":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}},"10":{"start":{"line":26,"column":4},"end":{"line":29,"column":null}},"11":{"start":{"line":32,"column":2},"end":{"line":32,"column":null}},"12":{"start":{"line":39,"column":65},"end":{"line":39,"column":null}},"13":{"start":{"line":41,"column":42},"end":{"line":43,"column":null}},"14":{"start":{"line":45,"column":2},"end":{"line":47,"column":null}},"15":{"start":{"line":46,"column":4},"end":{"line":46,"column":null}},"16":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"17":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"18":{"start":{"line":53,"column":19},"end":{"line":57,"column":null}},"19":{"start":{"line":59,"column":2},"end":{"line":59,"column":null}}},"fnMap":{"0":{"name":"(anonymous_3)","decl":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"loc":{"start":{"line":12,"column":4},"end":{"line":15,"column":null}}},"1":{"name":"handleResponse","decl":{"start":{"line":23,"column":15},"end":{"line":23,"column":33}},"loc":{"start":{"line":23,"column":51},"end":{"line":33,"column":null}}},"2":{"name":"(anonymous_5)","decl":{"start":{"line":25,"column":50},"end":{"line":25,"column":57}},"loc":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}}},"3":{"name":"apiFetch","decl":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"loc":{"start":{"line":37,"column":28},"end":{"line":60,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"type":"binary-expr","locations":[{"start":{"line":2,"column":2},"end":{"line":2,"column":38}},{"start":{"line":3,"column":3},"end":{"line":5,"column":32}}]},"1":{"loc":{"start":{"line":3,"column":3},"end":{"line":5,"column":32}},"type":"cond-expr","locations":[{"start":{"line":4,"column":6},"end":{"line":4,"column":46}},{"start":{"line":5,"column":6},"end":{"line":5,"column":32}}]},"2":{"loc":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":31,"column":null}}]},"3":{"loc":{"start":{"line":28,"column":6},"end":{"line":28,"column":84}},"type":"binary-expr","locations":[{"start":{"line":28,"column":6},"end":{"line":28,"column":22}},{"start":{"line":28,"column":26},"end":{"line":28,"column":43}},{"start":{"line":28,"column":47},"end":{"line":28,"column":84}}]},"4":{"loc":{"start":{"line":37,"column":2},"end":{"line":37,"column":28}},"type":"default-arg","locations":[{"start":{"line":37,"column":26},"end":{"line":37,"column":28}}]},"5":{"loc":{"start":{"line":45,"column":2},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":2},"end":{"line":47,"column":null}}]},"6":{"loc":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":51,"column":null}}]},"7":{"loc":{"start":{"line":49,"column":6},"end":{"line":49,"column":57}},"type":"binary-expr","locations":[{"start":{"line":49,"column":6},"end":{"line":49,"column":28}},{"start":{"line":49,"column":28},"end":{"line":49,"column":57}}]},"8":{"loc":{"start":{"line":56,"column":10},"end":{"line":56,"column":null}},"type":"cond-expr","locations":[{"start":{"line":56,"column":37},"end":{"line":56,"column":44}},{"start":{"line":56,"column":44},"end":{"line":56,"column":null}}]},"9":{"loc":{"start":{"line":56,"column":44},"end":{"line":56,"column":null}},"type":"cond-expr","locations":[{"start":{"line":56,"column":65},"end":{"line":56,"column":88}},{"start":{"line":56,"column":88},"end":{"line":56,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0,0,0],"4":[0],"5":[0],"6":[0],"7":[0,0],"8":[0,0],"9":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts","statementMap":{"0":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"1":{"start":{"line":27,"column":16},"end":{"line":27,"column":33}},"2":{"start":{"line":72,"column":16},"end":{"line":72,"column":37}},"3":{"start":{"line":13,"column":2},"end":{"line":20,"column":null}},"4":{"start":{"line":14,"column":16},"end":{"line":14,"column":34}},"5":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"6":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"7":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"8":{"start":{"line":16,"column":81},"end":{"line":16,"column":null}},"9":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"10":{"start":{"line":17,"column":79},"end":{"line":17,"column":null}},"11":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"12":{"start":{"line":18,"column":25},"end":{"line":18,"column":null}},"13":{"start":{"line":19,"column":4},"end":{"line":19,"column":null}},"14":{"start":{"line":19,"column":13},"end":{"line":19,"column":null}},"15":{"start":{"line":21,"column":14},"end":{"line":21,"column":null}},"16":{"start":{"line":22,"column":2},"end":{"line":22,"column":null}},"17":{"start":{"line":25,"column":55},"end":{"line":25,"column":null}},"18":{"start":{"line":28,"column":2},"end":{"line":40,"column":null}},"19":{"start":{"line":44,"column":59},"end":{"line":44,"column":null}},"20":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"21":{"start":{"line":46,"column":4},"end":{"line":50,"column":null}},"22":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}},"23":{"start":{"line":49,"column":6},"end":{"line":49,"column":null}},"24":{"start":{"line":53,"column":2},"end":{"line":69,"column":null}},"25":{"start":{"line":73,"column":2},"end":{"line":80,"column":null}}},"fnMap":{"0":{"name":"mimeToLabel","decl":{"start":{"line":12,"column":9},"end":{"line":12,"column":21}},"loc":{"start":{"line":12,"column":58},"end":{"line":23,"column":null}}},"1":{"name":"toDocumentListRow","decl":{"start":{"line":27,"column":16},"end":{"line":27,"column":33}},"loc":{"start":{"line":27,"column":59},"end":{"line":41,"column":null}}},"2":{"name":"toDocumentDetailView","decl":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"loc":{"start":{"line":43,"column":60},"end":{"line":70,"column":null}}},"3":{"name":"toDocumentPreviewView","decl":{"start":{"line":72,"column":16},"end":{"line":72,"column":37}},"loc":{"start":{"line":72,"column":62},"end":{"line":81,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":2},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":13,"column":2},"end":{"line":20,"column":null}}]},"1":{"loc":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":4},"end":{"line":15,"column":null}}]},"2":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"3":{"loc":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":17,"column":null}}]},"4":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":null}}]},"5":{"loc":{"start":{"line":19,"column":4},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":4},"end":{"line":19,"column":null}}]},"6":{"loc":{"start":{"line":22,"column":9},"end":{"line":22,"column":null}},"type":"binary-expr","locations":[{"start":{"line":22,"column":9},"end":{"line":22,"column":16}},{"start":{"line":22,"column":16},"end":{"line":22,"column":null}}]},"7":{"loc":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":2},"end":{"line":51,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0,0],"7":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts","statementMap":{"0":{"start":{"line":28,"column":22},"end":{"line":28,"column":34}},"1":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"2":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"3":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"4":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"5":{"start":{"line":9,"column":2},"end":{"line":11,"column":null}},"6":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"7":{"start":{"line":25,"column":2},"end":{"line":25,"column":null}},"8":{"start":{"line":32,"column":2},"end":{"line":36,"column":null}}},"fnMap":{"0":{"name":"fetchCourses","decl":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"loc":{"start":{"line":7,"column":22},"end":{"line":12,"column":null}}},"1":{"name":"fetchCourse","decl":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"loc":{"start":{"line":16,"column":22},"end":{"line":19,"column":null}}},"2":{"name":"fetchCourseLessons","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"loc":{"start":{"line":23,"column":22},"end":{"line":26,"column":null}}},"3":{"name":"createCourse","decl":{"start":{"line":28,"column":22},"end":{"line":28,"column":34}},"loc":{"start":{"line":30,"column":22},"end":{"line":37,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":5,"column":2},"end":{"line":5,"column":10}},"type":"default-arg","locations":[{"start":{"line":5,"column":9},"end":{"line":5,"column":10}}]},"1":{"loc":{"start":{"line":6,"column":2},"end":{"line":6,"column":13}},"type":"default-arg","locations":[{"start":{"line":6,"column":10},"end":{"line":6,"column":13}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts","statementMap":{"0":{"start":{"line":65,"column":22},"end":{"line":65,"column":36}},"1":{"start":{"line":33,"column":22},"end":{"line":33,"column":41}},"2":{"start":{"line":42,"column":22},"end":{"line":42,"column":42}},"3":{"start":{"line":24,"column":22},"end":{"line":24,"column":41}},"4":{"start":{"line":15,"column":22},"end":{"line":15,"column":40}},"5":{"start":{"line":85,"column":22},"end":{"line":85,"column":43}},"6":{"start":{"line":77,"column":22},"end":{"line":77,"column":41}},"7":{"start":{"line":93,"column":22},"end":{"line":93,"column":44}},"8":{"start":{"line":103,"column":22},"end":{"line":103,"column":47}},"9":{"start":{"line":51,"column":22},"end":{"line":51,"column":36}},"10":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"11":{"start":{"line":11,"column":79},"end":{"line":11,"column":null}},"12":{"start":{"line":19,"column":2},"end":{"line":21,"column":null}},"13":{"start":{"line":28,"column":2},"end":{"line":30,"column":null}},"14":{"start":{"line":37,"column":2},"end":{"line":39,"column":null}},"15":{"start":{"line":46,"column":2},"end":{"line":48,"column":null}},"16":{"start":{"line":56,"column":19},"end":{"line":56,"column":null}},"17":{"start":{"line":57,"column":2},"end":{"line":57,"column":null}},"18":{"start":{"line":58,"column":2},"end":{"line":62,"column":null}},"19":{"start":{"line":69,"column":2},"end":{"line":72,"column":null}},"20":{"start":{"line":81,"column":16},"end":{"line":81,"column":null}},"21":{"start":{"line":82,"column":2},"end":{"line":82,"column":null}},"22":{"start":{"line":89,"column":15},"end":{"line":89,"column":null}},"23":{"start":{"line":90,"column":2},"end":{"line":90,"column":null}},"24":{"start":{"line":97,"column":15},"end":{"line":97,"column":null}},"25":{"start":{"line":98,"column":2},"end":{"line":98,"column":null}},"26":{"start":{"line":109,"column":2},"end":{"line":123,"column":null}},"27":{"start":{"line":109,"column":15},"end":{"line":109,"column":18}},"28":{"start":{"line":110,"column":4},"end":{"line":121,"column":null}},"29":{"start":{"line":111,"column":21},"end":{"line":111,"column":null}},"30":{"start":{"line":112,"column":6},"end":{"line":114,"column":null}},"31":{"start":{"line":113,"column":8},"end":{"line":113,"column":null}},"32":{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},"33":{"start":{"line":119,"column":8},"end":{"line":119,"column":null}},"34":{"start":{"line":122,"column":4},"end":{"line":122,"column":null}},"35":{"start":{"line":122,"column":35},"end":{"line":122,"column":null}},"36":{"start":{"line":124,"column":2},"end":{"line":124,"column":null}}},"fnMap":{"0":{"name":"fetchDocumentsList","decl":{"start":{"line":15,"column":22},"end":{"line":15,"column":40}},"loc":{"start":{"line":17,"column":22},"end":{"line":22,"column":null}}},"1":{"name":"fetchDocumentStatus","decl":{"start":{"line":24,"column":22},"end":{"line":24,"column":41}},"loc":{"start":{"line":26,"column":22},"end":{"line":31,"column":null}}},"2":{"name":"fetchDocumentDetail","decl":{"start":{"line":33,"column":22},"end":{"line":33,"column":41}},"loc":{"start":{"line":35,"column":22},"end":{"line":40,"column":null}}},"3":{"name":"fetchDocumentPreview","decl":{"start":{"line":42,"column":22},"end":{"line":42,"column":42}},"loc":{"start":{"line":44,"column":22},"end":{"line":49,"column":null}}},"4":{"name":"uploadDocument","decl":{"start":{"line":51,"column":22},"end":{"line":51,"column":36}},"loc":{"start":{"line":54,"column":22},"end":{"line":63,"column":null}}},"5":{"name":"deleteDocument","decl":{"start":{"line":65,"column":22},"end":{"line":65,"column":36}},"loc":{"start":{"line":67,"column":22},"end":{"line":73,"column":null}}},"6":{"name":"getDocumentListRows","decl":{"start":{"line":77,"column":22},"end":{"line":77,"column":41}},"loc":{"start":{"line":79,"column":22},"end":{"line":83,"column":null}}},"7":{"name":"getDocumentDetailView","decl":{"start":{"line":85,"column":22},"end":{"line":85,"column":43}},"loc":{"start":{"line":87,"column":22},"end":{"line":91,"column":null}}},"8":{"name":"getDocumentPreviewView","decl":{"start":{"line":93,"column":22},"end":{"line":93,"column":44}},"loc":{"start":{"line":95,"column":22},"end":{"line":99,"column":null}}},"9":{"name":"pollDocumentUntilTerminal","decl":{"start":{"line":103,"column":22},"end":{"line":103,"column":47}},"loc":{"start":{"line":107,"column":18},"end":{"line":125,"column":null}}},"10":{"name":"(anonymous_21)","decl":{"start":{"line":122,"column":22},"end":{"line":122,"column":23}},"loc":{"start":{"line":122,"column":35},"end":{"line":122,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":106,"column":2},"end":{"line":106,"column":19}},"type":"default-arg","locations":[{"start":{"line":106,"column":15},"end":{"line":106,"column":19}}]},"1":{"loc":{"start":{"line":107,"column":2},"end":{"line":107,"column":18}},"type":"default-arg","locations":[{"start":{"line":107,"column":16},"end":{"line":107,"column":18}}]},"2":{"loc":{"start":{"line":112,"column":6},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":112,"column":6},"end":{"line":114,"column":null}}]},"3":{"loc":{"start":{"line":112,"column":10},"end":{"line":112,"column":66}},"type":"binary-expr","locations":[{"start":{"line":112,"column":10},"end":{"line":112,"column":39}},{"start":{"line":112,"column":39},"end":{"line":112,"column":66}}]},"4":{"loc":{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},"type":"if","locations":[{"start":{"line":116,"column":6},"end":{"line":120,"column":null}},{"start":{"line":118,"column":13},"end":{"line":120,"column":null}}]},"5":{"loc":{"start":{"line":116,"column":10},"end":{"line":116,"column":56}},"type":"binary-expr","locations":[{"start":{"line":116,"column":10},"end":{"line":116,"column":33}},{"start":{"line":116,"column":37},"end":{"line":116,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/index.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/api/index.ts","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":14},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":14},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":14},"end":{"line":4,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{},"b":{}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts","statementMap":{"0":{"start":{"line":6,"column":13},"end":{"line":6,"column":40}},"1":{"start":{"line":2,"column":32},"end":{"line":2,"column":null}},"2":{"start":{"line":4,"column":16},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":40},"end":{"line":83,"column":null}},"4":{"start":{"line":15,"column":8},"end":{"line":17,"column":null}},"5":{"start":{"line":16,"column":10},"end":{"line":16,"column":null}},"6":{"start":{"line":19,"column":8},"end":{"line":50,"column":null}},"7":{"start":{"line":20,"column":22},"end":{"line":27,"column":null}},"8":{"start":{"line":29,"column":10},"end":{"line":32,"column":null}},"9":{"start":{"line":30,"column":26},"end":{"line":30,"column":null}},"10":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}},"11":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"12":{"start":{"line":34,"column":23},"end":{"line":34,"column":null}},"13":{"start":{"line":36,"column":10},"end":{"line":44,"column":null}},"14":{"start":{"line":46,"column":10},"end":{"line":48,"column":null}},"15":{"start":{"line":47,"column":12},"end":{"line":47,"column":null}},"16":{"start":{"line":49,"column":10},"end":{"line":49,"column":null}},"17":{"start":{"line":56,"column":6},"end":{"line":61,"column":null}},"18":{"start":{"line":57,"column":8},"end":{"line":57,"column":null}},"19":{"start":{"line":58,"column":8},"end":{"line":58,"column":null}},"20":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"21":{"start":{"line":60,"column":8},"end":{"line":60,"column":null}},"22":{"start":{"line":62,"column":6},"end":{"line":62,"column":null}},"23":{"start":{"line":65,"column":6},"end":{"line":70,"column":null}},"24":{"start":{"line":66,"column":9},"end":{"line":66,"column":null}},"25":{"start":{"line":67,"column":9},"end":{"line":67,"column":null}},"26":{"start":{"line":68,"column":9},"end":{"line":68,"column":null}},"27":{"start":{"line":69,"column":9},"end":{"line":69,"column":null}},"28":{"start":{"line":71,"column":6},"end":{"line":71,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":6},"end":{"line":14,"column":12}},"loc":{"start":{"line":14,"column":33},"end":{"line":51,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":30,"column":49},"end":{"line":30,"column":56}},"loc":{"start":{"line":30,"column":56},"end":{"line":30,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":55,"column":4},"end":{"line":55,"column":10}},"loc":{"start":{"line":55,"column":29},"end":{"line":63,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":64,"column":4},"end":{"line":64,"column":10}},"loc":{"start":{"line":64,"column":36},"end":{"line":72,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":4,"column":16},"end":{"line":4,"column":null}},"type":"binary-expr","locations":[{"start":{"line":4,"column":16},"end":{"line":4,"column":47}},{"start":{"line":4,"column":51},"end":{"line":4,"column":null}}]},"1":{"loc":{"start":{"line":15,"column":8},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":8},"end":{"line":17,"column":null}}]},"2":{"loc":{"start":{"line":15,"column":12},"end":{"line":15,"column":59}},"type":"binary-expr","locations":[{"start":{"line":15,"column":12},"end":{"line":15,"column":35}},{"start":{"line":15,"column":35},"end":{"line":15,"column":59}}]},"3":{"loc":{"start":{"line":29,"column":10},"end":{"line":32,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":10},"end":{"line":32,"column":null}}]},"4":{"loc":{"start":{"line":31,"column":28},"end":{"line":31,"column":null}},"type":"binary-expr","locations":[{"start":{"line":31,"column":28},"end":{"line":31,"column":40}},{"start":{"line":31,"column":44},"end":{"line":31,"column":null}}]},"5":{"loc":{"start":{"line":37,"column":16},"end":{"line":37,"column":40}},"type":"binary-expr","locations":[{"start":{"line":37,"column":16},"end":{"line":37,"column":33}},{"start":{"line":37,"column":33},"end":{"line":37,"column":40}}]},"6":{"loc":{"start":{"line":38,"column":19},"end":{"line":38,"column":56}},"type":"binary-expr","locations":[{"start":{"line":38,"column":19},"end":{"line":38,"column":39}},{"start":{"line":38,"column":39},"end":{"line":38,"column":56}}]},"7":{"loc":{"start":{"line":39,"column":18},"end":{"line":39,"column":null}},"type":"binary-expr","locations":[{"start":{"line":39,"column":18},"end":{"line":39,"column":45}},{"start":{"line":39,"column":45},"end":{"line":39,"column":null}}]},"8":{"loc":{"start":{"line":40,"column":25},"end":{"line":40,"column":null}},"type":"binary-expr","locations":[{"start":{"line":40,"column":25},"end":{"line":40,"column":42}},{"start":{"line":40,"column":46},"end":{"line":40,"column":null}}]},"9":{"loc":{"start":{"line":41,"column":26},"end":{"line":41,"column":null}},"type":"binary-expr","locations":[{"start":{"line":41,"column":26},"end":{"line":41,"column":44}},{"start":{"line":41,"column":48},"end":{"line":41,"column":null}}]},"10":{"loc":{"start":{"line":42,"column":18},"end":{"line":42,"column":null}},"type":"binary-expr","locations":[{"start":{"line":42,"column":18},"end":{"line":42,"column":37}},{"start":{"line":42,"column":37},"end":{"line":42,"column":null}}]},"11":{"loc":{"start":{"line":43,"column":25},"end":{"line":43,"column":null}},"type":"binary-expr","locations":[{"start":{"line":43,"column":25},"end":{"line":43,"column":52}},{"start":{"line":43,"column":52},"end":{"line":43,"column":null}}]},"12":{"loc":{"start":{"line":46,"column":10},"end":{"line":48,"column":null}},"type":"if","locations":[{"start":{"line":46,"column":10},"end":{"line":48,"column":null}}]},"13":{"loc":{"start":{"line":56,"column":6},"end":{"line":61,"column":null}},"type":"if","locations":[{"start":{"line":56,"column":6},"end":{"line":61,"column":null}}]},"14":{"loc":{"start":{"line":65,"column":6},"end":{"line":70,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":6},"end":{"line":70,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0],"13":[0],"14":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts","statementMap":{"0":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"1":{"start":{"line":32,"column":13},"end":{"line":32,"column":19}},"2":{"start":{"line":1,"column":29},"end":{"line":1,"column":null}},"3":{"start":{"line":2,"column":25},"end":{"line":2,"column":null}},"4":{"start":{"line":5,"column":21},"end":{"line":5,"column":null}},"5":{"start":{"line":8,"column":23},"end":{"line":8,"column":38}},"6":{"start":{"line":10,"column":19},"end":{"line":11,"column":null}},"7":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"8":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"9":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"10":{"start":{"line":18,"column":16},"end":{"line":21,"column":null}},"11":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":24,"column":21},"end":{"line":24,"column":null}},"13":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"14":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"15":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}},"16":{"start":{"line":32,"column":22},"end":{"line":34,"column":null}}},"fnMap":{"0":{"name":"authMiddleware","decl":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"loc":{"start":{"line":7,"column":57},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_4)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":5}},"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"type":"binary-expr","locations":[{"start":{"line":11,"column":14},"end":{"line":11,"column":35}},{"start":{"line":11,"column":35},"end":{"line":11,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":14,"column":6},"end":{"line":14,"column":84}},"type":"binary-expr","locations":[{"start":{"line":14,"column":6},"end":{"line":14,"column":18}},{"start":{"line":14,"column":18},"end":{"line":14,"column":51}},{"start":{"line":14,"column":51},"end":{"line":14,"column":84}}]},"3":{"loc":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":27,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0,0,0],"3":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts","statementMap":{"0":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"1":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"2":{"start":{"line":19,"column":22},"end":{"line":19,"column":32}},"3":{"start":{"line":43,"column":22},"end":{"line":43,"column":31}},"4":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"5":{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},"6":{"start":{"line":33,"column":2},"end":{"line":33,"column":null}},"7":{"start":{"line":40,"column":2},"end":{"line":40,"column":null}},"8":{"start":{"line":47,"column":2},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"getCourses","decl":{"start":{"line":19,"column":22},"end":{"line":19,"column":32}},"loc":{"start":{"line":22,"column":22},"end":{"line":27,"column":null}}},"1":{"name":"getCourse","decl":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"loc":{"start":{"line":31,"column":22},"end":{"line":34,"column":null}}},"2":{"name":"getCourseLessons","decl":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"loc":{"start":{"line":38,"column":22},"end":{"line":41,"column":null}}},"3":{"name":"getLesson","decl":{"start":{"line":43,"column":22},"end":{"line":43,"column":31}},"loc":{"start":{"line":45,"column":22},"end":{"line":48,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":10}},"type":"default-arg","locations":[{"start":{"line":20,"column":9},"end":{"line":20,"column":10}}]},"1":{"loc":{"start":{"line":21,"column":2},"end":{"line":21,"column":13}},"type":"default-arg","locations":[{"start":{"line":21,"column":10},"end":{"line":21,"column":13}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}} -,"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts": {"path":"/Users/lucavisconti/BP-Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts","statementMap":{"0":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"1":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"2":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"3":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"4":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"5":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"6":{"start":{"line":25,"column":2},"end":{"line":27,"column":null}},"7":{"start":{"line":35,"column":19},"end":{"line":35,"column":null}},"8":{"start":{"line":36,"column":2},"end":{"line":36,"column":null}},"9":{"start":{"line":38,"column":2},"end":{"line":42,"column":null}},"10":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"11":{"start":{"line":58,"column":2},"end":{"line":61,"column":null}},"12":{"start":{"line":74,"column":2},"end":{"line":88,"column":null}},"13":{"start":{"line":74,"column":15},"end":{"line":74,"column":18}},"14":{"start":{"line":75,"column":4},"end":{"line":86,"column":null}},"15":{"start":{"line":76,"column":21},"end":{"line":76,"column":null}},"16":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"17":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"18":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"19":{"start":{"line":84,"column":8},"end":{"line":84,"column":null}},"20":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"21":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}},"22":{"start":{"line":89,"column":2},"end":{"line":89,"column":null}}},"fnMap":{"0":{"name":"getDocuments","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"loc":{"start":{"line":23,"column":22},"end":{"line":28,"column":null}}},"1":{"name":"uploadDocument","decl":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"loc":{"start":{"line":33,"column":22},"end":{"line":43,"column":null}}},"2":{"name":"getDocumentStatus","decl":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"loc":{"start":{"line":47,"column":22},"end":{"line":52,"column":null}}},"3":{"name":"deleteDocument","decl":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"loc":{"start":{"line":56,"column":22},"end":{"line":62,"column":null}}},"4":{"name":"pollDocumentStatus","decl":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"loc":{"start":{"line":72,"column":18},"end":{"line":90,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":87,"column":22},"end":{"line":87,"column":23}},"loc":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":71,"column":2},"end":{"line":71,"column":19}},"type":"default-arg","locations":[{"start":{"line":71,"column":15},"end":{"line":71,"column":19}}]},"1":{"loc":{"start":{"line":72,"column":2},"end":{"line":72,"column":18}},"type":"default-arg","locations":[{"start":{"line":72,"column":16},"end":{"line":72,"column":18}}]},"2":{"loc":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"type":"if","locations":[{"start":{"line":77,"column":6},"end":{"line":79,"column":null}}]},"3":{"loc":{"start":{"line":77,"column":10},"end":{"line":77,"column":66}},"type":"binary-expr","locations":[{"start":{"line":77,"column":10},"end":{"line":77,"column":39}},{"start":{"line":77,"column":39},"end":{"line":77,"column":66}}]},"4":{"loc":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},{"start":{"line":83,"column":13},"end":{"line":85,"column":null}}]},"5":{"loc":{"start":{"line":81,"column":10},"end":{"line":81,"column":56}},"type":"binary-expr","locations":[{"start":{"line":81,"column":10},"end":{"line":81,"column":33}},{"start":{"line":81,"column":37},"end":{"line":81,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0]}} +{"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/middleware.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/middleware.ts","statementMap":{"0":{"start":{"line":5,"column":36},"end":{"line":5,"column":50}},"1":{"start":{"line":5,"column":9},"end":{"line":5,"column":27}},"2":{"start":{"line":5,"column":50},"end":{"line":5,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0},"f":{},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/error.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/error.tsx","statementMap":{"0":{"start":{"line":5,"column":24},"end":{"line":5,"column":36}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":12,"column":2},"end":{"line":14,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}}},"fnMap":{"0":{"name":"GlobalError","decl":{"start":{"line":5,"column":24},"end":{"line":5,"column":36}},"loc":{"start":{"line":11,"column":1},"end":{"line":29,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"loc":{"start":{"line":12,"column":12},"end":{"line":14,"column":5}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0,"1":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/layout.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/layout.tsx","statementMap":{"0":{"start":{"line":16,"column":24},"end":{"line":16,"column":35}},"1":{"start":{"line":6,"column":13},"end":{"line":6,"column":21}},"2":{"start":{"line":11,"column":13},"end":{"line":11,"column":21}},"3":{"start":{"line":2,"column":7},"end":{"line":2,"column":null}},"4":{"start":{"line":3,"column":29},"end":{"line":3,"column":null}},"5":{"start":{"line":4,"column":34},"end":{"line":4,"column":null}},"6":{"start":{"line":6,"column":34},"end":{"line":9,"column":null}},"7":{"start":{"line":11,"column":34},"end":{"line":14,"column":null}}},"fnMap":{"0":{"name":"RootLayout","decl":{"start":{"line":16,"column":24},"end":{"line":16,"column":35}},"loc":{"start":{"line":16,"column":78},"end":{"line":27,"column":1}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/page.tsx","statementMap":{"0":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":8,"column":17},"end":{"line":8,"column":null}},"5":{"start":{"line":9,"column":21},"end":{"line":9,"column":null}},"6":{"start":{"line":11,"column":2},"end":{"line":17,"column":null}},"7":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"8":{"start":{"line":13,"column":6},"end":{"line":13,"column":null}},"9":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"10":{"start":{"line":15,"column":6},"end":{"line":15,"column":null}},"11":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}}},"fnMap":{"0":{"name":"Home","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"loc":{"start":{"line":7,"column":24},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":11,"column":12},"end":{"line":11,"column":null}},"loc":{"start":{"line":11,"column":12},"end":{"line":17,"column":5}}}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":12,"column":4},"end":{"line":16,"column":null}},{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":11},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":11},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":19,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":2},"end":{"line":27,"column":null}}]},"3":{"loc":{"start":{"line":19,"column":6},"end":{"line":19,"column":60}},"type":"binary-expr","locations":[{"start":{"line":19,"column":6},"end":{"line":19,"column":30}},{"start":{"line":19,"column":30},"end":{"line":19,"column":60}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/layout.tsx","statementMap":{"0":{"start":{"line":8,"column":24},"end":{"line":8,"column":35}},"1":{"start":{"line":2,"column":26},"end":{"line":2,"column":null}}},"fnMap":{"0":{"name":"AuthLayout","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":35}},"loc":{"start":{"line":8,"column":64},"end":{"line":29,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/login/page.tsx","statementMap":{"0":{"start":{"line":221,"column":24},"end":{"line":221,"column":null}},"1":{"start":{"line":3,"column":23},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":17},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":43},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":46},"end":{"line":6,"column":null}},"5":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"6":{"start":{"line":17,"column":20},"end":{"line":17,"column":null}},"7":{"start":{"line":18,"column":23},"end":{"line":18,"column":null}},"8":{"start":{"line":21,"column":2},"end":{"line":21,"column":null}},"9":{"start":{"line":24,"column":17},"end":{"line":24,"column":null}},"10":{"start":{"line":25,"column":23},"end":{"line":25,"column":null}},"11":{"start":{"line":26,"column":22},"end":{"line":26,"column":null}},"12":{"start":{"line":27,"column":21},"end":{"line":27,"column":null}},"13":{"start":{"line":28,"column":23},"end":{"line":28,"column":null}},"14":{"start":{"line":30,"column":28},"end":{"line":30,"column":null}},"15":{"start":{"line":31,"column":34},"end":{"line":31,"column":null}},"16":{"start":{"line":32,"column":36},"end":{"line":32,"column":null}},"17":{"start":{"line":33,"column":30},"end":{"line":33,"column":null}},"18":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"19":{"start":{"line":38,"column":23},"end":{"line":50,"column":null}},"20":{"start":{"line":39,"column":34},"end":{"line":39,"column":null}},"21":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"22":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"23":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"24":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"25":{"start":{"line":45,"column":4},"end":{"line":47,"column":null}},"26":{"start":{"line":46,"column":6},"end":{"line":46,"column":null}},"27":{"start":{"line":48,"column":4},"end":{"line":48,"column":null}},"28":{"start":{"line":49,"column":4},"end":{"line":49,"column":null}},"29":{"start":{"line":52,"column":23},"end":{"line":79,"column":null}},"30":{"start":{"line":53,"column":4},"end":{"line":53,"column":null}},"31":{"start":{"line":54,"column":4},"end":{"line":54,"column":null}},"32":{"start":{"line":55,"column":4},"end":{"line":55,"column":null}},"33":{"start":{"line":55,"column":25},"end":{"line":55,"column":null}},"34":{"start":{"line":56,"column":4},"end":{"line":56,"column":null}},"35":{"start":{"line":57,"column":4},"end":{"line":78,"column":null}},"36":{"start":{"line":58,"column":21},"end":{"line":63,"column":null}},"37":{"start":{"line":64,"column":6},"end":{"line":73,"column":null}},"38":{"start":{"line":66,"column":10},"end":{"line":68,"column":null}},"39":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"40":{"start":{"line":70,"column":13},"end":{"line":73,"column":null}},"41":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"42":{"start":{"line":72,"column":8},"end":{"line":72,"column":null}},"43":{"start":{"line":75,"column":6},"end":{"line":75,"column":null}},"44":{"start":{"line":77,"column":6},"end":{"line":77,"column":null}},"45":{"start":{"line":118,"column":31},"end":{"line":118,"column":null}},"46":{"start":{"line":148,"column":31},"end":{"line":148,"column":null}}},"fnMap":{"0":{"name":"LoginForm","decl":{"start":{"line":23,"column":9},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":9},"end":{"line":219,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":38,"column":23},"end":{"line":38,"column":null}},"loc":{"start":{"line":38,"column":23},"end":{"line":50,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":52,"column":23},"end":{"line":52,"column":30}},"loc":{"start":{"line":52,"column":30},"end":{"line":79,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":118,"column":24},"end":{"line":118,"column":25}},"loc":{"start":{"line":118,"column":31},"end":{"line":118,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":148,"column":24},"end":{"line":148,"column":25}},"loc":{"start":{"line":148,"column":31},"end":{"line":148,"column":null}}},"5":{"name":"LoginPage","decl":{"start":{"line":221,"column":24},"end":{"line":221,"column":null}},"loc":{"start":{"line":221,"column":24},"end":{"line":231,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":22},"end":{"line":26,"column":null}},"type":"binary-expr","locations":[{"start":{"line":26,"column":22},"end":{"line":26,"column":57}},{"start":{"line":26,"column":57},"end":{"line":26,"column":null}}]},"1":{"loc":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"type":"cond-expr","locations":[{"start":{"line":36,"column":38},"end":{"line":36,"column":89}},{"start":{"line":36,"column":89},"end":{"line":36,"column":null}}]},"2":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"4":{"loc":{"start":{"line":45,"column":4},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":47,"column":null}}]},"5":{"loc":{"start":{"line":55,"column":4},"end":{"line":55,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":4},"end":{"line":55,"column":null}}]},"6":{"loc":{"start":{"line":64,"column":6},"end":{"line":73,"column":null}},"type":"if","locations":[{"start":{"line":64,"column":6},"end":{"line":73,"column":null}},{"start":{"line":70,"column":13},"end":{"line":73,"column":null}}]},"7":{"loc":{"start":{"line":66,"column":10},"end":{"line":68,"column":null}},"type":"cond-expr","locations":[{"start":{"line":67,"column":14},"end":{"line":67,"column":null}},{"start":{"line":68,"column":14},"end":{"line":68,"column":null}}]},"8":{"loc":{"start":{"line":70,"column":13},"end":{"line":73,"column":null}},"type":"if","locations":[{"start":{"line":70,"column":13},"end":{"line":73,"column":null}}]},"9":{"loc":{"start":{"line":87,"column":7},"end":{"line":87,"column":null}},"type":"binary-expr","locations":[{"start":{"line":87,"column":7},"end":{"line":87,"column":null}}]},"10":{"loc":{"start":{"line":93,"column":7},"end":{"line":93,"column":null}},"type":"binary-expr","locations":[{"start":{"line":93,"column":7},"end":{"line":93,"column":null}}]},"11":{"loc":{"start":{"line":99,"column":7},"end":{"line":99,"column":21}},"type":"binary-expr","locations":[{"start":{"line":99,"column":7},"end":{"line":99,"column":21}}]},"12":{"loc":{"start":{"line":119,"column":41},"end":{"line":119,"column":85}},"type":"cond-expr","locations":[{"start":{"line":119,"column":56},"end":{"line":119,"column":73}},{"start":{"line":119,"column":73},"end":{"line":119,"column":85}}]},"13":{"loc":{"start":{"line":121,"column":28},"end":{"line":121,"column":null}},"type":"cond-expr","locations":[{"start":{"line":121,"column":43},"end":{"line":121,"column":52}},{"start":{"line":121,"column":52},"end":{"line":121,"column":null}}]},"14":{"loc":{"start":{"line":122,"column":32},"end":{"line":122,"column":null}},"type":"cond-expr","locations":[{"start":{"line":122,"column":47},"end":{"line":122,"column":63}},{"start":{"line":122,"column":63},"end":{"line":122,"column":null}}]},"15":{"loc":{"start":{"line":125,"column":11},"end":{"line":125,"column":23}},"type":"binary-expr","locations":[{"start":{"line":125,"column":11},"end":{"line":125,"column":23}}]},"16":{"loc":{"start":{"line":149,"column":41},"end":{"line":149,"column":88}},"type":"cond-expr","locations":[{"start":{"line":149,"column":59},"end":{"line":149,"column":76}},{"start":{"line":149,"column":76},"end":{"line":149,"column":88}}]},"17":{"loc":{"start":{"line":151,"column":28},"end":{"line":151,"column":null}},"type":"cond-expr","locations":[{"start":{"line":151,"column":46},"end":{"line":151,"column":55}},{"start":{"line":151,"column":55},"end":{"line":151,"column":null}}]},"18":{"loc":{"start":{"line":152,"column":32},"end":{"line":152,"column":null}},"type":"cond-expr","locations":[{"start":{"line":152,"column":50},"end":{"line":152,"column":69}},{"start":{"line":152,"column":69},"end":{"line":152,"column":null}}]},"19":{"loc":{"start":{"line":155,"column":11},"end":{"line":155,"column":26}},"type":"binary-expr","locations":[{"start":{"line":155,"column":11},"end":{"line":155,"column":26}}]},"20":{"loc":{"start":{"line":168,"column":13},"end":{"line":193,"column":null}},"type":"cond-expr","locations":[{"start":{"line":169,"column":14},"end":{"line":193,"column":null}},{"start":{"line":193,"column":14},"end":{"line":193,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":2,"9":327,"10":327,"11":327,"12":327,"13":327,"14":327,"15":325,"16":325,"17":325,"18":325,"19":325,"20":15,"21":15,"22":3,"23":12,"24":2,"25":15,"26":6,"27":15,"28":15,"29":325,"30":15,"31":15,"32":15,"33":6,"34":9,"35":9,"36":9,"37":8,"38":2,"39":2,"40":6,"41":6,"42":6,"43":0,"44":8,"45":179,"46":106},"f":{"0":327,"1":15,"2":15,"3":179,"4":106,"5":20},"b":{"0":[327,262],"1":[36,289],"2":[3,12],"3":[2],"4":[6],"5":[6],"6":[2,6],"7":[0,2],"8":[6],"9":[325],"10":[325],"11":[325],"12":[6,319],"13":[6,319],"14":[6,319],"15":[325],"16":[7,318],"17":[7,318],"18":[7,318],"19":[325],"20":[9,316]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/(auth)/signup/page.tsx","statementMap":{"0":{"start":{"line":27,"column":24},"end":{"line":27,"column":null}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":23},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":36},"end":{"line":6,"column":null}},"5":{"start":{"line":8,"column":16},"end":{"line":8,"column":null}},"6":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"7":{"start":{"line":21,"column":20},"end":{"line":21,"column":null}},"8":{"start":{"line":22,"column":23},"end":{"line":22,"column":null}},"9":{"start":{"line":25,"column":2},"end":{"line":25,"column":null}},"10":{"start":{"line":28,"column":17},"end":{"line":28,"column":null}},"11":{"start":{"line":30,"column":28},"end":{"line":30,"column":null}},"12":{"start":{"line":31,"column":34},"end":{"line":31,"column":null}},"13":{"start":{"line":32,"column":48},"end":{"line":32,"column":null}},"14":{"start":{"line":33,"column":40},"end":{"line":33,"column":null}},"15":{"start":{"line":34,"column":36},"end":{"line":34,"column":null}},"16":{"start":{"line":35,"column":30},"end":{"line":35,"column":null}},"17":{"start":{"line":37,"column":23},"end":{"line":72,"column":null}},"18":{"start":{"line":38,"column":34},"end":{"line":38,"column":null}},"19":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"20":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"21":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"22":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}},"23":{"start":{"line":46,"column":4},"end":{"line":58,"column":null}},"24":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}},"25":{"start":{"line":48,"column":11},"end":{"line":58,"column":null}},"26":{"start":{"line":49,"column":6},"end":{"line":49,"column":null}},"27":{"start":{"line":50,"column":11},"end":{"line":58,"column":null}},"28":{"start":{"line":51,"column":6},"end":{"line":51,"column":null}},"29":{"start":{"line":52,"column":11},"end":{"line":58,"column":null}},"30":{"start":{"line":53,"column":6},"end":{"line":53,"column":null}},"31":{"start":{"line":54,"column":11},"end":{"line":58,"column":null}},"32":{"start":{"line":55,"column":6},"end":{"line":55,"column":null}},"33":{"start":{"line":56,"column":11},"end":{"line":58,"column":null}},"34":{"start":{"line":57,"column":6},"end":{"line":57,"column":null}},"35":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"36":{"start":{"line":61,"column":6},"end":{"line":61,"column":null}},"37":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"38":{"start":{"line":63,"column":6},"end":{"line":63,"column":null}},"39":{"start":{"line":66,"column":4},"end":{"line":68,"column":null}},"40":{"start":{"line":67,"column":6},"end":{"line":67,"column":null}},"41":{"start":{"line":70,"column":4},"end":{"line":70,"column":null}},"42":{"start":{"line":71,"column":4},"end":{"line":71,"column":null}},"43":{"start":{"line":74,"column":23},"end":{"line":122,"column":null}},"44":{"start":{"line":75,"column":4},"end":{"line":75,"column":null}},"45":{"start":{"line":76,"column":4},"end":{"line":76,"column":null}},"46":{"start":{"line":77,"column":4},"end":{"line":77,"column":null}},"47":{"start":{"line":77,"column":25},"end":{"line":77,"column":null}},"48":{"start":{"line":78,"column":4},"end":{"line":78,"column":null}},"49":{"start":{"line":79,"column":4},"end":{"line":121,"column":null}},"50":{"start":{"line":80,"column":31},"end":{"line":84,"column":null}},"51":{"start":{"line":86,"column":6},"end":{"line":102,"column":null}},"52":{"start":{"line":87,"column":22},"end":{"line":87,"column":null}},"53":{"start":{"line":90,"column":23},"end":{"line":92,"column":25}},"54":{"start":{"line":91,"column":53},"end":{"line":91,"column":58}},"55":{"start":{"line":94,"column":8},"end":{"line":100,"column":null}},"56":{"start":{"line":95,"column":10},"end":{"line":95,"column":null}},"57":{"start":{"line":96,"column":15},"end":{"line":100,"column":null}},"58":{"start":{"line":97,"column":10},"end":{"line":97,"column":null}},"59":{"start":{"line":99,"column":10},"end":{"line":99,"column":null}},"60":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"61":{"start":{"line":104,"column":21},"end":{"line":109,"column":null}},"62":{"start":{"line":111,"column":6},"end":{"line":116,"column":null}},"63":{"start":{"line":112,"column":8},"end":{"line":112,"column":null}},"64":{"start":{"line":113,"column":13},"end":{"line":116,"column":null}},"65":{"start":{"line":114,"column":8},"end":{"line":114,"column":null}},"66":{"start":{"line":115,"column":8},"end":{"line":115,"column":null}},"67":{"start":{"line":118,"column":6},"end":{"line":118,"column":null}},"68":{"start":{"line":120,"column":6},"end":{"line":120,"column":null}},"69":{"start":{"line":154,"column":31},"end":{"line":154,"column":null}},"70":{"start":{"line":184,"column":31},"end":{"line":184,"column":null}},"71":{"start":{"line":214,"column":31},"end":{"line":214,"column":null}},"72":{"start":{"line":251,"column":31},"end":{"line":251,"column":null}}},"fnMap":{"0":{"name":"SignupPage","decl":{"start":{"line":27,"column":24},"end":{"line":27,"column":null}},"loc":{"start":{"line":27,"column":24},"end":{"line":322,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":37,"column":23},"end":{"line":37,"column":null}},"loc":{"start":{"line":37,"column":23},"end":{"line":72,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":74,"column":23},"end":{"line":74,"column":30}},"loc":{"start":{"line":74,"column":30},"end":{"line":122,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":91,"column":29},"end":{"line":91,"column":30}},"loc":{"start":{"line":91,"column":53},"end":{"line":91,"column":58}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":154,"column":24},"end":{"line":154,"column":25}},"loc":{"start":{"line":154,"column":31},"end":{"line":154,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":184,"column":24},"end":{"line":184,"column":25}},"loc":{"start":{"line":184,"column":31},"end":{"line":184,"column":null}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":214,"column":24},"end":{"line":214,"column":25}},"loc":{"start":{"line":214,"column":31},"end":{"line":214,"column":null}}},"7":{"name":"(anonymous_9)","decl":{"start":{"line":251,"column":24},"end":{"line":251,"column":25}},"loc":{"start":{"line":251,"column":31},"end":{"line":251,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":16},"end":{"line":8,"column":null}},"type":"binary-expr","locations":[{"start":{"line":8,"column":16},"end":{"line":8,"column":47}},{"start":{"line":8,"column":51},"end":{"line":8,"column":null}}]},"1":{"loc":{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":44,"column":null}},{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":42,"column":11},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":11},"end":{"line":44,"column":null}}]},"3":{"loc":{"start":{"line":46,"column":4},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":46,"column":4},"end":{"line":58,"column":null}},{"start":{"line":48,"column":11},"end":{"line":58,"column":null}}]},"4":{"loc":{"start":{"line":48,"column":11},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":48,"column":11},"end":{"line":58,"column":null}},{"start":{"line":50,"column":11},"end":{"line":58,"column":null}}]},"5":{"loc":{"start":{"line":50,"column":11},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":11},"end":{"line":58,"column":null}},{"start":{"line":52,"column":11},"end":{"line":58,"column":null}}]},"6":{"loc":{"start":{"line":52,"column":11},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":52,"column":11},"end":{"line":58,"column":null}},{"start":{"line":54,"column":11},"end":{"line":58,"column":null}}]},"7":{"loc":{"start":{"line":54,"column":11},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":54,"column":11},"end":{"line":58,"column":null}},{"start":{"line":56,"column":11},"end":{"line":58,"column":null}}]},"8":{"loc":{"start":{"line":56,"column":11},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":56,"column":11},"end":{"line":58,"column":null}}]},"9":{"loc":{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":64,"column":null}},{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"10":{"loc":{"start":{"line":62,"column":11},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":11},"end":{"line":64,"column":null}}]},"11":{"loc":{"start":{"line":66,"column":4},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":4},"end":{"line":68,"column":null}}]},"12":{"loc":{"start":{"line":66,"column":8},"end":{"line":66,"column":47}},"type":"binary-expr","locations":[{"start":{"line":66,"column":8},"end":{"line":66,"column":23}},{"start":{"line":66,"column":23},"end":{"line":66,"column":47}}]},"13":{"loc":{"start":{"line":77,"column":4},"end":{"line":77,"column":null}},"type":"if","locations":[{"start":{"line":77,"column":4},"end":{"line":77,"column":null}}]},"14":{"loc":{"start":{"line":83,"column":62},"end":{"line":83,"column":82}},"type":"binary-expr","locations":[{"start":{"line":83,"column":62},"end":{"line":83,"column":77}},{"start":{"line":83,"column":77},"end":{"line":83,"column":82}}]},"15":{"loc":{"start":{"line":86,"column":6},"end":{"line":102,"column":null}},"type":"if","locations":[{"start":{"line":86,"column":6},"end":{"line":102,"column":null}}]},"16":{"loc":{"start":{"line":90,"column":23},"end":{"line":92,"column":25}},"type":"cond-expr","locations":[{"start":{"line":91,"column":12},"end":{"line":91,"column":null}},{"start":{"line":92,"column":13},"end":{"line":92,"column":25}}]},"17":{"loc":{"start":{"line":94,"column":8},"end":{"line":100,"column":null}},"type":"if","locations":[{"start":{"line":94,"column":8},"end":{"line":100,"column":null}},{"start":{"line":96,"column":15},"end":{"line":100,"column":null}}]},"18":{"loc":{"start":{"line":96,"column":15},"end":{"line":100,"column":null}},"type":"if","locations":[{"start":{"line":96,"column":15},"end":{"line":100,"column":null}},{"start":{"line":98,"column":15},"end":{"line":100,"column":null}}]},"19":{"loc":{"start":{"line":96,"column":19},"end":{"line":96,"column":87}},"type":"binary-expr","locations":[{"start":{"line":96,"column":19},"end":{"line":96,"column":54}},{"start":{"line":96,"column":54},"end":{"line":96,"column":87}}]},"20":{"loc":{"start":{"line":97,"column":31},"end":{"line":97,"column":62}},"type":"binary-expr","locations":[{"start":{"line":97,"column":31},"end":{"line":97,"column":41}},{"start":{"line":97,"column":41},"end":{"line":97,"column":62}}]},"21":{"loc":{"start":{"line":99,"column":31},"end":{"line":99,"column":63}},"type":"binary-expr","locations":[{"start":{"line":99,"column":31},"end":{"line":99,"column":41}},{"start":{"line":99,"column":41},"end":{"line":99,"column":63}}]},"22":{"loc":{"start":{"line":111,"column":6},"end":{"line":116,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":6},"end":{"line":116,"column":null}},{"start":{"line":113,"column":13},"end":{"line":116,"column":null}}]},"23":{"loc":{"start":{"line":113,"column":13},"end":{"line":116,"column":null}},"type":"if","locations":[{"start":{"line":113,"column":13},"end":{"line":116,"column":null}}]},"24":{"loc":{"start":{"line":130,"column":7},"end":{"line":130,"column":21}},"type":"binary-expr","locations":[{"start":{"line":130,"column":7},"end":{"line":130,"column":21}}]},"25":{"loc":{"start":{"line":155,"column":41},"end":{"line":155,"column":91}},"type":"cond-expr","locations":[{"start":{"line":155,"column":62},"end":{"line":155,"column":79}},{"start":{"line":155,"column":79},"end":{"line":155,"column":91}}]},"26":{"loc":{"start":{"line":157,"column":28},"end":{"line":157,"column":null}},"type":"cond-expr","locations":[{"start":{"line":157,"column":49},"end":{"line":157,"column":58}},{"start":{"line":157,"column":58},"end":{"line":157,"column":null}}]},"27":{"loc":{"start":{"line":158,"column":32},"end":{"line":158,"column":null}},"type":"cond-expr","locations":[{"start":{"line":158,"column":53},"end":{"line":158,"column":75}},{"start":{"line":158,"column":75},"end":{"line":158,"column":null}}]},"28":{"loc":{"start":{"line":161,"column":11},"end":{"line":161,"column":29}},"type":"binary-expr","locations":[{"start":{"line":161,"column":11},"end":{"line":161,"column":29}}]},"29":{"loc":{"start":{"line":185,"column":41},"end":{"line":185,"column":85}},"type":"cond-expr","locations":[{"start":{"line":185,"column":56},"end":{"line":185,"column":73}},{"start":{"line":185,"column":73},"end":{"line":185,"column":85}}]},"30":{"loc":{"start":{"line":187,"column":28},"end":{"line":187,"column":null}},"type":"cond-expr","locations":[{"start":{"line":187,"column":43},"end":{"line":187,"column":52}},{"start":{"line":187,"column":52},"end":{"line":187,"column":null}}]},"31":{"loc":{"start":{"line":188,"column":32},"end":{"line":188,"column":null}},"type":"cond-expr","locations":[{"start":{"line":188,"column":47},"end":{"line":188,"column":63}},{"start":{"line":188,"column":63},"end":{"line":188,"column":null}}]},"32":{"loc":{"start":{"line":191,"column":11},"end":{"line":191,"column":23}},"type":"binary-expr","locations":[{"start":{"line":191,"column":11},"end":{"line":191,"column":23}}]},"33":{"loc":{"start":{"line":215,"column":41},"end":{"line":215,"column":88}},"type":"cond-expr","locations":[{"start":{"line":215,"column":59},"end":{"line":215,"column":76}},{"start":{"line":215,"column":76},"end":{"line":215,"column":88}}]},"34":{"loc":{"start":{"line":217,"column":28},"end":{"line":217,"column":null}},"type":"cond-expr","locations":[{"start":{"line":217,"column":46},"end":{"line":217,"column":55}},{"start":{"line":217,"column":55},"end":{"line":217,"column":null}}]},"35":{"loc":{"start":{"line":218,"column":32},"end":{"line":218,"column":null}},"type":"cond-expr","locations":[{"start":{"line":218,"column":50},"end":{"line":218,"column":69}},{"start":{"line":218,"column":69},"end":{"line":218,"column":null}}]},"36":{"loc":{"start":{"line":222,"column":12},"end":{"line":230,"column":13}},"type":"cond-expr","locations":[{"start":{"line":222,"column":12},"end":{"line":230,"column":13}}]},"37":{"loc":{"start":{"line":252,"column":41},"end":{"line":252,"column":95}},"type":"cond-expr","locations":[{"start":{"line":252,"column":66},"end":{"line":252,"column":83}},{"start":{"line":252,"column":83},"end":{"line":252,"column":95}}]},"38":{"loc":{"start":{"line":254,"column":28},"end":{"line":254,"column":null}},"type":"cond-expr","locations":[{"start":{"line":254,"column":53},"end":{"line":254,"column":62}},{"start":{"line":254,"column":62},"end":{"line":254,"column":null}}]},"39":{"loc":{"start":{"line":255,"column":32},"end":{"line":255,"column":null}},"type":"cond-expr","locations":[{"start":{"line":255,"column":57},"end":{"line":255,"column":83}},{"start":{"line":255,"column":83},"end":{"line":255,"column":null}}]},"40":{"loc":{"start":{"line":258,"column":11},"end":{"line":258,"column":33}},"type":"binary-expr","locations":[{"start":{"line":258,"column":11},"end":{"line":258,"column":33}}]},"41":{"loc":{"start":{"line":271,"column":13},"end":{"line":296,"column":null}},"type":"cond-expr","locations":[{"start":{"line":272,"column":14},"end":{"line":296,"column":null}},{"start":{"line":296,"column":14},"end":{"line":296,"column":null}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":2,"9":2,"10":767,"11":767,"12":765,"13":765,"14":765,"15":765,"16":765,"17":765,"18":18,"19":18,"20":1,"21":17,"22":1,"23":18,"24":0,"25":18,"26":1,"27":17,"28":1,"29":16,"30":1,"31":15,"32":1,"33":14,"34":14,"35":18,"36":1,"37":17,"38":1,"39":18,"40":0,"41":18,"42":18,"43":765,"44":18,"45":18,"46":18,"47":18,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":17,"70":281,"71":217,"72":209},"f":{"0":767,"1":18,"2":18,"3":0,"4":17,"5":281,"6":217,"7":209},"b":{"0":[2,2],"1":[1,17],"2":[1],"3":[0,18],"4":[1,17],"5":[1,16],"6":[1,15],"7":[1,14],"8":[14],"9":[1,17],"10":[1],"11":[0],"12":[18,2],"13":[18],"14":[0,0],"15":[0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0],"24":[765],"25":[0,765],"26":[0,765],"27":[0,765],"28":[765],"29":[2,763],"30":[2,763],"31":[2,763],"32":[765],"33":[18,747],"34":[18,747],"35":[18,747],"36":[18],"37":[2,763],"38":[2,763],"39":[2,763],"40":[765],"41":[0,765]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/api/auth/[...nextauth]/route.ts","statementMap":{"0":{"start":{"line":9,"column":9},"end":{"line":9,"column":20}},"1":{"start":{"line":9,"column":25},"end":{"line":9,"column":36}},"2":{"start":{"line":4,"column":21},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":28},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":16},"end":{"line":7,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/error.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/error.tsx","statementMap":{"0":{"start":{"line":5,"column":24},"end":{"line":5,"column":37}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":12,"column":2},"end":{"line":14,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}}},"fnMap":{"0":{"name":"CoursesError","decl":{"start":{"line":5,"column":24},"end":{"line":5,"column":37}},"loc":{"start":{"line":11,"column":1},"end":{"line":27,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"loc":{"start":{"line":12,"column":12},"end":{"line":14,"column":5}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0,"1":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/layout.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/layout.tsx","statementMap":{"0":{"start":{"line":4,"column":24},"end":{"line":4,"column":38}},"1":{"start":{"line":1,"column":23},"end":{"line":1,"column":null}},"2":{"start":{"line":2,"column":30},"end":{"line":2,"column":null}}},"fnMap":{"0":{"name":"CoursesLayout","decl":{"start":{"line":4,"column":24},"end":{"line":4,"column":38}},"loc":{"start":{"line":4,"column":81},"end":{"line":13,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/page.tsx","statementMap":{"0":{"start":{"line":20,"column":24},"end":{"line":20,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":27},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":73},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":36},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":27},"end":{"line":8,"column":null}},"7":{"start":{"line":9,"column":34},"end":{"line":9,"column":null}},"8":{"start":{"line":21,"column":36},"end":{"line":21,"column":null}},"9":{"start":{"line":22,"column":17},"end":{"line":22,"column":null}},"10":{"start":{"line":23,"column":32},"end":{"line":23,"column":null}},"11":{"start":{"line":24,"column":34},"end":{"line":24,"column":null}},"12":{"start":{"line":25,"column":40},"end":{"line":25,"column":null}},"13":{"start":{"line":26,"column":32},"end":{"line":26,"column":null}},"14":{"start":{"line":27,"column":28},"end":{"line":27,"column":null}},"15":{"start":{"line":28,"column":20},"end":{"line":28,"column":null}},"16":{"start":{"line":29,"column":38},"end":{"line":29,"column":null}},"17":{"start":{"line":31,"column":2},"end":{"line":40,"column":null}},"18":{"start":{"line":33,"column":6},"end":{"line":36,"column":null}},"19":{"start":{"line":34,"column":8},"end":{"line":34,"column":null}},"20":{"start":{"line":35,"column":8},"end":{"line":35,"column":null}},"21":{"start":{"line":38,"column":4},"end":{"line":38,"column":null}},"22":{"start":{"line":39,"column":4},"end":{"line":39,"column":null}},"23":{"start":{"line":39,"column":17},"end":{"line":39,"column":null}},"24":{"start":{"line":42,"column":2},"end":{"line":89,"column":null}},"25":{"start":{"line":43,"column":4},"end":{"line":46,"column":null}},"26":{"start":{"line":44,"column":6},"end":{"line":44,"column":null}},"27":{"start":{"line":45,"column":6},"end":{"line":45,"column":null}},"28":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"29":{"start":{"line":47,"column":36},"end":{"line":47,"column":null}},"30":{"start":{"line":49,"column":18},"end":{"line":49,"column":null}},"31":{"start":{"line":52,"column":6},"end":{"line":86,"column":null}},"32":{"start":{"line":53,"column":21},"end":{"line":53,"column":null}},"33":{"start":{"line":54,"column":8},"end":{"line":54,"column":null}},"34":{"start":{"line":56,"column":28},"end":{"line":57,"column":null}},"35":{"start":{"line":57,"column":26},"end":{"line":57,"column":null}},"36":{"start":{"line":60,"column":60},"end":{"line":60,"column":null}},"37":{"start":{"line":61,"column":24},"end":{"line":61,"column":null}},"38":{"start":{"line":62,"column":25},"end":{"line":62,"column":null}},"39":{"start":{"line":63,"column":28},"end":{"line":63,"column":null}},"40":{"start":{"line":64,"column":8},"end":{"line":79,"column":null}},"41":{"start":{"line":65,"column":10},"end":{"line":78,"column":null}},"42":{"start":{"line":66,"column":25},"end":{"line":66,"column":32}},"43":{"start":{"line":67,"column":36},"end":{"line":73,"column":null}},"44":{"start":{"line":69,"column":40},"end":{"line":69,"column":62}},"45":{"start":{"line":70,"column":45},"end":{"line":70,"column":null}},"46":{"start":{"line":72,"column":40},"end":{"line":72,"column":62}},"47":{"start":{"line":74,"column":12},"end":{"line":74,"column":null}},"48":{"start":{"line":75,"column":12},"end":{"line":75,"column":null}},"49":{"start":{"line":76,"column":12},"end":{"line":76,"column":null}},"50":{"start":{"line":77,"column":12},"end":{"line":77,"column":null}},"51":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"52":{"start":{"line":81,"column":8},"end":{"line":81,"column":null}},"53":{"start":{"line":83,"column":8},"end":{"line":83,"column":null}},"54":{"start":{"line":85,"column":8},"end":{"line":85,"column":null}},"55":{"start":{"line":88,"column":4},"end":{"line":88,"column":null}},"56":{"start":{"line":92,"column":20},"end":{"line":92,"column":null}},"57":{"start":{"line":93,"column":4},"end":{"line":93,"column":null}},"58":{"start":{"line":93,"column":25},"end":{"line":93,"column":null}},"59":{"start":{"line":94,"column":4},"end":{"line":94,"column":null}},"60":{"start":{"line":97,"column":2},"end":{"line":114,"column":null}},"61":{"start":{"line":104,"column":14},"end":{"line":104,"column":27}},"62":{"start":{"line":188,"column":27},"end":{"line":188,"column":null}},"63":{"start":{"line":201,"column":57},"end":{"line":201,"column":78}},"64":{"start":{"line":208,"column":12},"end":{"line":208,"column":40}},"65":{"start":{"line":212,"column":27},"end":{"line":212,"column":null}},"66":{"start":{"line":223,"column":42},"end":{"line":223,"column":64}}},"fnMap":{"0":{"name":"CoursesPage","decl":{"start":{"line":20,"column":24},"end":{"line":20,"column":null}},"loc":{"start":{"line":20,"column":24},"end":{"line":227,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":31,"column":12},"end":{"line":31,"column":null}},"loc":{"start":{"line":31,"column":12},"end":{"line":40,"column":5}}},"2":{"name":"onKey","decl":{"start":{"line":32,"column":13},"end":{"line":32,"column":19}},"loc":{"start":{"line":32,"column":35},"end":{"line":37,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":39,"column":11},"end":{"line":39,"column":17}},"loc":{"start":{"line":39,"column":17},"end":{"line":39,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":42,"column":12},"end":{"line":42,"column":null}},"loc":{"start":{"line":42,"column":12},"end":{"line":89,"column":5}}},"5":{"name":"fetchAll","decl":{"start":{"line":51,"column":19},"end":{"line":51,"column":null}},"loc":{"start":{"line":51,"column":19},"end":{"line":87,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":57,"column":19},"end":{"line":57,"column":20}},"loc":{"start":{"line":57,"column":26},"end":{"line":57,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":64,"column":28},"end":{"line":64,"column":29}},"loc":{"start":{"line":64,"column":32},"end":{"line":79,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":69,"column":33},"end":{"line":69,"column":34}},"loc":{"start":{"line":69,"column":40},"end":{"line":69,"column":62}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":70,"column":38},"end":{"line":70,"column":39}},"loc":{"start":{"line":70,"column":45},"end":{"line":70,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":72,"column":33},"end":{"line":72,"column":34}},"loc":{"start":{"line":72,"column":40},"end":{"line":72,"column":62}}},"11":{"name":"handleCreate","decl":{"start":{"line":91,"column":17},"end":{"line":91,"column":30}},"loc":{"start":{"line":91,"column":65},"end":{"line":95,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":93,"column":15},"end":{"line":93,"column":16}},"loc":{"start":{"line":93,"column":25},"end":{"line":93,"column":null}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":103,"column":27},"end":{"line":103,"column":28}},"loc":{"start":{"line":104,"column":14},"end":{"line":104,"column":27}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":188,"column":21},"end":{"line":188,"column":27}},"loc":{"start":{"line":188,"column":27},"end":{"line":188,"column":null}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":201,"column":51},"end":{"line":201,"column":57}},"loc":{"start":{"line":201,"column":57},"end":{"line":201,"column":78}}},"16":{"name":"(anonymous_17)","decl":{"start":{"line":207,"column":23},"end":{"line":207,"column":24}},"loc":{"start":{"line":208,"column":12},"end":{"line":208,"column":40}}},"17":{"name":"(anonymous_18)","decl":{"start":{"line":212,"column":21},"end":{"line":212,"column":27}},"loc":{"start":{"line":212,"column":27},"end":{"line":212,"column":null}}},"18":{"name":"(anonymous_19)","decl":{"start":{"line":223,"column":36},"end":{"line":223,"column":42}},"loc":{"start":{"line":223,"column":42},"end":{"line":223,"column":64}}},"19":{"name":"StatBox","decl":{"start":{"line":229,"column":9},"end":{"line":229,"column":17}},"loc":{"start":{"line":229,"column":73},"end":{"line":241,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":33,"column":6},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":33,"column":6},"end":{"line":36,"column":null}}]},"1":{"loc":{"start":{"line":33,"column":10},"end":{"line":33,"column":53}},"type":"binary-expr","locations":[{"start":{"line":33,"column":11},"end":{"line":33,"column":20}},{"start":{"line":33,"column":24},"end":{"line":33,"column":32}},{"start":{"line":33,"column":38},"end":{"line":33,"column":53}}]},"2":{"loc":{"start":{"line":43,"column":4},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":46,"column":null}}]},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":47,"column":null}}]},"4":{"loc":{"start":{"line":65,"column":10},"end":{"line":78,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":10},"end":{"line":78,"column":null}}]},"5":{"loc":{"start":{"line":70,"column":45},"end":{"line":70,"column":null}},"type":"binary-expr","locations":[{"start":{"line":70,"column":45},"end":{"line":70,"column":74}},{"start":{"line":70,"column":74},"end":{"line":70,"column":null}}]},"6":{"loc":{"start":{"line":83,"column":17},"end":{"line":83,"column":null}},"type":"cond-expr","locations":[{"start":{"line":83,"column":40},"end":{"line":83,"column":51}},{"start":{"line":83,"column":54},"end":{"line":83,"column":null}}]},"7":{"loc":{"start":{"line":97,"column":2},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":97,"column":2},"end":{"line":114,"column":null}}]},"8":{"loc":{"start":{"line":97,"column":6},"end":{"line":97,"column":71}},"type":"binary-expr","locations":[{"start":{"line":97,"column":6},"end":{"line":97,"column":31}},{"start":{"line":97,"column":31},"end":{"line":97,"column":61}},{"start":{"line":97,"column":61},"end":{"line":97,"column":71}}]},"9":{"loc":{"start":{"line":136,"column":32},"end":{"line":136,"column":76}},"type":"cond-expr","locations":[{"start":{"line":136,"column":55},"end":{"line":136,"column":66}},{"start":{"line":136,"column":66},"end":{"line":136,"column":76}}]},"10":{"loc":{"start":{"line":182,"column":8},"end":{"line":194,"column":18}},"type":"cond-expr","locations":[{"start":{"line":182,"column":8},"end":{"line":194,"column":18}}]},"11":{"loc":{"start":{"line":195,"column":8},"end":{"line":206,"column":9}},"type":"cond-expr","locations":[{"start":{"line":195,"column":8},"end":{"line":206,"column":9}}]},"12":{"loc":{"start":{"line":208,"column":63},"end":{"line":208,"column":null}},"type":"binary-expr","locations":[{"start":{"line":208,"column":63},"end":{"line":208,"column":82}},{"start":{"line":208,"column":86},"end":{"line":208,"column":null}}]},"13":{"loc":{"start":{"line":222,"column":7},"end":{"line":222,"column":null}},"type":"binary-expr","locations":[{"start":{"line":222,"column":7},"end":{"line":222,"column":null}}]},"14":{"loc":{"start":{"line":233,"column":48},"end":{"line":233,"column":63}},"type":"cond-expr","locations":[{"start":{"line":233,"column":55},"end":{"line":233,"column":60}},{"start":{"line":233,"column":60},"end":{"line":233,"column":63}}]},"15":{"loc":{"start":{"line":234,"column":15},"end":{"line":234,"column":null}},"type":"cond-expr","locations":[{"start":{"line":234,"column":22},"end":{"line":234,"column":45}},{"start":{"line":234,"column":45},"end":{"line":234,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"b":{"0":[0],"1":[0,0,0],"2":[0],"3":[0],"4":[0],"5":[0,0],"6":[0,0],"7":[0],"8":[0,0,0],"9":[0,0],"10":[0],"11":[0],"12":[0,0],"13":[0],"14":[0,0],"15":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/layout.tsx","statementMap":{"0":{"start":{"line":1,"column":24},"end":{"line":1,"column":37}}},"fnMap":{"0":{"name":"CourseLayout","decl":{"start":{"line":1,"column":24},"end":{"line":1,"column":37}},"loc":{"start":{"line":1,"column":80},"end":{"line":3,"column":null}}}},"branchMap":{},"s":{"0":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/page.tsx","statementMap":{"0":{"start":{"line":73,"column":24},"end":{"line":73,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":39},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":31},"end":{"line":7,"column":null}},"6":{"start":{"line":8,"column":30},"end":{"line":8,"column":null}},"7":{"start":{"line":9,"column":36},"end":{"line":9,"column":null}},"8":{"start":{"line":11,"column":40},"end":{"line":11,"column":null}},"9":{"start":{"line":15,"column":59},"end":{"line":20,"column":null}},"10":{"start":{"line":23,"column":2},"end":{"line":23,"column":null}},"11":{"start":{"line":23,"column":20},"end":{"line":23,"column":null}},"12":{"start":{"line":24,"column":2},"end":{"line":24,"column":null}},"13":{"start":{"line":24,"column":27},"end":{"line":24,"column":null}},"14":{"start":{"line":25,"column":2},"end":{"line":25,"column":null}},"15":{"start":{"line":28,"column":42},"end":{"line":33,"column":null}},"16":{"start":{"line":35,"column":25},"end":{"line":35,"column":null}},"17":{"start":{"line":38,"column":17},"end":{"line":38,"column":null}},"18":{"start":{"line":39,"column":14},"end":{"line":39,"column":null}},"19":{"start":{"line":43,"column":21},"end":{"line":43,"column":null}},"20":{"start":{"line":44,"column":21},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":8},"end":{"line":46,"column":23}},"22":{"start":{"line":74,"column":17},"end":{"line":74,"column":null}},"23":{"start":{"line":75,"column":17},"end":{"line":75,"column":null}},"24":{"start":{"line":76,"column":19},"end":{"line":76,"column":34}},"25":{"start":{"line":77,"column":28},"end":{"line":77,"column":null}},"26":{"start":{"line":78,"column":22},"end":{"line":78,"column":null}},"27":{"start":{"line":80,"column":30},"end":{"line":80,"column":null}},"28":{"start":{"line":81,"column":26},"end":{"line":81,"column":null}},"29":{"start":{"line":82,"column":32},"end":{"line":82,"column":null}},"30":{"start":{"line":83,"column":40},"end":{"line":83,"column":null}},"31":{"start":{"line":84,"column":28},"end":{"line":84,"column":null}},"32":{"start":{"line":85,"column":38},"end":{"line":85,"column":null}},"33":{"start":{"line":86,"column":30},"end":{"line":86,"column":null}},"34":{"start":{"line":87,"column":38},"end":{"line":87,"column":null}},"35":{"start":{"line":89,"column":27},"end":{"line":89,"column":null}},"36":{"start":{"line":89,"column":45},"end":{"line":89,"column":74}},"37":{"start":{"line":89,"column":66},"end":{"line":89,"column":74}},"38":{"start":{"line":91,"column":2},"end":{"line":103,"column":null}},"39":{"start":{"line":93,"column":6},"end":{"line":100,"column":null}},"40":{"start":{"line":94,"column":27},"end":{"line":94,"column":null}},"41":{"start":{"line":95,"column":8},"end":{"line":95,"column":null}},"42":{"start":{"line":97,"column":8},"end":{"line":97,"column":null}},"43":{"start":{"line":99,"column":8},"end":{"line":99,"column":null}},"44":{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},"45":{"start":{"line":102,"column":18},"end":{"line":102,"column":null}},"46":{"start":{"line":105,"column":2},"end":{"line":119,"column":null}},"47":{"start":{"line":107,"column":6},"end":{"line":116,"column":null}},"48":{"start":{"line":108,"column":8},"end":{"line":108,"column":null}},"49":{"start":{"line":109,"column":21},"end":{"line":109,"column":null}},"50":{"start":{"line":110,"column":8},"end":{"line":110,"column":null}},"51":{"start":{"line":111,"column":8},"end":{"line":111,"column":null}},"52":{"start":{"line":111,"column":44},"end":{"line":111,"column":null}},"53":{"start":{"line":113,"column":8},"end":{"line":113,"column":null}},"54":{"start":{"line":115,"column":8},"end":{"line":115,"column":null}},"55":{"start":{"line":118,"column":4},"end":{"line":118,"column":null}},"56":{"start":{"line":118,"column":18},"end":{"line":118,"column":null}},"57":{"start":{"line":121,"column":2},"end":{"line":136,"column":null}},"58":{"start":{"line":138,"column":2},"end":{"line":155,"column":null}},"59":{"start":{"line":147,"column":27},"end":{"line":147,"column":null}},"60":{"start":{"line":157,"column":18},"end":{"line":157,"column":65}},"61":{"start":{"line":157,"column":37},"end":{"line":157,"column":59}},"62":{"start":{"line":158,"column":21},"end":{"line":160,"column":10}},"63":{"start":{"line":159,"column":11},"end":{"line":159,"column":null}},"64":{"start":{"line":161,"column":17},"end":{"line":161,"column":64}},"65":{"start":{"line":161,"column":36},"end":{"line":161,"column":58}},"66":{"start":{"line":163,"column":19},"end":{"line":169,"column":null}},"67":{"start":{"line":164,"column":4},"end":{"line":164,"column":null}},"68":{"start":{"line":164,"column":26},"end":{"line":164,"column":null}},"69":{"start":{"line":165,"column":4},"end":{"line":165,"column":null}},"70":{"start":{"line":165,"column":28},"end":{"line":165,"column":null}},"71":{"start":{"line":166,"column":4},"end":{"line":166,"column":null}},"72":{"start":{"line":166,"column":33},"end":{"line":166,"column":null}},"73":{"start":{"line":167,"column":4},"end":{"line":167,"column":null}},"74":{"start":{"line":167,"column":28},"end":{"line":167,"column":null}},"75":{"start":{"line":168,"column":4},"end":{"line":168,"column":null}},"76":{"start":{"line":171,"column":19},"end":{"line":171,"column":null}},"77":{"start":{"line":171,"column":36},"end":{"line":171,"column":60}},"78":{"start":{"line":202,"column":14},"end":{"line":202,"column":null}},"79":{"start":{"line":208,"column":57},"end":{"line":208,"column":100}},"80":{"start":{"line":240,"column":14},"end":{"line":241,"column":null}},"81":{"start":{"line":242,"column":31},"end":{"line":242,"column":null}},"82":{"start":{"line":269,"column":18},"end":{"line":269,"column":31}},"83":{"start":{"line":278,"column":16},"end":{"line":279,"column":null}},"84":{"start":{"line":282,"column":34},"end":{"line":282,"column":null}},"85":{"start":{"line":283,"column":32},"end":{"line":283,"column":null}},"86":{"start":{"line":338,"column":24},"end":{"line":338,"column":null}},"87":{"start":{"line":347,"column":24},"end":{"line":347,"column":null}},"88":{"start":{"line":354,"column":37},"end":{"line":354,"column":null}},"89":{"start":{"line":382,"column":14},"end":{"line":382,"column":null}},"90":{"start":{"line":402,"column":14},"end":{"line":402,"column":null}},"91":{"start":{"line":403,"column":19},"end":{"line":403,"column":null}},"92":{"start":{"line":436,"column":10},"end":{"line":436,"column":null}},"93":{"start":{"line":437,"column":10},"end":{"line":437,"column":null}}},"fnMap":{"0":{"name":"formatSize","decl":{"start":{"line":22,"column":9},"end":{"line":22,"column":20}},"loc":{"start":{"line":22,"column":33},"end":{"line":26,"column":null}}},"1":{"name":"Lifecycle","decl":{"start":{"line":37,"column":9},"end":{"line":37,"column":19}},"loc":{"start":{"line":37,"column":49},"end":{"line":71,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":42,"column":28},"end":{"line":42,"column":29}},"loc":{"start":{"line":42,"column":32},"end":{"line":63,"column":null}}},"3":{"name":"CourseWorkspacePage","decl":{"start":{"line":73,"column":24},"end":{"line":73,"column":null}},"loc":{"start":{"line":73,"column":24},"end":{"line":379,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":89,"column":39},"end":{"line":89,"column":45}},"loc":{"start":{"line":89,"column":45},"end":{"line":89,"column":74}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":89,"column":59},"end":{"line":89,"column":60}},"loc":{"start":{"line":89,"column":66},"end":{"line":89,"column":74}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":91,"column":12},"end":{"line":91,"column":null}},"loc":{"start":{"line":91,"column":12},"end":{"line":103,"column":5}}},"7":{"name":"load","decl":{"start":{"line":92,"column":19},"end":{"line":92,"column":null}},"loc":{"start":{"line":92,"column":19},"end":{"line":101,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":105,"column":12},"end":{"line":105,"column":null}},"loc":{"start":{"line":105,"column":12},"end":{"line":119,"column":5}}},"9":{"name":"loadDocs","decl":{"start":{"line":106,"column":19},"end":{"line":106,"column":null}},"loc":{"start":{"line":106,"column":19},"end":{"line":117,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":147,"column":21},"end":{"line":147,"column":27}},"loc":{"start":{"line":147,"column":27},"end":{"line":147,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":157,"column":30},"end":{"line":157,"column":31}},"loc":{"start":{"line":157,"column":37},"end":{"line":157,"column":59}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":159,"column":4},"end":{"line":159,"column":5}},"loc":{"start":{"line":159,"column":11},"end":{"line":159,"column":null}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":161,"column":29},"end":{"line":161,"column":30}},"loc":{"start":{"line":161,"column":36},"end":{"line":161,"column":58}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":163,"column":31},"end":{"line":163,"column":32}},"loc":{"start":{"line":163,"column":32},"end":{"line":169,"column":null}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":171,"column":29},"end":{"line":171,"column":30}},"loc":{"start":{"line":171,"column":36},"end":{"line":171,"column":60}}},"16":{"name":"(anonymous_17)","decl":{"start":{"line":201,"column":21},"end":{"line":201,"column":null}},"loc":{"start":{"line":202,"column":14},"end":{"line":202,"column":null}}},"17":{"name":"(anonymous_18)","decl":{"start":{"line":208,"column":51},"end":{"line":208,"column":57}},"loc":{"start":{"line":208,"column":57},"end":{"line":208,"column":100}}},"18":{"name":"(anonymous_19)","decl":{"start":{"line":239,"column":32},"end":{"line":239,"column":33}},"loc":{"start":{"line":240,"column":14},"end":{"line":241,"column":null}}},"19":{"name":"(anonymous_20)","decl":{"start":{"line":242,"column":25},"end":{"line":242,"column":31}},"loc":{"start":{"line":242,"column":31},"end":{"line":242,"column":null}}},"20":{"name":"(anonymous_21)","decl":{"start":{"line":268,"column":31},"end":{"line":268,"column":32}},"loc":{"start":{"line":269,"column":18},"end":{"line":269,"column":31}}},"21":{"name":"(anonymous_22)","decl":{"start":{"line":277,"column":27},"end":{"line":277,"column":28}},"loc":{"start":{"line":278,"column":16},"end":{"line":279,"column":null}}},"22":{"name":"(anonymous_23)","decl":{"start":{"line":282,"column":28},"end":{"line":282,"column":34}},"loc":{"start":{"line":282,"column":34},"end":{"line":282,"column":null}}},"23":{"name":"(anonymous_24)","decl":{"start":{"line":283,"column":26},"end":{"line":283,"column":32}},"loc":{"start":{"line":283,"column":32},"end":{"line":283,"column":null}}},"24":{"name":"(anonymous_25)","decl":{"start":{"line":337,"column":37},"end":{"line":337,"column":null}},"loc":{"start":{"line":338,"column":24},"end":{"line":338,"column":null}}},"25":{"name":"(anonymous_26)","decl":{"start":{"line":346,"column":31},"end":{"line":346,"column":null}},"loc":{"start":{"line":347,"column":24},"end":{"line":347,"column":null}}},"26":{"name":"(anonymous_27)","decl":{"start":{"line":354,"column":31},"end":{"line":354,"column":37}},"loc":{"start":{"line":354,"column":37},"end":{"line":354,"column":null}}},"27":{"name":"Stat2","decl":{"start":{"line":381,"column":9},"end":{"line":381,"column":15}},"loc":{"start":{"line":381,"column":91},"end":{"line":389,"column":null}}},"28":{"name":"DocRow","decl":{"start":{"line":391,"column":9},"end":{"line":391,"column":16}},"loc":{"start":{"line":401,"column":1},"end":{"line":445,"column":null}}},"29":{"name":"(anonymous_30)","decl":{"start":{"line":435,"column":17},"end":{"line":435,"column":18}},"loc":{"start":{"line":435,"column":18},"end":{"line":438,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":23,"column":2},"end":{"line":23,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":23,"column":null}}]},"1":{"loc":{"start":{"line":24,"column":2},"end":{"line":24,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":24,"column":null}}]},"2":{"loc":{"start":{"line":39,"column":14},"end":{"line":39,"column":null}},"type":"cond-expr","locations":[{"start":{"line":39,"column":23},"end":{"line":39,"column":28}},{"start":{"line":39,"column":28},"end":{"line":39,"column":null}}]},"3":{"loc":{"start":{"line":43,"column":21},"end":{"line":43,"column":null}},"type":"binary-expr","locations":[{"start":{"line":43,"column":21},"end":{"line":43,"column":32}},{"start":{"line":43,"column":32},"end":{"line":43,"column":null}}]},"4":{"loc":{"start":{"line":44,"column":21},"end":{"line":44,"column":null}},"type":"binary-expr","locations":[{"start":{"line":44,"column":21},"end":{"line":44,"column":32}},{"start":{"line":44,"column":32},"end":{"line":44,"column":null}}]},"5":{"loc":{"start":{"line":49,"column":16},"end":{"line":53,"column":null}},"type":"cond-expr","locations":[{"start":{"line":50,"column":20},"end":{"line":50,"column":null}},{"start":{"line":51,"column":20},"end":{"line":53,"column":null}}]},"6":{"loc":{"start":{"line":51,"column":20},"end":{"line":53,"column":null}},"type":"cond-expr","locations":[{"start":{"line":52,"column":22},"end":{"line":52,"column":null}},{"start":{"line":53,"column":22},"end":{"line":53,"column":null}}]},"7":{"loc":{"start":{"line":58,"column":13},"end":{"line":58,"column":null}},"type":"binary-expr","locations":[{"start":{"line":58,"column":13},"end":{"line":58,"column":null}}]},"8":{"loc":{"start":{"line":64,"column":7},"end":{"line":64,"column":null}},"type":"binary-expr","locations":[{"start":{"line":64,"column":7},"end":{"line":64,"column":null}}]},"9":{"loc":{"start":{"line":97,"column":17},"end":{"line":97,"column":null}},"type":"cond-expr","locations":[{"start":{"line":97,"column":40},"end":{"line":97,"column":51}},{"start":{"line":97,"column":54},"end":{"line":97,"column":null}}]},"10":{"loc":{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},"type":"if","locations":[{"start":{"line":102,"column":4},"end":{"line":102,"column":null}}]},"11":{"loc":{"start":{"line":111,"column":8},"end":{"line":111,"column":null}},"type":"if","locations":[{"start":{"line":111,"column":8},"end":{"line":111,"column":null}}]},"12":{"loc":{"start":{"line":111,"column":12},"end":{"line":111,"column":44}},"type":"binary-expr","locations":[{"start":{"line":111,"column":12},"end":{"line":111,"column":31}},{"start":{"line":111,"column":31},"end":{"line":111,"column":44}}]},"13":{"loc":{"start":{"line":118,"column":4},"end":{"line":118,"column":null}},"type":"if","locations":[{"start":{"line":118,"column":4},"end":{"line":118,"column":null}}]},"14":{"loc":{"start":{"line":121,"column":2},"end":{"line":136,"column":null}},"type":"if","locations":[{"start":{"line":121,"column":2},"end":{"line":136,"column":null}}]},"15":{"loc":{"start":{"line":138,"column":2},"end":{"line":155,"column":null}},"type":"if","locations":[{"start":{"line":138,"column":2},"end":{"line":155,"column":null}}]},"16":{"loc":{"start":{"line":138,"column":6},"end":{"line":138,"column":24}},"type":"binary-expr","locations":[{"start":{"line":138,"column":6},"end":{"line":138,"column":15}},{"start":{"line":138,"column":15},"end":{"line":138,"column":24}}]},"17":{"loc":{"start":{"line":145,"column":34},"end":{"line":145,"column":null}},"type":"binary-expr","locations":[{"start":{"line":145,"column":34},"end":{"line":145,"column":43}},{"start":{"line":145,"column":43},"end":{"line":145,"column":null}}]},"18":{"loc":{"start":{"line":159,"column":11},"end":{"line":159,"column":null}},"type":"binary-expr","locations":[{"start":{"line":159,"column":11},"end":{"line":159,"column":40}},{"start":{"line":159,"column":40},"end":{"line":159,"column":null}}]},"19":{"loc":{"start":{"line":164,"column":4},"end":{"line":164,"column":null}},"type":"if","locations":[{"start":{"line":164,"column":4},"end":{"line":164,"column":null}}]},"20":{"loc":{"start":{"line":165,"column":4},"end":{"line":165,"column":null}},"type":"if","locations":[{"start":{"line":165,"column":4},"end":{"line":165,"column":null}}]},"21":{"loc":{"start":{"line":166,"column":4},"end":{"line":166,"column":null}},"type":"if","locations":[{"start":{"line":166,"column":4},"end":{"line":166,"column":null}}]},"22":{"loc":{"start":{"line":166,"column":40},"end":{"line":166,"column":null}},"type":"binary-expr","locations":[{"start":{"line":166,"column":40},"end":{"line":166,"column":69}},{"start":{"line":166,"column":69},"end":{"line":166,"column":null}}]},"23":{"loc":{"start":{"line":167,"column":4},"end":{"line":167,"column":null}},"type":"if","locations":[{"start":{"line":167,"column":4},"end":{"line":167,"column":null}}]},"24":{"loc":{"start":{"line":171,"column":19},"end":{"line":171,"column":null}},"type":"binary-expr","locations":[{"start":{"line":171,"column":19},"end":{"line":171,"column":60}},{"start":{"line":171,"column":60},"end":{"line":171,"column":null}}]},"25":{"loc":{"start":{"line":186,"column":11},"end":{"line":186,"column":29}},"type":"binary-expr","locations":[{"start":{"line":186,"column":11},"end":{"line":186,"column":29}}]},"26":{"loc":{"start":{"line":202,"column":14},"end":{"line":202,"column":null}},"type":"binary-expr","locations":[{"start":{"line":202,"column":14},"end":{"line":202,"column":26}},{"start":{"line":202,"column":26},"end":{"line":202,"column":null}}]},"27":{"loc":{"start":{"line":244,"column":18},"end":{"line":246,"column":null}},"type":"cond-expr","locations":[{"start":{"line":245,"column":22},"end":{"line":245,"column":null}},{"start":{"line":246,"column":22},"end":{"line":246,"column":null}}]},"28":{"loc":{"start":{"line":266,"column":13},"end":{"line":279,"column":null}},"type":"cond-expr","locations":[{"start":{"line":267,"column":14},"end":{"line":272,"column":25}},{"start":{"line":272,"column":16},"end":{"line":279,"column":null}}]},"29":{"loc":{"start":{"line":272,"column":16},"end":{"line":279,"column":null}},"type":"cond-expr","locations":[{"start":{"line":273,"column":14},"end":{"line":277,"column":23}},{"start":{"line":277,"column":14},"end":{"line":279,"column":null}}]},"30":{"loc":{"start":{"line":274,"column":17},"end":{"line":274,"column":null}},"type":"cond-expr","locations":[{"start":{"line":274,"column":36},"end":{"line":274,"column":67}},{"start":{"line":274,"column":67},"end":{"line":274,"column":null}}]},"31":{"loc":{"start":{"line":294,"column":14},"end":{"line":365,"column":15}},"type":"cond-expr","locations":[{"start":{"line":294,"column":14},"end":{"line":365,"column":15}}]},"32":{"loc":{"start":{"line":301,"column":21},"end":{"line":301,"column":56}},"type":"binary-expr","locations":[{"start":{"line":301,"column":21},"end":{"line":301,"column":42}},{"start":{"line":301,"column":46},"end":{"line":301,"column":56}}]},"33":{"loc":{"start":{"line":312,"column":21},"end":{"line":312,"column":45}},"type":"binary-expr","locations":[{"start":{"line":312,"column":21},"end":{"line":312,"column":45}}]},"34":{"loc":{"start":{"line":319,"column":19},"end":{"line":319,"column":50}},"type":"binary-expr","locations":[{"start":{"line":319,"column":19},"end":{"line":319,"column":50}},{"start":{"line":319,"column":50},"end":{"line":319,"column":71}}]},"35":{"loc":{"start":{"line":382,"column":14},"end":{"line":382,"column":null}},"type":"cond-expr","locations":[{"start":{"line":382,"column":20},"end":{"line":382,"column":33}},{"start":{"line":382,"column":33},"end":{"line":382,"column":null}}]},"36":{"loc":{"start":{"line":382,"column":33},"end":{"line":382,"column":null}},"type":"cond-expr","locations":[{"start":{"line":382,"column":40},"end":{"line":382,"column":54}},{"start":{"line":382,"column":54},"end":{"line":382,"column":null}}]},"37":{"loc":{"start":{"line":402,"column":14},"end":{"line":402,"column":null}},"type":"binary-expr","locations":[{"start":{"line":402,"column":14},"end":{"line":402,"column":35}},{"start":{"line":402,"column":39},"end":{"line":402,"column":null}}]},"38":{"loc":{"start":{"line":403,"column":19},"end":{"line":403,"column":null}},"type":"binary-expr","locations":[{"start":{"line":403,"column":19},"end":{"line":403,"column":50}},{"start":{"line":403,"column":50},"end":{"line":403,"column":null}}]},"39":{"loc":{"start":{"line":409,"column":8},"end":{"line":409,"column":null}},"type":"cond-expr","locations":[{"start":{"line":409,"column":19},"end":{"line":409,"column":55}},{"start":{"line":409,"column":55},"end":{"line":409,"column":null}}]},"40":{"loc":{"start":{"line":417,"column":13},"end":{"line":417,"column":null}},"type":"binary-expr","locations":[{"start":{"line":417,"column":13},"end":{"line":417,"column":29}},{"start":{"line":417,"column":33},"end":{"line":417,"column":null}}]},"41":{"loc":{"start":{"line":427,"column":64},"end":{"line":427,"column":91}},"type":"cond-expr","locations":[{"start":{"line":427,"column":75},"end":{"line":427,"column":88}},{"start":{"line":427,"column":88},"end":{"line":427,"column":91}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0],"8":[0],"9":[0,0],"10":[0],"11":[0],"12":[0,0],"13":[0],"14":[0],"15":[0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0],"20":[0],"21":[0],"22":[0,0],"23":[0],"24":[0,0],"25":[0],"26":[0,0],"27":[0,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0],"32":[0,0],"33":[0],"34":[0,0],"35":[0,0],"36":[0,0],"37":[0,0],"38":[0,0],"39":[0,0],"40":[0,0],"41":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/debug/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/debug/page.tsx","statementMap":{"0":{"start":{"line":14,"column":24},"end":{"line":14,"column":null}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":11,"column":7},"end":{"line":11,"column":null}},"5":{"start":{"line":15,"column":17},"end":{"line":15,"column":null}},"6":{"start":{"line":16,"column":17},"end":{"line":16,"column":null}},"7":{"start":{"line":17,"column":19},"end":{"line":17,"column":34}},"8":{"start":{"line":18,"column":28},"end":{"line":18,"column":null}},"9":{"start":{"line":19,"column":22},"end":{"line":19,"column":null}},"10":{"start":{"line":21,"column":30},"end":{"line":21,"column":null}},"11":{"start":{"line":22,"column":40},"end":{"line":22,"column":null}},"12":{"start":{"line":24,"column":28},"end":{"line":24,"column":null}},"13":{"start":{"line":25,"column":30},"end":{"line":25,"column":null}},"14":{"start":{"line":26,"column":48},"end":{"line":26,"column":null}},"15":{"start":{"line":27,"column":42},"end":{"line":27,"column":null}},"16":{"start":{"line":28,"column":34},"end":{"line":28,"column":null}},"17":{"start":{"line":30,"column":21},"end":{"line":37,"column":null}},"18":{"start":{"line":31,"column":4},"end":{"line":36,"column":null}},"19":{"start":{"line":32,"column":16},"end":{"line":32,"column":null}},"20":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"21":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"22":{"start":{"line":39,"column":2},"end":{"line":41,"column":null}},"23":{"start":{"line":40,"column":4},"end":{"line":40,"column":null}},"24":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"25":{"start":{"line":44,"column":23},"end":{"line":44,"column":null}},"26":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"27":{"start":{"line":46,"column":4},"end":{"line":53,"column":null}},"28":{"start":{"line":47,"column":21},"end":{"line":47,"column":null}},"29":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"30":{"start":{"line":50,"column":6},"end":{"line":50,"column":null}},"31":{"start":{"line":52,"column":6},"end":{"line":52,"column":null}},"32":{"start":{"line":57,"column":4},"end":{"line":57,"column":null}},"33":{"start":{"line":57,"column":23},"end":{"line":57,"column":null}},"34":{"start":{"line":58,"column":4},"end":{"line":58,"column":null}},"35":{"start":{"line":59,"column":4},"end":{"line":66,"column":null}},"36":{"start":{"line":60,"column":19},"end":{"line":60,"column":null}},"37":{"start":{"line":61,"column":6},"end":{"line":61,"column":null}},"38":{"start":{"line":63,"column":6},"end":{"line":63,"column":null}},"39":{"start":{"line":65,"column":6},"end":{"line":65,"column":null}},"40":{"start":{"line":73,"column":25},"end":{"line":73,"column":null}},"41":{"start":{"line":125,"column":20},"end":{"line":125,"column":37}},"42":{"start":{"line":156,"column":31},"end":{"line":156,"column":null}},"43":{"start":{"line":162,"column":31},"end":{"line":162,"column":null}},"44":{"start":{"line":166,"column":16},"end":{"line":166,"column":32}},"45":{"start":{"line":208,"column":18},"end":{"line":209,"column":null}}},"fnMap":{"0":{"name":"DebugPage","decl":{"start":{"line":14,"column":24},"end":{"line":14,"column":null}},"loc":{"start":{"line":14,"column":24},"end":{"line":239,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":30,"column":33},"end":{"line":30,"column":null}},"loc":{"start":{"line":30,"column":33},"end":{"line":37,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":39,"column":12},"end":{"line":39,"column":null}},"loc":{"start":{"line":39,"column":12},"end":{"line":41,"column":5}}},"3":{"name":"handleTestRetrieval","decl":{"start":{"line":43,"column":17},"end":{"line":43,"column":null}},"loc":{"start":{"line":43,"column":17},"end":{"line":54,"column":null}}},"4":{"name":"handleGetEvidence","decl":{"start":{"line":56,"column":17},"end":{"line":56,"column":null}},"loc":{"start":{"line":56,"column":17},"end":{"line":67,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":73,"column":19},"end":{"line":73,"column":25}},"loc":{"start":{"line":73,"column":25},"end":{"line":73,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":124,"column":63},"end":{"line":124,"column":64}},"loc":{"start":{"line":125,"column":20},"end":{"line":125,"column":37}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":156,"column":24},"end":{"line":156,"column":25}},"loc":{"start":{"line":156,"column":31},"end":{"line":156,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":162,"column":24},"end":{"line":162,"column":25}},"loc":{"start":{"line":162,"column":31},"end":{"line":162,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":165,"column":90},"end":{"line":165,"column":91}},"loc":{"start":{"line":166,"column":16},"end":{"line":166,"column":32}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":207,"column":41},"end":{"line":207,"column":42}},"loc":{"start":{"line":208,"column":18},"end":{"line":209,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":35,"column":21},"end":{"line":35,"column":null}},"type":"cond-expr","locations":[{"start":{"line":35,"column":44},"end":{"line":35,"column":55}},{"start":{"line":35,"column":58},"end":{"line":35,"column":null}}]},"1":{"loc":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":44,"column":4},"end":{"line":44,"column":null}}]},"2":{"loc":{"start":{"line":50,"column":34},"end":{"line":50,"column":80}},"type":"cond-expr","locations":[{"start":{"line":50,"column":57},"end":{"line":50,"column":68}},{"start":{"line":50,"column":71},"end":{"line":50,"column":80}}]},"3":{"loc":{"start":{"line":57,"column":4},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":57,"column":4},"end":{"line":57,"column":null}}]},"4":{"loc":{"start":{"line":109,"column":11},"end":{"line":109,"column":26}},"type":"binary-expr","locations":[{"start":{"line":109,"column":11},"end":{"line":109,"column":26}}]},"5":{"loc":{"start":{"line":110,"column":11},"end":{"line":110,"column":null}},"type":"binary-expr","locations":[{"start":{"line":110,"column":11},"end":{"line":110,"column":null}}]},"6":{"loc":{"start":{"line":114,"column":53},"end":{"line":114,"column":115}},"type":"cond-expr","locations":[{"start":{"line":114,"column":85},"end":{"line":114,"column":102}},{"start":{"line":114,"column":102},"end":{"line":114,"column":115}}]},"7":{"loc":{"start":{"line":129,"column":19},"end":{"line":129,"column":null}},"type":"binary-expr","locations":[{"start":{"line":129,"column":19},"end":{"line":129,"column":null}}]},"8":{"loc":{"start":{"line":140,"column":11},"end":{"line":140,"column":22}},"type":"binary-expr","locations":[{"start":{"line":140,"column":11},"end":{"line":140,"column":22}},{"start":{"line":140,"column":22},"end":{"line":140,"column":null}}]},"9":{"loc":{"start":{"line":175,"column":24},"end":{"line":175,"column":null}},"type":"binary-expr","locations":[{"start":{"line":175,"column":24},"end":{"line":175,"column":36}},{"start":{"line":175,"column":36},"end":{"line":175,"column":null}}]},"10":{"loc":{"start":{"line":182,"column":24},"end":{"line":182,"column":null}},"type":"binary-expr","locations":[{"start":{"line":182,"column":24},"end":{"line":182,"column":36}},{"start":{"line":182,"column":36},"end":{"line":182,"column":null}}]},"11":{"loc":{"start":{"line":189,"column":11},"end":{"line":189,"column":null}},"type":"binary-expr","locations":[{"start":{"line":189,"column":11},"end":{"line":189,"column":null}}]},"12":{"loc":{"start":{"line":192,"column":34},"end":{"line":192,"column":69}},"type":"binary-expr","locations":[{"start":{"line":192,"column":34},"end":{"line":192,"column":63}},{"start":{"line":192,"column":67},"end":{"line":192,"column":69}}]},"13":{"loc":{"start":{"line":200,"column":11},"end":{"line":200,"column":null}},"type":"binary-expr","locations":[{"start":{"line":200,"column":11},"end":{"line":200,"column":null}}]},"14":{"loc":{"start":{"line":220,"column":21},"end":{"line":220,"column":41}},"type":"binary-expr","locations":[{"start":{"line":220,"column":21},"end":{"line":220,"column":41}}]},"15":{"loc":{"start":{"line":225,"column":23},"end":{"line":225,"column":null}},"type":"cond-expr","locations":[{"start":{"line":225,"column":49},"end":{"line":225,"column":55}},{"start":{"line":225,"column":55},"end":{"line":225,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0],"4":[0],"5":[0],"6":[0,0],"7":[0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0],"12":[0,0],"13":[0],"14":[0],"15":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx","statementMap":{"0":{"start":{"line":409,"column":24},"end":{"line":409,"column":null}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":67},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":54},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":27},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":39},"end":{"line":7,"column":null}},"6":{"start":{"line":13,"column":15},"end":{"line":13,"column":null}},"7":{"start":{"line":14,"column":2},"end":{"line":17,"column":null}},"8":{"start":{"line":15,"column":16},"end":{"line":15,"column":null}},"9":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"10":{"start":{"line":16,"column":24},"end":{"line":16,"column":null}},"11":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"12":{"start":{"line":18,"column":65},"end":{"line":18,"column":null}},"13":{"start":{"line":42,"column":24},"end":{"line":42,"column":null}},"14":{"start":{"line":42,"column":60},"end":{"line":42,"column":null}},"15":{"start":{"line":59,"column":31},"end":{"line":59,"column":61}},"16":{"start":{"line":60,"column":14},"end":{"line":61,"column":null}},"17":{"start":{"line":63,"column":35},"end":{"line":63,"column":null}},"18":{"start":{"line":93,"column":23},"end":{"line":93,"column":null}},"19":{"start":{"line":94,"column":20},"end":{"line":94,"column":null}},"20":{"start":{"line":96,"column":2},"end":{"line":98,"column":null}},"21":{"start":{"line":97,"column":4},"end":{"line":97,"column":null}},"22":{"start":{"line":100,"column":2},"end":{"line":117,"column":null}},"23":{"start":{"line":122,"column":25},"end":{"line":122,"column":null}},"24":{"start":{"line":123,"column":8},"end":{"line":125,"column":null}},"25":{"start":{"line":129,"column":20},"end":{"line":129,"column":null}},"26":{"start":{"line":174,"column":22},"end":{"line":174,"column":41}},"27":{"start":{"line":175,"column":18},"end":{"line":175,"column":89}},"28":{"start":{"line":176,"column":21},"end":{"line":176,"column":111}},"29":{"start":{"line":177,"column":18},"end":{"line":177,"column":105}},"30":{"start":{"line":192,"column":29},"end":{"line":192,"column":null}},"31":{"start":{"line":193,"column":12},"end":{"line":195,"column":null}},"32":{"start":{"line":196,"column":31},"end":{"line":196,"column":null}},"33":{"start":{"line":248,"column":17},"end":{"line":248,"column":null}},"34":{"start":{"line":249,"column":17},"end":{"line":249,"column":null}},"35":{"start":{"line":250,"column":23},"end":{"line":250,"column":null}},"36":{"start":{"line":251,"column":28},"end":{"line":251,"column":null}},"37":{"start":{"line":253,"column":19},"end":{"line":253,"column":34}},"38":{"start":{"line":254,"column":21},"end":{"line":254,"column":38}},"39":{"start":{"line":255,"column":22},"end":{"line":255,"column":null}},"40":{"start":{"line":257,"column":20},"end":{"line":257,"column":null}},"41":{"start":{"line":259,"column":32},"end":{"line":259,"column":null}},"42":{"start":{"line":260,"column":32},"end":{"line":260,"column":null}},"43":{"start":{"line":261,"column":28},"end":{"line":261,"column":null}},"44":{"start":{"line":262,"column":40},"end":{"line":262,"column":null}},"45":{"start":{"line":264,"column":15},"end":{"line":276,"column":null}},"46":{"start":{"line":265,"column":4},"end":{"line":265,"column":null}},"47":{"start":{"line":265,"column":22},"end":{"line":265,"column":null}},"48":{"start":{"line":266,"column":4},"end":{"line":275,"column":null}},"49":{"start":{"line":267,"column":6},"end":{"line":267,"column":null}},"50":{"start":{"line":268,"column":6},"end":{"line":268,"column":null}},"51":{"start":{"line":269,"column":19},"end":{"line":269,"column":null}},"52":{"start":{"line":270,"column":6},"end":{"line":270,"column":null}},"53":{"start":{"line":272,"column":6},"end":{"line":272,"column":null}},"54":{"start":{"line":274,"column":6},"end":{"line":274,"column":null}},"55":{"start":{"line":278,"column":2},"end":{"line":280,"column":null}},"56":{"start":{"line":279,"column":4},"end":{"line":279,"column":null}},"57":{"start":{"line":283,"column":2},"end":{"line":288,"column":null}},"58":{"start":{"line":284,"column":4},"end":{"line":287,"column":null}},"59":{"start":{"line":285,"column":22},"end":{"line":285,"column":null}},"60":{"start":{"line":286,"column":6},"end":{"line":286,"column":null}},"61":{"start":{"line":290,"column":2},"end":{"line":290,"column":null}},"62":{"start":{"line":290,"column":15},"end":{"line":290,"column":null}},"63":{"start":{"line":292,"column":2},"end":{"line":313,"column":null}},"64":{"start":{"line":304,"column":29},"end":{"line":304,"column":null}},"65":{"start":{"line":315,"column":17},"end":{"line":315,"column":43}},"66":{"start":{"line":316,"column":19},"end":{"line":316,"column":null}},"67":{"start":{"line":323,"column":25},"end":{"line":323,"column":null}},"68":{"start":{"line":356,"column":29},"end":{"line":356,"column":null}},"69":{"start":{"line":356,"column":51},"end":{"line":356,"column":null}},"70":{"start":{"line":367,"column":29},"end":{"line":367,"column":null}},"71":{"start":{"line":367,"column":51},"end":{"line":367,"column":null}}},"fnMap":{"0":{"name":"uniqueSections","decl":{"start":{"line":12,"column":9},"end":{"line":12,"column":24}},"loc":{"start":{"line":12,"column":49},"end":{"line":19,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":14,"column":17},"end":{"line":14,"column":18}},"loc":{"start":{"line":14,"column":21},"end":{"line":17,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":18,"column":40},"end":{"line":18,"column":41}},"loc":{"start":{"line":18,"column":65},"end":{"line":18,"column":null}}},"3":{"name":"Spinner","decl":{"start":{"line":23,"column":9},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":9},"end":{"line":29,"column":null}}},"4":{"name":"OutlinePane","decl":{"start":{"line":33,"column":9},"end":{"line":33,"column":21}},"loc":{"start":{"line":41,"column":1},"end":{"line":80,"column":null}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":42,"column":53},"end":{"line":42,"column":54}},"loc":{"start":{"line":42,"column":60},"end":{"line":42,"column":null}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":58,"column":26},"end":{"line":58,"column":27}},"loc":{"start":{"line":58,"column":30},"end":{"line":74,"column":null}}},"7":{"name":"(anonymous_9)","decl":{"start":{"line":63,"column":29},"end":{"line":63,"column":35}},"loc":{"start":{"line":63,"column":35},"end":{"line":63,"column":null}}},"8":{"name":"ViewerPane","decl":{"start":{"line":84,"column":9},"end":{"line":84,"column":20}},"loc":{"start":{"line":92,"column":1},"end":{"line":159,"column":null}}},"9":{"name":"(anonymous_11)","decl":{"start":{"line":96,"column":12},"end":{"line":96,"column":null}},"loc":{"start":{"line":96,"column":12},"end":{"line":98,"column":5}}},"10":{"name":"(anonymous_12)","decl":{"start":{"line":121,"column":18},"end":{"line":121,"column":19}},"loc":{"start":{"line":121,"column":26},"end":{"line":156,"column":null}}},"11":{"name":"(anonymous_13)","decl":{"start":{"line":128,"column":18},"end":{"line":128,"column":19}},"loc":{"start":{"line":128,"column":19},"end":{"line":130,"column":null}}},"12":{"name":"ChunkBrowser","decl":{"start":{"line":163,"column":9},"end":{"line":163,"column":22}},"loc":{"start":{"line":173,"column":1},"end":{"line":243,"column":null}}},"13":{"name":"(anonymous_15)","decl":{"start":{"line":191,"column":22},"end":{"line":191,"column":23}},"loc":{"start":{"line":191,"column":30},"end":{"line":213,"column":null}}},"14":{"name":"(anonymous_16)","decl":{"start":{"line":196,"column":25},"end":{"line":196,"column":31}},"loc":{"start":{"line":196,"column":31},"end":{"line":196,"column":null}}},"15":{"name":"PreviewContent","decl":{"start":{"line":247,"column":9},"end":{"line":247,"column":null}},"loc":{"start":{"line":247,"column":9},"end":{"line":405,"column":null}}},"16":{"name":"(anonymous_18)","decl":{"start":{"line":264,"column":27},"end":{"line":264,"column":null}},"loc":{"start":{"line":264,"column":27},"end":{"line":276,"column":5}}},"17":{"name":"(anonymous_19)","decl":{"start":{"line":278,"column":12},"end":{"line":278,"column":null}},"loc":{"start":{"line":278,"column":12},"end":{"line":280,"column":5}}},"18":{"name":"(anonymous_20)","decl":{"start":{"line":283,"column":12},"end":{"line":283,"column":null}},"loc":{"start":{"line":283,"column":12},"end":{"line":288,"column":5}}},"19":{"name":"(anonymous_21)","decl":{"start":{"line":304,"column":23},"end":{"line":304,"column":29}},"loc":{"start":{"line":304,"column":29},"end":{"line":304,"column":null}}},"20":{"name":"(anonymous_22)","decl":{"start":{"line":323,"column":19},"end":{"line":323,"column":25}},"loc":{"start":{"line":323,"column":25},"end":{"line":323,"column":null}}},"21":{"name":"(anonymous_23)","decl":{"start":{"line":356,"column":23},"end":{"line":356,"column":29}},"loc":{"start":{"line":356,"column":29},"end":{"line":356,"column":null}}},"22":{"name":"(anonymous_24)","decl":{"start":{"line":356,"column":44},"end":{"line":356,"column":45}},"loc":{"start":{"line":356,"column":51},"end":{"line":356,"column":null}}},"23":{"name":"(anonymous_25)","decl":{"start":{"line":367,"column":23},"end":{"line":367,"column":29}},"loc":{"start":{"line":367,"column":29},"end":{"line":367,"column":null}}},"24":{"name":"(anonymous_26)","decl":{"start":{"line":367,"column":44},"end":{"line":367,"column":45}},"loc":{"start":{"line":367,"column":51},"end":{"line":367,"column":null}}},"25":{"name":"DocumentPreviewPage","decl":{"start":{"line":409,"column":24},"end":{"line":409,"column":null}},"loc":{"start":{"line":409,"column":24},"end":{"line":415,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":16},"end":{"line":15,"column":null}},"type":"binary-expr","locations":[{"start":{"line":15,"column":16},"end":{"line":15,"column":25}},{"start":{"line":15,"column":29},"end":{"line":15,"column":null}}]},"1":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":52,"column":8},"end":{"line":56,"column":9}},"type":"cond-expr","locations":[{"start":{"line":52,"column":8},"end":{"line":56,"column":9}}]},"3":{"loc":{"start":{"line":65,"column":22},"end":{"line":67,"column":null}},"type":"cond-expr","locations":[{"start":{"line":66,"column":26},"end":{"line":66,"column":null}},{"start":{"line":67,"column":26},"end":{"line":67,"column":null}}]},"4":{"loc":{"start":{"line":70,"column":21},"end":{"line":70,"column":49}},"type":"binary-expr","locations":[{"start":{"line":70,"column":21},"end":{"line":70,"column":27}},{"start":{"line":70,"column":31},"end":{"line":70,"column":49}}]},"5":{"loc":{"start":{"line":100,"column":2},"end":{"line":117,"column":null}},"type":"if","locations":[{"start":{"line":100,"column":2},"end":{"line":117,"column":null}}]},"6":{"loc":{"start":{"line":104,"column":10},"end":{"line":108,"column":11}},"type":"cond-expr","locations":[{"start":{"line":104,"column":10},"end":{"line":108,"column":11}}]},"7":{"loc":{"start":{"line":127,"column":14},"end":{"line":131,"column":null}},"type":"cond-expr","locations":[{"start":{"line":128,"column":18},"end":{"line":130,"column":null}},{"start":{"line":131,"column":18},"end":{"line":131,"column":null}}]},"8":{"loc":{"start":{"line":134,"column":14},"end":{"line":136,"column":null}},"type":"cond-expr","locations":[{"start":{"line":135,"column":18},"end":{"line":135,"column":null}},{"start":{"line":136,"column":18},"end":{"line":136,"column":null}}]},"9":{"loc":{"start":{"line":142,"column":18},"end":{"line":142,"column":null}},"type":"cond-expr","locations":[{"start":{"line":142,"column":29},"end":{"line":142,"column":78}},{"start":{"line":142,"column":78},"end":{"line":142,"column":null}}]},"10":{"loc":{"start":{"line":145,"column":17},"end":{"line":145,"column":48}},"type":"binary-expr","locations":[{"start":{"line":145,"column":17},"end":{"line":145,"column":28}},{"start":{"line":145,"column":32},"end":{"line":145,"column":48}}]},"11":{"loc":{"start":{"line":147,"column":15},"end":{"line":147,"column":28}},"type":"binary-expr","locations":[{"start":{"line":147,"column":15},"end":{"line":147,"column":28}}]},"12":{"loc":{"start":{"line":175,"column":18},"end":{"line":175,"column":89}},"type":"binary-expr","locations":[{"start":{"line":175,"column":18},"end":{"line":175,"column":42}},{"start":{"line":175,"column":42},"end":{"line":175,"column":64}},{"start":{"line":175,"column":64},"end":{"line":175,"column":89}}]},"13":{"loc":{"start":{"line":186,"column":8},"end":{"line":190,"column":9}},"type":"cond-expr","locations":[{"start":{"line":186,"column":8},"end":{"line":190,"column":9}}]},"14":{"loc":{"start":{"line":198,"column":18},"end":{"line":200,"column":null}},"type":"cond-expr","locations":[{"start":{"line":199,"column":22},"end":{"line":199,"column":null}},{"start":{"line":200,"column":22},"end":{"line":200,"column":null}}]},"15":{"loc":{"start":{"line":204,"column":70},"end":{"line":204,"column":109}},"type":"cond-expr","locations":[{"start":{"line":204,"column":81},"end":{"line":204,"column":96}},{"start":{"line":204,"column":96},"end":{"line":204,"column":109}}]},"16":{"loc":{"start":{"line":206,"column":19},"end":{"line":206,"column":50}},"type":"binary-expr","locations":[{"start":{"line":206,"column":19},"end":{"line":206,"column":30}},{"start":{"line":206,"column":34},"end":{"line":206,"column":50}}]},"17":{"loc":{"start":{"line":208,"column":58},"end":{"line":208,"column":97}},"type":"cond-expr","locations":[{"start":{"line":208,"column":69},"end":{"line":208,"column":84}},{"start":{"line":208,"column":84},"end":{"line":208,"column":97}}]},"18":{"loc":{"start":{"line":218,"column":7},"end":{"line":218,"column":null}},"type":"binary-expr","locations":[{"start":{"line":218,"column":7},"end":{"line":218,"column":null}}]},"19":{"loc":{"start":{"line":257,"column":41},"end":{"line":257,"column":74}},"type":"binary-expr","locations":[{"start":{"line":257,"column":41},"end":{"line":257,"column":69}},{"start":{"line":257,"column":69},"end":{"line":257,"column":74}}]},"20":{"loc":{"start":{"line":265,"column":4},"end":{"line":265,"column":null}},"type":"if","locations":[{"start":{"line":265,"column":4},"end":{"line":265,"column":null}}]},"21":{"loc":{"start":{"line":272,"column":15},"end":{"line":272,"column":null}},"type":"cond-expr","locations":[{"start":{"line":272,"column":38},"end":{"line":272,"column":49}},{"start":{"line":272,"column":52},"end":{"line":272,"column":null}}]},"22":{"loc":{"start":{"line":284,"column":4},"end":{"line":287,"column":null}},"type":"if","locations":[{"start":{"line":284,"column":4},"end":{"line":287,"column":null}}]},"23":{"loc":{"start":{"line":290,"column":2},"end":{"line":290,"column":null}},"type":"if","locations":[{"start":{"line":290,"column":2},"end":{"line":290,"column":null}}]},"24":{"loc":{"start":{"line":292,"column":2},"end":{"line":313,"column":null}},"type":"if","locations":[{"start":{"line":292,"column":2},"end":{"line":313,"column":null}}]},"25":{"loc":{"start":{"line":292,"column":6},"end":{"line":292,"column":25}},"type":"binary-expr","locations":[{"start":{"line":292,"column":6},"end":{"line":292,"column":15}},{"start":{"line":292,"column":15},"end":{"line":292,"column":25}}]},"26":{"loc":{"start":{"line":297,"column":13},"end":{"line":297,"column":null}},"type":"binary-expr","locations":[{"start":{"line":297,"column":13},"end":{"line":297,"column":22}},{"start":{"line":297,"column":22},"end":{"line":297,"column":null}}]},"27":{"loc":{"start":{"line":315,"column":17},"end":{"line":315,"column":43}},"type":"binary-expr","locations":[{"start":{"line":315,"column":17},"end":{"line":315,"column":37}},{"start":{"line":315,"column":41},"end":{"line":315,"column":43}}]},"28":{"loc":{"start":{"line":344,"column":12},"end":{"line":344,"column":41}},"type":"binary-expr","locations":[{"start":{"line":344,"column":12},"end":{"line":344,"column":41}},{"start":{"line":344,"column":41},"end":{"line":344,"column":null}}]},"29":{"loc":{"start":{"line":346,"column":15},"end":{"line":346,"column":null}},"type":"cond-expr","locations":[{"start":{"line":346,"column":43},"end":{"line":346,"column":71}},{"start":{"line":346,"column":74},"end":{"line":346,"column":null}}]},"30":{"loc":{"start":{"line":347,"column":15},"end":{"line":347,"column":null}},"type":"cond-expr","locations":[{"start":{"line":347,"column":64},"end":{"line":347,"column":72}},{"start":{"line":347,"column":72},"end":{"line":347,"column":null}}]},"31":{"loc":{"start":{"line":347,"column":15},"end":{"line":347,"column":64}},"type":"binary-expr","locations":[{"start":{"line":347,"column":15},"end":{"line":347,"column":44}},{"start":{"line":347,"column":44},"end":{"line":347,"column":64}}]},"32":{"loc":{"start":{"line":348,"column":15},"end":{"line":348,"column":null}},"type":"cond-expr","locations":[{"start":{"line":348,"column":35},"end":{"line":348,"column":60}},{"start":{"line":348,"column":63},"end":{"line":348,"column":null}}]},"33":{"loc":{"start":{"line":353,"column":9},"end":{"line":353,"column":null}},"type":"binary-expr","locations":[{"start":{"line":353,"column":9},"end":{"line":353,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0},"b":{"0":[0,0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0],"6":[0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0],"12":[0,0,0],"13":[0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0],"19":[0,0],"20":[0],"21":[0,0],"22":[0],"23":[0],"24":[0],"25":[0,0],"26":[0,0],"27":[0,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0,0],"32":[0,0],"33":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/courses/[courseId]/study/page.tsx","statementMap":{"0":{"start":{"line":21,"column":24},"end":{"line":21,"column":null}},"1":{"start":{"line":3,"column":58},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":43},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":27},"end":{"line":5,"column":null}},"4":{"start":{"line":7,"column":70},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":36},"end":{"line":8,"column":null}},"6":{"start":{"line":14,"column":7},"end":{"line":14,"column":null}},"7":{"start":{"line":15,"column":26},"end":{"line":15,"column":null}},"8":{"start":{"line":16,"column":27},"end":{"line":16,"column":null}},"9":{"start":{"line":17,"column":27},"end":{"line":17,"column":null}},"10":{"start":{"line":18,"column":29},"end":{"line":18,"column":null}},"11":{"start":{"line":19,"column":30},"end":{"line":19,"column":null}},"12":{"start":{"line":22,"column":17},"end":{"line":22,"column":null}},"13":{"start":{"line":23,"column":23},"end":{"line":23,"column":null}},"14":{"start":{"line":24,"column":19},"end":{"line":24,"column":34}},"15":{"start":{"line":25,"column":28},"end":{"line":25,"column":null}},"16":{"start":{"line":26,"column":22},"end":{"line":26,"column":null}},"17":{"start":{"line":28,"column":23},"end":{"line":28,"column":null}},"18":{"start":{"line":29,"column":24},"end":{"line":29,"column":null}},"19":{"start":{"line":31,"column":30},"end":{"line":31,"column":null}},"20":{"start":{"line":32,"column":32},"end":{"line":32,"column":null}},"21":{"start":{"line":33,"column":46},"end":{"line":33,"column":null}},"22":{"start":{"line":34,"column":50},"end":{"line":34,"column":null}},"23":{"start":{"line":35,"column":46},"end":{"line":35,"column":null}},"24":{"start":{"line":36,"column":36},"end":{"line":36,"column":null}},"25":{"start":{"line":37,"column":32},"end":{"line":37,"column":null}},"26":{"start":{"line":38,"column":46},"end":{"line":38,"column":null}},"27":{"start":{"line":39,"column":50},"end":{"line":42,"column":null}},"28":{"start":{"line":44,"column":31},"end":{"line":49,"column":null}},"29":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"30":{"start":{"line":45,"column":27},"end":{"line":45,"column":null}},"31":{"start":{"line":46,"column":4},"end":{"line":47,"column":null}},"32":{"start":{"line":47,"column":51},"end":{"line":47,"column":59}},"33":{"start":{"line":51,"column":30},"end":{"line":51,"column":null}},"34":{"start":{"line":53,"column":29},"end":{"line":55,"column":null}},"35":{"start":{"line":54,"column":4},"end":{"line":54,"column":null}},"36":{"start":{"line":57,"column":2},"end":{"line":93,"column":null}},"37":{"start":{"line":59,"column":6},"end":{"line":73,"column":null}},"38":{"start":{"line":60,"column":48},"end":{"line":64,"column":null}},"39":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"40":{"start":{"line":66,"column":8},"end":{"line":66,"column":null}},"41":{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},"42":{"start":{"line":67,"column":36},"end":{"line":67,"column":null}},"43":{"start":{"line":68,"column":8},"end":{"line":68,"column":null}},"44":{"start":{"line":68,"column":43},"end":{"line":68,"column":null}},"45":{"start":{"line":72,"column":8},"end":{"line":72,"column":null}},"46":{"start":{"line":77,"column":6},"end":{"line":77,"column":null}},"47":{"start":{"line":77,"column":24},"end":{"line":77,"column":null}},"48":{"start":{"line":78,"column":6},"end":{"line":86,"column":null}},"49":{"start":{"line":79,"column":18},"end":{"line":79,"column":null}},"50":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"51":{"start":{"line":81,"column":8},"end":{"line":83,"column":null}},"52":{"start":{"line":82,"column":10},"end":{"line":82,"column":null}},"53":{"start":{"line":89,"column":4},"end":{"line":92,"column":null}},"54":{"start":{"line":90,"column":6},"end":{"line":90,"column":null}},"55":{"start":{"line":91,"column":6},"end":{"line":91,"column":null}},"56":{"start":{"line":95,"column":29},"end":{"line":111,"column":null}},"57":{"start":{"line":97,"column":6},"end":{"line":97,"column":null}},"58":{"start":{"line":97,"column":24},"end":{"line":97,"column":null}},"59":{"start":{"line":98,"column":23},"end":{"line":98,"column":null}},"60":{"start":{"line":99,"column":6},"end":{"line":109,"column":null}},"61":{"start":{"line":100,"column":23},"end":{"line":100,"column":null}},"62":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"63":{"start":{"line":101,"column":38},"end":{"line":101,"column":null}},"64":{"start":{"line":102,"column":8},"end":{"line":102,"column":null}},"65":{"start":{"line":103,"column":8},"end":{"line":106,"column":null}},"66":{"start":{"line":104,"column":10},"end":{"line":104,"column":null}},"67":{"start":{"line":105,"column":10},"end":{"line":105,"column":null}},"68":{"start":{"line":105,"column":27},"end":{"line":105,"column":45}},"69":{"start":{"line":114,"column":2},"end":{"line":123,"column":null}},"70":{"start":{"line":153,"column":14},"end":{"line":153,"column":43}}},"fnMap":{"0":{"name":"StudyPage","decl":{"start":{"line":21,"column":24},"end":{"line":21,"column":null}},"loc":{"start":{"line":21,"column":24},"end":{"line":194,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":44,"column":39},"end":{"line":44,"column":null}},"loc":{"start":{"line":44,"column":39},"end":{"line":49,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":47,"column":44},"end":{"line":47,"column":45}},"loc":{"start":{"line":47,"column":51},"end":{"line":47,"column":59}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":53,"column":41},"end":{"line":53,"column":42}},"loc":{"start":{"line":53,"column":68},"end":{"line":55,"column":5}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":57,"column":12},"end":{"line":57,"column":null}},"loc":{"start":{"line":57,"column":12},"end":{"line":93,"column":5}}},"5":{"name":"load","decl":{"start":{"line":58,"column":19},"end":{"line":58,"column":null}},"loc":{"start":{"line":58,"column":19},"end":{"line":74,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":68,"column":36},"end":{"line":68,"column":37}},"loc":{"start":{"line":68,"column":43},"end":{"line":68,"column":null}}},"7":{"name":"loadProgress","decl":{"start":{"line":76,"column":19},"end":{"line":76,"column":null}},"loc":{"start":{"line":76,"column":19},"end":{"line":87,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":96,"column":4},"end":{"line":96,"column":11}},"loc":{"start":{"line":96,"column":11},"end":{"line":110,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":101,"column":28},"end":{"line":101,"column":29}},"loc":{"start":{"line":101,"column":38},"end":{"line":101,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":105,"column":21},"end":{"line":105,"column":27}},"loc":{"start":{"line":105,"column":27},"end":{"line":105,"column":45}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":152,"column":27},"end":{"line":152,"column":28}},"loc":{"start":{"line":153,"column":14},"end":{"line":153,"column":43}}}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":23},"end":{"line":28,"column":null}},"type":"binary-expr","locations":[{"start":{"line":28,"column":23},"end":{"line":28,"column":48}},{"start":{"line":28,"column":48},"end":{"line":28,"column":null}}]},"1":{"loc":{"start":{"line":29,"column":24},"end":{"line":29,"column":null}},"type":"binary-expr","locations":[{"start":{"line":29,"column":24},"end":{"line":29,"column":71}},{"start":{"line":29,"column":71},"end":{"line":29,"column":null}}]},"2":{"loc":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":45,"column":null}}]},"3":{"loc":{"start":{"line":51,"column":30},"end":{"line":51,"column":null}},"type":"cond-expr","locations":[{"start":{"line":51,"column":57},"end":{"line":51,"column":94}},{"start":{"line":51,"column":94},"end":{"line":51,"column":null}}]},"4":{"loc":{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":8},"end":{"line":67,"column":null}}]},"5":{"loc":{"start":{"line":77,"column":6},"end":{"line":77,"column":null}},"type":"if","locations":[{"start":{"line":77,"column":6},"end":{"line":77,"column":null}}]},"6":{"loc":{"start":{"line":81,"column":8},"end":{"line":83,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":8},"end":{"line":83,"column":null}}]},"7":{"loc":{"start":{"line":89,"column":4},"end":{"line":92,"column":null}},"type":"if","locations":[{"start":{"line":89,"column":4},"end":{"line":92,"column":null}}]},"8":{"loc":{"start":{"line":97,"column":6},"end":{"line":97,"column":null}},"type":"if","locations":[{"start":{"line":97,"column":6},"end":{"line":97,"column":null}}]},"9":{"loc":{"start":{"line":103,"column":8},"end":{"line":106,"column":null}},"type":"if","locations":[{"start":{"line":103,"column":8},"end":{"line":106,"column":null}}]},"10":{"loc":{"start":{"line":114,"column":2},"end":{"line":123,"column":null}},"type":"if","locations":[{"start":{"line":114,"column":2},"end":{"line":123,"column":null}}]},"11":{"loc":{"start":{"line":128,"column":7},"end":{"line":128,"column":null}},"type":"binary-expr","locations":[{"start":{"line":128,"column":7},"end":{"line":128,"column":null}}]},"12":{"loc":{"start":{"line":146,"column":7},"end":{"line":146,"column":null}},"type":"binary-expr","locations":[{"start":{"line":146,"column":7},"end":{"line":146,"column":null}}]},"13":{"loc":{"start":{"line":149,"column":13},"end":{"line":149,"column":null}},"type":"cond-expr","locations":[{"start":{"line":149,"column":38},"end":{"line":149,"column":55}},{"start":{"line":149,"column":55},"end":{"line":149,"column":null}}]}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":69,"13":69,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0},"f":{"0":69,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0,0],"4":[0],"5":[0],"6":[0],"7":[0],"8":[0],"9":[0],"10":[0],"11":[0],"12":[0],"13":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/dashboard/error.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/dashboard/error.tsx","statementMap":{"0":{"start":{"line":5,"column":24},"end":{"line":5,"column":39}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":12,"column":2},"end":{"line":14,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}}},"fnMap":{"0":{"name":"DashboardError","decl":{"start":{"line":5,"column":24},"end":{"line":5,"column":39}},"loc":{"start":{"line":11,"column":1},"end":{"line":29,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":12},"end":{"line":12,"column":null}},"loc":{"start":{"line":12,"column":12},"end":{"line":14,"column":5}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0,"1":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/app/dashboard/page.tsx","statementMap":{"0":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":26},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":59},"end":{"line":7,"column":null}},"6":{"start":{"line":10,"column":36},"end":{"line":10,"column":null}},"7":{"start":{"line":11,"column":17},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":32},"end":{"line":12,"column":null}},"9":{"start":{"line":13,"column":46},"end":{"line":13,"column":null}},"10":{"start":{"line":15,"column":2},"end":{"line":30,"column":null}},"11":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"12":{"start":{"line":16,"column":36},"end":{"line":16,"column":null}},"13":{"start":{"line":19,"column":6},"end":{"line":26,"column":null}},"14":{"start":{"line":20,"column":21},"end":{"line":20,"column":null}},"15":{"start":{"line":21,"column":8},"end":{"line":21,"column":null}},"16":{"start":{"line":25,"column":8},"end":{"line":25,"column":null}},"17":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"18":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"19":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"20":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"21":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"22":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}},"23":{"start":{"line":49,"column":4},"end":{"line":49,"column":null}}},"fnMap":{"0":{"name":"DashboardPage","decl":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":24},"end":{"line":141,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"loc":{"start":{"line":15,"column":12},"end":{"line":30,"column":5}}},"2":{"name":"fetchCourses","decl":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"loc":{"start":{"line":18,"column":19},"end":{"line":27,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":24},"end":{"line":48,"column":null}},"loc":{"start":{"line":48,"column":24},"end":{"line":50,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":32,"column":2},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":2},"end":{"line":41,"column":null}}]},"2":{"loc":{"start":{"line":43,"column":2},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":43,"column":2},"end":{"line":46,"column":null}}]},"3":{"loc":{"start":{"line":72,"column":22},"end":{"line":72,"column":73}},"type":"binary-expr","locations":[{"start":{"line":72,"column":22},"end":{"line":72,"column":52}},{"start":{"line":72,"column":52},"end":{"line":72,"column":73}}]},"4":{"loc":{"start":{"line":87,"column":21},"end":{"line":87,"column":null}},"type":"binary-expr","locations":[{"start":{"line":87,"column":21},"end":{"line":87,"column":51}},{"start":{"line":87,"column":51},"end":{"line":87,"column":null}}]},"5":{"loc":{"start":{"line":93,"column":21},"end":{"line":93,"column":null}},"type":"binary-expr","locations":[{"start":{"line":93,"column":21},"end":{"line":93,"column":44}},{"start":{"line":93,"column":44},"end":{"line":93,"column":null}}]},"6":{"loc":{"start":{"line":106,"column":21},"end":{"line":109,"column":36}},"type":"cond-expr","locations":[{"start":{"line":107,"column":22},"end":{"line":109,"column":30}},{"start":{"line":109,"column":22},"end":{"line":109,"column":36}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/CourseCard.tsx","statementMap":{"0":{"start":{"line":29,"column":16},"end":{"line":29,"column":27}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":30,"column":17},"end":{"line":30,"column":null}},"3":{"start":{"line":31,"column":21},"end":{"line":31,"column":null}},"4":{"start":{"line":33,"column":4},"end":{"line":39,"column":null}}},"fnMap":{"0":{"name":"Mini","decl":{"start":{"line":18,"column":9},"end":{"line":18,"column":14}},"loc":{"start":{"line":18,"column":70},"end":{"line":27,"column":null}}},"1":{"name":"CourseCard","decl":{"start":{"line":29,"column":16},"end":{"line":29,"column":27}},"loc":{"start":{"line":29,"column":68},"end":{"line":97,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":55},"end":{"line":21,"column":null}},"type":"cond-expr","locations":[{"start":{"line":21,"column":62},"end":{"line":21,"column":85}},{"start":{"line":21,"column":85},"end":{"line":21,"column":null}}]},"1":{"loc":{"start":{"line":29,"column":37},"end":{"line":29,"column":49}},"type":"default-arg","locations":[{"start":{"line":29,"column":45},"end":{"line":29,"column":49}}]},"2":{"loc":{"start":{"line":30,"column":17},"end":{"line":30,"column":null}},"type":"binary-expr","locations":[{"start":{"line":30,"column":17},"end":{"line":30,"column":33}},{"start":{"line":30,"column":33},"end":{"line":30,"column":null}}]},"3":{"loc":{"start":{"line":31,"column":21},"end":{"line":31,"column":null}},"type":"binary-expr","locations":[{"start":{"line":31,"column":21},"end":{"line":31,"column":42}},{"start":{"line":31,"column":42},"end":{"line":31,"column":null}}]},"4":{"loc":{"start":{"line":33,"column":4},"end":{"line":39,"column":null}},"type":"cond-expr","locations":[{"start":{"line":34,"column":8},"end":{"line":34,"column":null}},{"start":{"line":35,"column":8},"end":{"line":39,"column":null}}]},"5":{"loc":{"start":{"line":35,"column":8},"end":{"line":39,"column":null}},"type":"cond-expr","locations":[{"start":{"line":36,"column":10},"end":{"line":36,"column":null}},{"start":{"line":37,"column":10},"end":{"line":39,"column":null}}]},"6":{"loc":{"start":{"line":37,"column":10},"end":{"line":39,"column":null}},"type":"cond-expr","locations":[{"start":{"line":38,"column":12},"end":{"line":38,"column":null}},{"start":{"line":39,"column":12},"end":{"line":39,"column":null}}]},"7":{"loc":{"start":{"line":49,"column":11},"end":{"line":49,"column":81}},"type":"cond-expr","locations":[{"start":{"line":49,"column":32},"end":{"line":49,"column":66}},{"start":{"line":49,"column":66},"end":{"line":49,"column":81}}]},"8":{"loc":{"start":{"line":70,"column":7},"end":{"line":70,"column":25}},"type":"binary-expr","locations":[{"start":{"line":70,"column":7},"end":{"line":70,"column":25}}]},"9":{"loc":{"start":{"line":77,"column":7},"end":{"line":77,"column":null}},"type":"binary-expr","locations":[{"start":{"line":77,"column":7},"end":{"line":77,"column":null}}]},"10":{"loc":{"start":{"line":81,"column":55},"end":{"line":81,"column":null}},"type":"binary-expr","locations":[{"start":{"line":81,"column":55},"end":{"line":81,"column":69}},{"start":{"line":81,"column":69},"end":{"line":81,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0],"9":[0],"10":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/CreateCourseModal.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/CreateCourseModal.tsx","statementMap":{"0":{"start":{"line":10,"column":16},"end":{"line":10,"column":34}},"1":{"start":{"line":3,"column":44},"end":{"line":3,"column":null}},"2":{"start":{"line":11,"column":28},"end":{"line":11,"column":null}},"3":{"start":{"line":12,"column":40},"end":{"line":12,"column":null}},"4":{"start":{"line":13,"column":30},"end":{"line":13,"column":null}},"5":{"start":{"line":14,"column":24},"end":{"line":14,"column":null}},"6":{"start":{"line":15,"column":19},"end":{"line":15,"column":null}},"7":{"start":{"line":17,"column":2},"end":{"line":24,"column":null}},"8":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"9":{"start":{"line":20,"column":6},"end":{"line":20,"column":null}},"10":{"start":{"line":20,"column":30},"end":{"line":20,"column":null}},"11":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"12":{"start":{"line":23,"column":4},"end":{"line":23,"column":null}},"13":{"start":{"line":23,"column":17},"end":{"line":23,"column":null}},"14":{"start":{"line":27,"column":4},"end":{"line":30,"column":null}},"15":{"start":{"line":28,"column":6},"end":{"line":28,"column":null}},"16":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"17":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"18":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"19":{"start":{"line":33,"column":4},"end":{"line":38,"column":null}},"20":{"start":{"line":34,"column":6},"end":{"line":34,"column":null}},"21":{"start":{"line":36,"column":6},"end":{"line":36,"column":null}},"22":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"23":{"start":{"line":49,"column":24},"end":{"line":49,"column":null}},"24":{"start":{"line":83,"column":16},"end":{"line":83,"column":null}},"25":{"start":{"line":84,"column":16},"end":{"line":84,"column":null}},"26":{"start":{"line":87,"column":16},"end":{"line":87,"column":null}},"27":{"start":{"line":87,"column":50},"end":{"line":87,"column":null}},"28":{"start":{"line":102,"column":31},"end":{"line":102,"column":null}}},"fnMap":{"0":{"name":"CreateCourseModal","decl":{"start":{"line":10,"column":16},"end":{"line":10,"column":34}},"loc":{"start":{"line":10,"column":79},"end":{"line":129,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":12},"end":{"line":17,"column":null}},"loc":{"start":{"line":17,"column":12},"end":{"line":24,"column":5}}},"2":{"name":"onKey","decl":{"start":{"line":19,"column":13},"end":{"line":19,"column":19}},"loc":{"start":{"line":19,"column":35},"end":{"line":21,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":23,"column":11},"end":{"line":23,"column":17}},"loc":{"start":{"line":23,"column":17},"end":{"line":23,"column":null}}},"4":{"name":"handleCreate","decl":{"start":{"line":26,"column":17},"end":{"line":26,"column":null}},"loc":{"start":{"line":26,"column":17},"end":{"line":39,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":49,"column":17},"end":{"line":49,"column":18}},"loc":{"start":{"line":49,"column":24},"end":{"line":49,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":82,"column":24},"end":{"line":82,"column":25}},"loc":{"start":{"line":82,"column":25},"end":{"line":85,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":86,"column":25},"end":{"line":86,"column":26}},"loc":{"start":{"line":86,"column":26},"end":{"line":88,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":102,"column":24},"end":{"line":102,"column":25}},"loc":{"start":{"line":102,"column":31},"end":{"line":102,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":6},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":6},"end":{"line":20,"column":null}}]},"1":{"loc":{"start":{"line":27,"column":4},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":30,"column":null}}]},"2":{"loc":{"start":{"line":34,"column":35},"end":{"line":34,"column":null}},"type":"binary-expr","locations":[{"start":{"line":34,"column":35},"end":{"line":34,"column":57}},{"start":{"line":34,"column":57},"end":{"line":34,"column":null}}]},"3":{"loc":{"start":{"line":36,"column":13},"end":{"line":36,"column":null}},"type":"cond-expr","locations":[{"start":{"line":36,"column":34},"end":{"line":36,"column":43}},{"start":{"line":36,"column":46},"end":{"line":36,"column":null}}]},"4":{"loc":{"start":{"line":87,"column":16},"end":{"line":87,"column":null}},"type":"if","locations":[{"start":{"line":87,"column":16},"end":{"line":87,"column":null}}]},"5":{"loc":{"start":{"line":87,"column":20},"end":{"line":87,"column":50}},"type":"binary-expr","locations":[{"start":{"line":87,"column":20},"end":{"line":87,"column":41}},{"start":{"line":87,"column":41},"end":{"line":87,"column":50}}]},"6":{"loc":{"start":{"line":109,"column":9},"end":{"line":109,"column":null}},"type":"binary-expr","locations":[{"start":{"line":109,"column":9},"end":{"line":109,"column":null}}]},"7":{"loc":{"start":{"line":122,"column":75},"end":{"line":122,"column":null}},"type":"binary-expr","locations":[{"start":{"line":122,"column":75},"end":{"line":122,"column":85}},{"start":{"line":122,"column":85},"end":{"line":122,"column":null}}]},"8":{"loc":{"start":{"line":123,"column":13},"end":{"line":123,"column":null}},"type":"cond-expr","locations":[{"start":{"line":123,"column":22},"end":{"line":123,"column":36}},{"start":{"line":123,"column":36},"end":{"line":123,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0,0],"4":[0],"5":[0,0],"6":[0],"7":[0,0],"8":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/courses/ProcessingIndicator.tsx","statementMap":{"0":{"start":{"line":52,"column":16},"end":{"line":52,"column":36}},"1":{"start":{"line":14,"column":4},"end":{"line":39,"column":null}},"2":{"start":{"line":41,"column":54},"end":{"line":50,"column":null}},"3":{"start":{"line":53,"column":17},"end":{"line":53,"column":38}},"4":{"start":{"line":54,"column":21},"end":{"line":54,"column":null}}},"fnMap":{"0":{"name":"ProcessingIndicator","decl":{"start":{"line":52,"column":16},"end":{"line":52,"column":36}},"loc":{"start":{"line":52,"column":95},"end":{"line":65,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":52,"column":53},"end":{"line":52,"column":67}},"type":"default-arg","locations":[{"start":{"line":52,"column":65},"end":{"line":52,"column":67}}]},"1":{"loc":{"start":{"line":54,"column":21},"end":{"line":54,"column":null}},"type":"cond-expr","locations":[{"start":{"line":54,"column":70},"end":{"line":54,"column":89}},{"start":{"line":54,"column":92},"end":{"line":54,"column":null}}]},"2":{"loc":{"start":{"line":54,"column":21},"end":{"line":54,"column":70}},"type":"binary-expr","locations":[{"start":{"line":54,"column":21},"end":{"line":54,"column":30}},{"start":{"line":54,"column":30},"end":{"line":54,"column":50}},{"start":{"line":54,"column":50},"end":{"line":54,"column":70}}]},"3":{"loc":{"start":{"line":62,"column":7},"end":{"line":62,"column":21}},"type":"binary-expr","locations":[{"start":{"line":62,"column":7},"end":{"line":62,"column":21}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{"0":[0],"1":[0,0],"2":[0,0,0],"3":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentProcessingPanel.tsx","statementMap":{"0":{"start":{"line":24,"column":16},"end":{"line":24,"column":40}},"1":{"start":{"line":3,"column":49},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":38},"end":{"line":4,"column":null}},"3":{"start":{"line":29,"column":30},"end":{"line":29,"column":null}},"4":{"start":{"line":30,"column":32},"end":{"line":30,"column":null}},"5":{"start":{"line":31,"column":28},"end":{"line":31,"column":null}},"6":{"start":{"line":33,"column":15},"end":{"line":43,"column":null}},"7":{"start":{"line":34,"column":4},"end":{"line":42,"column":null}},"8":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"9":{"start":{"line":36,"column":19},"end":{"line":36,"column":null}},"10":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"11":{"start":{"line":39,"column":6},"end":{"line":39,"column":null}},"12":{"start":{"line":41,"column":6},"end":{"line":41,"column":null}},"13":{"start":{"line":45,"column":2},"end":{"line":47,"column":null}},"14":{"start":{"line":46,"column":4},"end":{"line":46,"column":null}},"15":{"start":{"line":49,"column":2},"end":{"line":57,"column":null}},"16":{"start":{"line":59,"column":2},"end":{"line":68,"column":null}},"17":{"start":{"line":70,"column":2},"end":{"line":70,"column":null}},"18":{"start":{"line":70,"column":15},"end":{"line":70,"column":null}},"19":{"start":{"line":99,"column":25},"end":{"line":99,"column":null}}},"fnMap":{"0":{"name":"DetailRow","decl":{"start":{"line":13,"column":9},"end":{"line":13,"column":19}},"loc":{"start":{"line":13,"column":78},"end":{"line":22,"column":null}}},"1":{"name":"DocumentProcessingPanel","decl":{"start":{"line":24,"column":16},"end":{"line":24,"column":40}},"loc":{"start":{"line":28,"column":31},"end":{"line":120,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":33,"column":27},"end":{"line":33,"column":null}},"loc":{"start":{"line":33,"column":27},"end":{"line":43,"column":5}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":45,"column":12},"end":{"line":45,"column":null}},"loc":{"start":{"line":45,"column":12},"end":{"line":47,"column":5}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":99,"column":19},"end":{"line":99,"column":25}},"loc":{"start":{"line":99,"column":25},"end":{"line":99,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":9},"end":{"line":18,"column":18}},"type":"binary-expr","locations":[{"start":{"line":18,"column":9},"end":{"line":18,"column":18}}]},"1":{"loc":{"start":{"line":39,"column":15},"end":{"line":39,"column":null}},"type":"cond-expr","locations":[{"start":{"line":39,"column":38},"end":{"line":39,"column":49}},{"start":{"line":39,"column":52},"end":{"line":39,"column":null}}]},"2":{"loc":{"start":{"line":49,"column":2},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":57,"column":null}}]},"3":{"loc":{"start":{"line":59,"column":2},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":59,"column":2},"end":{"line":68,"column":null}}]},"4":{"loc":{"start":{"line":70,"column":2},"end":{"line":70,"column":null}},"type":"if","locations":[{"start":{"line":70,"column":2},"end":{"line":70,"column":null}}]},"5":{"loc":{"start":{"line":81,"column":9},"end":{"line":81,"column":28}},"type":"binary-expr","locations":[{"start":{"line":81,"column":9},"end":{"line":81,"column":28}}]},"6":{"loc":{"start":{"line":87,"column":9},"end":{"line":87,"column":34}},"type":"binary-expr","locations":[{"start":{"line":87,"column":9},"end":{"line":87,"column":34}}]},"7":{"loc":{"start":{"line":97,"column":7},"end":{"line":97,"column":24}},"type":"binary-expr","locations":[{"start":{"line":97,"column":7},"end":{"line":97,"column":24}},{"start":{"line":97,"column":24},"end":{"line":97,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0],"7":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentRow.tsx","statementMap":{"0":{"start":{"line":27,"column":16},"end":{"line":27,"column":28}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":37},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":31},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":36},"end":{"line":7,"column":null}},"5":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"6":{"start":{"line":16,"column":20},"end":{"line":16,"column":null}},"7":{"start":{"line":17,"column":2},"end":{"line":17,"column":null}},"8":{"start":{"line":17,"column":27},"end":{"line":17,"column":null}},"9":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"10":{"start":{"line":21,"column":79},"end":{"line":25,"column":null}},"11":{"start":{"line":28,"column":34},"end":{"line":28,"column":null}},"12":{"start":{"line":29,"column":17},"end":{"line":29,"column":null}},"13":{"start":{"line":30,"column":17},"end":{"line":30,"column":null}},"14":{"start":{"line":31,"column":19},"end":{"line":31,"column":34}},"15":{"start":{"line":33,"column":20},"end":{"line":33,"column":70}},"16":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"17":{"start":{"line":36,"column":47},"end":{"line":36,"column":null}},"18":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"19":{"start":{"line":38,"column":4},"end":{"line":43,"column":null}},"20":{"start":{"line":39,"column":6},"end":{"line":39,"column":null}},"21":{"start":{"line":40,"column":6},"end":{"line":40,"column":null}},"22":{"start":{"line":42,"column":6},"end":{"line":42,"column":null}},"23":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"formatFileSize","decl":{"start":{"line":15,"column":9},"end":{"line":15,"column":24}},"loc":{"start":{"line":15,"column":37},"end":{"line":19,"column":null}}},"1":{"name":"DocumentRow","decl":{"start":{"line":27,"column":16},"end":{"line":27,"column":28}},"loc":{"start":{"line":27,"column":87},"end":{"line":127,"column":null}}},"2":{"name":"handleDelete","decl":{"start":{"line":35,"column":17},"end":{"line":35,"column":null}},"loc":{"start":{"line":35,"column":17},"end":{"line":44,"column":null}}},"3":{"name":"handlePreview","decl":{"start":{"line":46,"column":11},"end":{"line":46,"column":null}},"loc":{"start":{"line":46,"column":11},"end":{"line":48,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":2},"end":{"line":16,"column":null}}]},"1":{"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":2},"end":{"line":17,"column":null}}]},"2":{"loc":{"start":{"line":33,"column":20},"end":{"line":33,"column":70}},"type":"binary-expr","locations":[{"start":{"line":33,"column":20},"end":{"line":33,"column":48}},{"start":{"line":33,"column":52},"end":{"line":33,"column":70}}]},"3":{"loc":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0,0],"3":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/documents/DocumentUpload.tsx","statementMap":{"0":{"start":{"line":365,"column":16},"end":{"line":365,"column":31}},"1":{"start":{"line":3,"column":46},"end":{"line":3,"column":null}},"2":{"start":{"line":8,"column":7},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":25},"end":{"line":9,"column":null}},"4":{"start":{"line":11,"column":25},"end":{"line":11,"column":null}},"5":{"start":{"line":15,"column":23},"end":{"line":15,"column":null}},"6":{"start":{"line":16,"column":21},"end":{"line":16,"column":null}},"7":{"start":{"line":17,"column":27},"end":{"line":20,"column":null}},"8":{"start":{"line":21,"column":23},"end":{"line":21,"column":null}},"9":{"start":{"line":23,"column":59},"end":{"line":27,"column":null}},"10":{"start":{"line":29,"column":63},"end":{"line":36,"column":null}},"11":{"start":{"line":65,"column":14},"end":{"line":65,"column":null}},"12":{"start":{"line":66,"column":2},"end":{"line":66,"column":null}},"13":{"start":{"line":66,"column":35},"end":{"line":66,"column":null}},"14":{"start":{"line":68,"column":2},"end":{"line":68,"column":null}},"15":{"start":{"line":72,"column":2},"end":{"line":72,"column":null}},"16":{"start":{"line":72,"column":34},"end":{"line":72,"column":null}},"17":{"start":{"line":73,"column":2},"end":{"line":73,"column":null}},"18":{"start":{"line":88,"column":16},"end":{"line":89,"column":null}},"19":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}},"20":{"start":{"line":89,"column":22},"end":{"line":89,"column":null}},"21":{"start":{"line":89,"column":39},"end":{"line":89,"column":null}},"22":{"start":{"line":91,"column":2},"end":{"line":130,"column":null}},"23":{"start":{"line":92,"column":16},"end":{"line":93,"column":null}},"24":{"start":{"line":93,"column":6},"end":{"line":93,"column":null}},"25":{"start":{"line":95,"column":4},"end":{"line":95,"column":null}},"26":{"start":{"line":97,"column":4},"end":{"line":119,"column":null}},"27":{"start":{"line":97,"column":17},"end":{"line":97,"column":20}},"28":{"start":{"line":98,"column":6},"end":{"line":117,"column":null}},"29":{"start":{"line":99,"column":18},"end":{"line":99,"column":null}},"30":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"31":{"start":{"line":101,"column":8},"end":{"line":105,"column":null}},"32":{"start":{"line":102,"column":10},"end":{"line":102,"column":null}},"33":{"start":{"line":103,"column":10},"end":{"line":103,"column":null}},"34":{"start":{"line":104,"column":10},"end":{"line":104,"column":null}},"35":{"start":{"line":106,"column":8},"end":{"line":114,"column":null}},"36":{"start":{"line":107,"column":10},"end":{"line":111,"column":null}},"37":{"start":{"line":112,"column":10},"end":{"line":112,"column":null}},"38":{"start":{"line":113,"column":10},"end":{"line":113,"column":null}},"39":{"start":{"line":116,"column":8},"end":{"line":116,"column":null}},"40":{"start":{"line":116,"column":61},"end":{"line":116,"column":null}},"41":{"start":{"line":118,"column":6},"end":{"line":118,"column":null}},"42":{"start":{"line":118,"column":31},"end":{"line":118,"column":null}},"43":{"start":{"line":120,"column":4},"end":{"line":120,"column":null}},"44":{"start":{"line":121,"column":4},"end":{"line":121,"column":null}},"45":{"start":{"line":123,"column":4},"end":{"line":127,"column":null}},"46":{"start":{"line":129,"column":4},"end":{"line":129,"column":null}},"47":{"start":{"line":140,"column":2},"end":{"line":151,"column":null}},"48":{"start":{"line":141,"column":4},"end":{"line":151,"column":null}},"49":{"start":{"line":142,"column":6},"end":{"line":151,"column":null}},"50":{"start":{"line":155,"column":16},"end":{"line":156,"column":null}},"51":{"start":{"line":156,"column":4},"end":{"line":156,"column":null}},"52":{"start":{"line":156,"column":22},"end":{"line":156,"column":null}},"53":{"start":{"line":156,"column":39},"end":{"line":156,"column":null}},"54":{"start":{"line":158,"column":2},"end":{"line":193,"column":null}},"55":{"start":{"line":159,"column":4},"end":{"line":159,"column":null}},"56":{"start":{"line":161,"column":4},"end":{"line":183,"column":null}},"57":{"start":{"line":161,"column":17},"end":{"line":161,"column":20}},"58":{"start":{"line":162,"column":6},"end":{"line":181,"column":null}},"59":{"start":{"line":163,"column":18},"end":{"line":163,"column":null}},"60":{"start":{"line":164,"column":8},"end":{"line":164,"column":null}},"61":{"start":{"line":165,"column":8},"end":{"line":169,"column":null}},"62":{"start":{"line":166,"column":10},"end":{"line":166,"column":null}},"63":{"start":{"line":167,"column":10},"end":{"line":167,"column":null}},"64":{"start":{"line":168,"column":10},"end":{"line":168,"column":null}},"65":{"start":{"line":170,"column":8},"end":{"line":178,"column":null}},"66":{"start":{"line":171,"column":10},"end":{"line":175,"column":null}},"67":{"start":{"line":176,"column":10},"end":{"line":176,"column":null}},"68":{"start":{"line":177,"column":10},"end":{"line":177,"column":null}},"69":{"start":{"line":180,"column":8},"end":{"line":180,"column":null}},"70":{"start":{"line":180,"column":61},"end":{"line":180,"column":null}},"71":{"start":{"line":182,"column":6},"end":{"line":182,"column":null}},"72":{"start":{"line":182,"column":31},"end":{"line":182,"column":null}},"73":{"start":{"line":184,"column":4},"end":{"line":184,"column":null}},"74":{"start":{"line":185,"column":4},"end":{"line":185,"column":null}},"75":{"start":{"line":187,"column":4},"end":{"line":191,"column":null}},"76":{"start":{"line":192,"column":4},"end":{"line":192,"column":null}},"77":{"start":{"line":207,"column":21},"end":{"line":207,"column":null}},"78":{"start":{"line":208,"column":19},"end":{"line":208,"column":null}},"79":{"start":{"line":209,"column":19},"end":{"line":209,"column":null}},"80":{"start":{"line":212,"column":4},"end":{"line":222,"column":null}},"81":{"start":{"line":366,"column":24},"end":{"line":366,"column":null}},"82":{"start":{"line":367,"column":26},"end":{"line":367,"column":null}},"83":{"start":{"line":368,"column":38},"end":{"line":368,"column":null}},"84":{"start":{"line":369,"column":42},"end":{"line":369,"column":null}},"85":{"start":{"line":370,"column":19},"end":{"line":370,"column":null}},"86":{"start":{"line":371,"column":20},"end":{"line":371,"column":null}},"87":{"start":{"line":372,"column":21},"end":{"line":372,"column":null}},"88":{"start":{"line":374,"column":18},"end":{"line":381,"column":null}},"89":{"start":{"line":375,"column":4},"end":{"line":375,"column":null}},"90":{"start":{"line":376,"column":17},"end":{"line":376,"column":null}},"91":{"start":{"line":377,"column":4},"end":{"line":380,"column":null}},"92":{"start":{"line":378,"column":6},"end":{"line":378,"column":null}},"93":{"start":{"line":379,"column":6},"end":{"line":379,"column":null}},"94":{"start":{"line":383,"column":18},"end":{"line":390,"column":null}},"95":{"start":{"line":384,"column":4},"end":{"line":389,"column":null}},"96":{"start":{"line":385,"column":6},"end":{"line":385,"column":null}},"97":{"start":{"line":386,"column":6},"end":{"line":386,"column":null}},"98":{"start":{"line":388,"column":6},"end":{"line":388,"column":null}},"99":{"start":{"line":392,"column":22},"end":{"line":442,"column":null}},"100":{"start":{"line":394,"column":24},"end":{"line":394,"column":null}},"101":{"start":{"line":395,"column":6},"end":{"line":395,"column":null}},"102":{"start":{"line":395,"column":34},"end":{"line":395,"column":null}},"103":{"start":{"line":398,"column":31},"end":{"line":398,"column":33}},"104":{"start":{"line":399,"column":6},"end":{"line":405,"column":null}},"105":{"start":{"line":400,"column":8},"end":{"line":403,"column":null}},"106":{"start":{"line":401,"column":10},"end":{"line":401,"column":null}},"107":{"start":{"line":402,"column":10},"end":{"line":402,"column":null}},"108":{"start":{"line":404,"column":8},"end":{"line":404,"column":null}},"109":{"start":{"line":406,"column":6},"end":{"line":406,"column":null}},"110":{"start":{"line":406,"column":33},"end":{"line":406,"column":null}},"111":{"start":{"line":408,"column":35},"end":{"line":420,"column":null}},"112":{"start":{"line":409,"column":32},"end":{"line":409,"column":null}},"113":{"start":{"line":410,"column":8},"end":{"line":419,"column":null}},"114":{"start":{"line":422,"column":6},"end":{"line":422,"column":null}},"115":{"start":{"line":422,"column":24},"end":{"line":422,"column":null}},"116":{"start":{"line":424,"column":6},"end":{"line":440,"column":null}},"117":{"start":{"line":425,"column":8},"end":{"line":425,"column":null}},"118":{"start":{"line":425,"column":37},"end":{"line":425,"column":null}},"119":{"start":{"line":426,"column":43},"end":{"line":426,"column":null}},"120":{"start":{"line":427,"column":8},"end":{"line":439,"column":null}},"121":{"start":{"line":428,"column":10},"end":{"line":428,"column":null}},"122":{"start":{"line":428,"column":28},"end":{"line":428,"column":null}},"123":{"start":{"line":428,"column":45},"end":{"line":428,"column":null}},"124":{"start":{"line":429,"column":10},"end":{"line":437,"column":null}},"125":{"start":{"line":445,"column":22},"end":{"line":449,"column":null}},"126":{"start":{"line":447,"column":6},"end":{"line":447,"column":null}},"127":{"start":{"line":452,"column":21},"end":{"line":454,"column":null}},"128":{"start":{"line":453,"column":4},"end":{"line":453,"column":null}},"129":{"start":{"line":453,"column":22},"end":{"line":453,"column":null}},"130":{"start":{"line":453,"column":41},"end":{"line":453,"column":null}},"131":{"start":{"line":456,"column":20},"end":{"line":458,"column":null}},"132":{"start":{"line":457,"column":4},"end":{"line":457,"column":null}},"133":{"start":{"line":457,"column":22},"end":{"line":457,"column":null}},"134":{"start":{"line":457,"column":41},"end":{"line":457,"column":null}},"135":{"start":{"line":460,"column":17},"end":{"line":466,"column":null}},"136":{"start":{"line":462,"column":6},"end":{"line":462,"column":null}},"137":{"start":{"line":463,"column":6},"end":{"line":463,"column":null}},"138":{"start":{"line":464,"column":6},"end":{"line":464,"column":null}},"139":{"start":{"line":464,"column":43},"end":{"line":464,"column":null}},"140":{"start":{"line":469,"column":20},"end":{"line":469,"column":94}},"141":{"start":{"line":469,"column":39},"end":{"line":469,"column":88}},"142":{"start":{"line":476,"column":10},"end":{"line":477,"column":null}},"143":{"start":{"line":478,"column":27},"end":{"line":478,"column":null}},"144":{"start":{"line":493,"column":10},"end":{"line":493,"column":null}},"145":{"start":{"line":494,"column":10},"end":{"line":494,"column":null}},"146":{"start":{"line":496,"column":27},"end":{"line":496,"column":null}},"147":{"start":{"line":498,"column":23},"end":{"line":498,"column":null}},"148":{"start":{"line":512,"column":12},"end":{"line":512,"column":null}},"149":{"start":{"line":512,"column":32},"end":{"line":512,"column":null}},"150":{"start":{"line":513,"column":12},"end":{"line":513,"column":null}},"151":{"start":{"line":542,"column":12},"end":{"line":543,"column":null}},"152":{"start":{"line":545,"column":41},"end":{"line":545,"column":75}},"153":{"start":{"line":546,"column":31},"end":{"line":546,"column":null}}},"fnMap":{"0":{"name":"isSupportedType","decl":{"start":{"line":64,"column":9},"end":{"line":64,"column":25}},"loc":{"start":{"line":64,"column":35},"end":{"line":69,"column":null}}},"1":{"name":"validateFile","decl":{"start":{"line":71,"column":9},"end":{"line":71,"column":22}},"loc":{"start":{"line":71,"column":32},"end":{"line":74,"column":null}}},"2":{"name":"runUpload","decl":{"start":{"line":78,"column":15},"end":{"line":78,"column":null}},"loc":{"start":{"line":86,"column":25},"end":{"line":131,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":88,"column":16},"end":{"line":88,"column":17}},"loc":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":89,"column":12},"end":{"line":89,"column":13}},"loc":{"start":{"line":89,"column":22},"end":{"line":89,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":89,"column":31},"end":{"line":89,"column":32}},"loc":{"start":{"line":89,"column":39},"end":{"line":89,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":92,"column":92},"end":{"line":92,"column":93}},"loc":{"start":{"line":93,"column":6},"end":{"line":93,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":118,"column":24},"end":{"line":118,"column":25}},"loc":{"start":{"line":118,"column":31},"end":{"line":118,"column":null}}},"8":{"name":"runRetry","decl":{"start":{"line":133,"column":15},"end":{"line":133,"column":null}},"loc":{"start":{"line":138,"column":25},"end":{"line":194,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":140,"column":10},"end":{"line":140,"column":11}},"loc":{"start":{"line":141,"column":4},"end":{"line":151,"column":null}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":141,"column":13},"end":{"line":141,"column":14}},"loc":{"start":{"line":142,"column":6},"end":{"line":151,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":155,"column":16},"end":{"line":155,"column":17}},"loc":{"start":{"line":156,"column":4},"end":{"line":156,"column":null}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":156,"column":12},"end":{"line":156,"column":13}},"loc":{"start":{"line":156,"column":22},"end":{"line":156,"column":null}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":156,"column":31},"end":{"line":156,"column":32}},"loc":{"start":{"line":156,"column":39},"end":{"line":156,"column":null}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":182,"column":24},"end":{"line":182,"column":25}},"loc":{"start":{"line":182,"column":31},"end":{"line":182,"column":null}}},"15":{"name":"JobRow","decl":{"start":{"line":198,"column":9},"end":{"line":198,"column":16}},"loc":{"start":{"line":206,"column":1},"end":{"line":361,"column":null}}},"16":{"name":"DocumentUpload","decl":{"start":{"line":365,"column":16},"end":{"line":365,"column":31}},"loc":{"start":{"line":365,"column":95},"end":{"line":561,"column":null}}},"17":{"name":"(anonymous_18)","decl":{"start":{"line":374,"column":30},"end":{"line":374,"column":null}},"loc":{"start":{"line":374,"column":30},"end":{"line":381,"column":5}}},"18":{"name":"(anonymous_19)","decl":{"start":{"line":383,"column":30},"end":{"line":383,"column":31}},"loc":{"start":{"line":383,"column":31},"end":{"line":390,"column":5}}},"19":{"name":"(anonymous_20)","decl":{"start":{"line":393,"column":4},"end":{"line":393,"column":5}},"loc":{"start":{"line":393,"column":5},"end":{"line":441,"column":null}}},"20":{"name":"(anonymous_21)","decl":{"start":{"line":408,"column":48},"end":{"line":408,"column":49}},"loc":{"start":{"line":408,"column":49},"end":{"line":420,"column":null}}},"21":{"name":"(anonymous_22)","decl":{"start":{"line":422,"column":14},"end":{"line":422,"column":15}},"loc":{"start":{"line":422,"column":24},"end":{"line":422,"column":null}}},"22":{"name":"(anonymous_23)","decl":{"start":{"line":427,"column":16},"end":{"line":427,"column":null}},"loc":{"start":{"line":427,"column":16},"end":{"line":439,"column":null}}},"23":{"name":"(anonymous_24)","decl":{"start":{"line":428,"column":18},"end":{"line":428,"column":19}},"loc":{"start":{"line":428,"column":28},"end":{"line":428,"column":null}}},"24":{"name":"(anonymous_25)","decl":{"start":{"line":428,"column":37},"end":{"line":428,"column":38}},"loc":{"start":{"line":428,"column":45},"end":{"line":428,"column":null}}},"25":{"name":"(anonymous_26)","decl":{"start":{"line":446,"column":4},"end":{"line":446,"column":5}},"loc":{"start":{"line":446,"column":20},"end":{"line":448,"column":null}}},"26":{"name":"(anonymous_27)","decl":{"start":{"line":452,"column":33},"end":{"line":452,"column":34}},"loc":{"start":{"line":452,"column":34},"end":{"line":454,"column":5}}},"27":{"name":"(anonymous_28)","decl":{"start":{"line":453,"column":12},"end":{"line":453,"column":13}},"loc":{"start":{"line":453,"column":22},"end":{"line":453,"column":null}}},"28":{"name":"(anonymous_29)","decl":{"start":{"line":453,"column":34},"end":{"line":453,"column":35}},"loc":{"start":{"line":453,"column":41},"end":{"line":453,"column":null}}},"29":{"name":"(anonymous_30)","decl":{"start":{"line":456,"column":32},"end":{"line":456,"column":null}},"loc":{"start":{"line":456,"column":32},"end":{"line":458,"column":5}}},"30":{"name":"(anonymous_31)","decl":{"start":{"line":457,"column":12},"end":{"line":457,"column":13}},"loc":{"start":{"line":457,"column":22},"end":{"line":457,"column":null}}},"31":{"name":"(anonymous_32)","decl":{"start":{"line":457,"column":34},"end":{"line":457,"column":35}},"loc":{"start":{"line":457,"column":41},"end":{"line":457,"column":null}}},"32":{"name":"(anonymous_33)","decl":{"start":{"line":461,"column":4},"end":{"line":461,"column":5}},"loc":{"start":{"line":461,"column":5},"end":{"line":465,"column":null}}},"33":{"name":"(anonymous_34)","decl":{"start":{"line":469,"column":32},"end":{"line":469,"column":33}},"loc":{"start":{"line":469,"column":39},"end":{"line":469,"column":88}}},"34":{"name":"(anonymous_35)","decl":{"start":{"line":475,"column":80},"end":{"line":475,"column":81}},"loc":{"start":{"line":476,"column":10},"end":{"line":477,"column":null}}},"35":{"name":"(anonymous_36)","decl":{"start":{"line":478,"column":21},"end":{"line":478,"column":27}},"loc":{"start":{"line":478,"column":27},"end":{"line":478,"column":null}}},"36":{"name":"(anonymous_37)","decl":{"start":{"line":492,"column":20},"end":{"line":492,"column":21}},"loc":{"start":{"line":492,"column":21},"end":{"line":495,"column":null}}},"37":{"name":"(anonymous_38)","decl":{"start":{"line":496,"column":21},"end":{"line":496,"column":27}},"loc":{"start":{"line":496,"column":27},"end":{"line":496,"column":null}}},"38":{"name":"(anonymous_39)","decl":{"start":{"line":498,"column":17},"end":{"line":498,"column":23}},"loc":{"start":{"line":498,"column":23},"end":{"line":498,"column":null}}},"39":{"name":"(anonymous_40)","decl":{"start":{"line":511,"column":20},"end":{"line":511,"column":21}},"loc":{"start":{"line":511,"column":21},"end":{"line":514,"column":null}}},"40":{"name":"(anonymous_41)","decl":{"start":{"line":541,"column":20},"end":{"line":541,"column":21}},"loc":{"start":{"line":542,"column":12},"end":{"line":543,"column":null}}},"41":{"name":"(anonymous_42)","decl":{"start":{"line":545,"column":35},"end":{"line":545,"column":41}},"loc":{"start":{"line":545,"column":41},"end":{"line":545,"column":75}}},"42":{"name":"(anonymous_43)","decl":{"start":{"line":546,"column":25},"end":{"line":546,"column":31}},"loc":{"start":{"line":546,"column":31},"end":{"line":546,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":65,"column":21},"end":{"line":65,"column":52}},"type":"binary-expr","locations":[{"start":{"line":65,"column":21},"end":{"line":65,"column":51}},{"start":{"line":65,"column":51},"end":{"line":65,"column":52}}]},"1":{"loc":{"start":{"line":66,"column":2},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":2},"end":{"line":66,"column":null}}]},"2":{"loc":{"start":{"line":68,"column":9},"end":{"line":68,"column":null}},"type":"binary-expr","locations":[{"start":{"line":68,"column":9},"end":{"line":68,"column":19}},{"start":{"line":68,"column":23},"end":{"line":68,"column":null}}]},"3":{"loc":{"start":{"line":72,"column":2},"end":{"line":72,"column":null}},"type":"if","locations":[{"start":{"line":72,"column":2},"end":{"line":72,"column":null}}]},"4":{"loc":{"start":{"line":89,"column":39},"end":{"line":89,"column":null}},"type":"cond-expr","locations":[{"start":{"line":89,"column":56},"end":{"line":89,"column":73}},{"start":{"line":89,"column":73},"end":{"line":89,"column":null}}]},"5":{"loc":{"start":{"line":101,"column":8},"end":{"line":105,"column":null}},"type":"if","locations":[{"start":{"line":101,"column":8},"end":{"line":105,"column":null}}]},"6":{"loc":{"start":{"line":106,"column":8},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":106,"column":8},"end":{"line":114,"column":null}}]},"7":{"loc":{"start":{"line":110,"column":26},"end":{"line":110,"column":null}},"type":"binary-expr","locations":[{"start":{"line":110,"column":26},"end":{"line":110,"column":41}},{"start":{"line":110,"column":45},"end":{"line":110,"column":null}}]},"8":{"loc":{"start":{"line":116,"column":8},"end":{"line":116,"column":null}},"type":"if","locations":[{"start":{"line":116,"column":8},"end":{"line":116,"column":null}}]},"9":{"loc":{"start":{"line":116,"column":14},"end":{"line":116,"column":57}},"type":"binary-expr","locations":[{"start":{"line":116,"column":14},"end":{"line":116,"column":37}},{"start":{"line":116,"column":41},"end":{"line":116,"column":57}}]},"10":{"loc":{"start":{"line":126,"column":20},"end":{"line":126,"column":null}},"type":"cond-expr","locations":[{"start":{"line":126,"column":43},"end":{"line":126,"column":54}},{"start":{"line":126,"column":57},"end":{"line":126,"column":null}}]},"11":{"loc":{"start":{"line":142,"column":6},"end":{"line":151,"column":null}},"type":"cond-expr","locations":[{"start":{"line":143,"column":10},"end":{"line":150,"column":null}},{"start":{"line":151,"column":10},"end":{"line":151,"column":null}}]},"12":{"loc":{"start":{"line":156,"column":39},"end":{"line":156,"column":null}},"type":"cond-expr","locations":[{"start":{"line":156,"column":56},"end":{"line":156,"column":73}},{"start":{"line":156,"column":73},"end":{"line":156,"column":null}}]},"13":{"loc":{"start":{"line":165,"column":8},"end":{"line":169,"column":null}},"type":"if","locations":[{"start":{"line":165,"column":8},"end":{"line":169,"column":null}}]},"14":{"loc":{"start":{"line":170,"column":8},"end":{"line":178,"column":null}},"type":"if","locations":[{"start":{"line":170,"column":8},"end":{"line":178,"column":null}}]},"15":{"loc":{"start":{"line":174,"column":26},"end":{"line":174,"column":null}},"type":"binary-expr","locations":[{"start":{"line":174,"column":26},"end":{"line":174,"column":41}},{"start":{"line":174,"column":45},"end":{"line":174,"column":null}}]},"16":{"loc":{"start":{"line":180,"column":8},"end":{"line":180,"column":null}},"type":"if","locations":[{"start":{"line":180,"column":8},"end":{"line":180,"column":null}}]},"17":{"loc":{"start":{"line":180,"column":14},"end":{"line":180,"column":57}},"type":"binary-expr","locations":[{"start":{"line":180,"column":14},"end":{"line":180,"column":37}},{"start":{"line":180,"column":41},"end":{"line":180,"column":57}}]},"18":{"loc":{"start":{"line":190,"column":20},"end":{"line":190,"column":null}},"type":"cond-expr","locations":[{"start":{"line":190,"column":43},"end":{"line":190,"column":54}},{"start":{"line":190,"column":57},"end":{"line":190,"column":null}}]},"19":{"loc":{"start":{"line":207,"column":21},"end":{"line":207,"column":null}},"type":"binary-expr","locations":[{"start":{"line":207,"column":21},"end":{"line":207,"column":49}},{"start":{"line":207,"column":49},"end":{"line":207,"column":null}}]},"20":{"loc":{"start":{"line":209,"column":19},"end":{"line":209,"column":null}},"type":"binary-expr","locations":[{"start":{"line":209,"column":19},"end":{"line":209,"column":49}},{"start":{"line":209,"column":49},"end":{"line":209,"column":null}}]},"21":{"loc":{"start":{"line":212,"column":4},"end":{"line":222,"column":null}},"type":"cond-expr","locations":[{"start":{"line":213,"column":8},"end":{"line":213,"column":27}},{"start":{"line":214,"column":8},"end":{"line":222,"column":null}}]},"22":{"loc":{"start":{"line":214,"column":8},"end":{"line":222,"column":null}},"type":"cond-expr","locations":[{"start":{"line":215,"column":10},"end":{"line":217,"column":null}},{"start":{"line":218,"column":10},"end":{"line":222,"column":null}}]},"23":{"loc":{"start":{"line":215,"column":10},"end":{"line":217,"column":null}},"type":"cond-expr","locations":[{"start":{"line":216,"column":13},"end":{"line":216,"column":69}},{"start":{"line":217,"column":12},"end":{"line":217,"column":null}}]},"24":{"loc":{"start":{"line":216,"column":13},"end":{"line":216,"column":69}},"type":"binary-expr","locations":[{"start":{"line":216,"column":13},"end":{"line":216,"column":46}},{"start":{"line":216,"column":50},"end":{"line":216,"column":69}}]},"25":{"loc":{"start":{"line":218,"column":10},"end":{"line":222,"column":null}},"type":"cond-expr","locations":[{"start":{"line":219,"column":12},"end":{"line":219,"column":null}},{"start":{"line":220,"column":12},"end":{"line":222,"column":null}}]},"26":{"loc":{"start":{"line":220,"column":12},"end":{"line":222,"column":null}},"type":"cond-expr","locations":[{"start":{"line":221,"column":14},"end":{"line":221,"column":null}},{"start":{"line":222,"column":14},"end":{"line":222,"column":null}}]},"27":{"loc":{"start":{"line":227,"column":8},"end":{"line":229,"column":null}},"type":"cond-expr","locations":[{"start":{"line":228,"column":12},"end":{"line":228,"column":null}},{"start":{"line":229,"column":12},"end":{"line":229,"column":null}}]},"28":{"loc":{"start":{"line":234,"column":9},"end":{"line":234,"column":null}},"type":"binary-expr","locations":[{"start":{"line":234,"column":9},"end":{"line":234,"column":null}}]},"29":{"loc":{"start":{"line":245,"column":9},"end":{"line":245,"column":null}},"type":"binary-expr","locations":[{"start":{"line":245,"column":9},"end":{"line":245,"column":null}}]},"30":{"loc":{"start":{"line":260,"column":9},"end":{"line":260,"column":null}},"type":"binary-expr","locations":[{"start":{"line":260,"column":9},"end":{"line":260,"column":null}}]},"31":{"loc":{"start":{"line":281,"column":9},"end":{"line":281,"column":null}},"type":"binary-expr","locations":[{"start":{"line":281,"column":9},"end":{"line":281,"column":null}}]},"32":{"loc":{"start":{"line":303,"column":12},"end":{"line":307,"column":null}},"type":"cond-expr","locations":[{"start":{"line":304,"column":16},"end":{"line":304,"column":null}},{"start":{"line":305,"column":16},"end":{"line":307,"column":null}}]},"33":{"loc":{"start":{"line":305,"column":16},"end":{"line":307,"column":null}},"type":"cond-expr","locations":[{"start":{"line":306,"column":18},"end":{"line":306,"column":null}},{"start":{"line":307,"column":18},"end":{"line":307,"column":null}}]},"34":{"loc":{"start":{"line":314,"column":9},"end":{"line":314,"column":21}},"type":"binary-expr","locations":[{"start":{"line":314,"column":9},"end":{"line":314,"column":21}},{"start":{"line":314,"column":21},"end":{"line":314,"column":55}},{"start":{"line":314,"column":55},"end":{"line":314,"column":null}}]},"35":{"loc":{"start":{"line":324,"column":9},"end":{"line":324,"column":null}},"type":"binary-expr","locations":[{"start":{"line":324,"column":9},"end":{"line":324,"column":null}}]},"36":{"loc":{"start":{"line":344,"column":7},"end":{"line":344,"column":null}},"type":"binary-expr","locations":[{"start":{"line":344,"column":7},"end":{"line":344,"column":null}}]},"37":{"loc":{"start":{"line":354,"column":7},"end":{"line":354,"column":19}},"type":"binary-expr","locations":[{"start":{"line":354,"column":7},"end":{"line":354,"column":19}},{"start":{"line":354,"column":19},"end":{"line":354,"column":35}}]},"38":{"loc":{"start":{"line":377,"column":4},"end":{"line":380,"column":null}},"type":"if","locations":[{"start":{"line":377,"column":4},"end":{"line":380,"column":null}}]},"39":{"loc":{"start":{"line":384,"column":4},"end":{"line":389,"column":null}},"type":"if","locations":[{"start":{"line":384,"column":4},"end":{"line":389,"column":null}},{"start":{"line":387,"column":11},"end":{"line":389,"column":null}}]},"40":{"loc":{"start":{"line":395,"column":6},"end":{"line":395,"column":null}},"type":"if","locations":[{"start":{"line":395,"column":6},"end":{"line":395,"column":null}}]},"41":{"loc":{"start":{"line":400,"column":8},"end":{"line":403,"column":null}},"type":"if","locations":[{"start":{"line":400,"column":8},"end":{"line":403,"column":null}}]},"42":{"loc":{"start":{"line":406,"column":6},"end":{"line":406,"column":null}},"type":"if","locations":[{"start":{"line":406,"column":6},"end":{"line":406,"column":null}}]},"43":{"loc":{"start":{"line":414,"column":19},"end":{"line":414,"column":null}},"type":"cond-expr","locations":[{"start":{"line":414,"column":37},"end":{"line":414,"column":48}},{"start":{"line":414,"column":48},"end":{"line":414,"column":null}}]},"44":{"loc":{"start":{"line":416,"column":21},"end":{"line":416,"column":null}},"type":"cond-expr","locations":[{"start":{"line":416,"column":40},"end":{"line":416,"column":69}},{"start":{"line":416,"column":69},"end":{"line":416,"column":null}}]},"45":{"loc":{"start":{"line":417,"column":24},"end":{"line":417,"column":null}},"type":"binary-expr","locations":[{"start":{"line":417,"column":24},"end":{"line":417,"column":43}},{"start":{"line":417,"column":43},"end":{"line":417,"column":null}}]},"46":{"loc":{"start":{"line":425,"column":8},"end":{"line":425,"column":null}},"type":"if","locations":[{"start":{"line":425,"column":8},"end":{"line":425,"column":null}}]},"47":{"loc":{"start":{"line":428,"column":45},"end":{"line":428,"column":null}},"type":"cond-expr","locations":[{"start":{"line":428,"column":59},"end":{"line":428,"column":91}},{"start":{"line":428,"column":91},"end":{"line":428,"column":null}}]},"48":{"loc":{"start":{"line":457,"column":41},"end":{"line":457,"column":null}},"type":"binary-expr","locations":[{"start":{"line":457,"column":41},"end":{"line":457,"column":67}},{"start":{"line":457,"column":67},"end":{"line":457,"column":null}}]},"49":{"loc":{"start":{"line":464,"column":6},"end":{"line":464,"column":null}},"type":"if","locations":[{"start":{"line":464,"column":6},"end":{"line":464,"column":null}}]},"50":{"loc":{"start":{"line":469,"column":39},"end":{"line":469,"column":88}},"type":"binary-expr","locations":[{"start":{"line":469,"column":39},"end":{"line":469,"column":65}},{"start":{"line":469,"column":65},"end":{"line":469,"column":88}}]},"51":{"loc":{"start":{"line":480,"column":14},"end":{"line":482,"column":null}},"type":"cond-expr","locations":[{"start":{"line":481,"column":18},"end":{"line":481,"column":null}},{"start":{"line":482,"column":18},"end":{"line":482,"column":null}}]},"52":{"loc":{"start":{"line":500,"column":10},"end":{"line":502,"column":null}},"type":"cond-expr","locations":[{"start":{"line":501,"column":14},"end":{"line":501,"column":null}},{"start":{"line":502,"column":14},"end":{"line":502,"column":null}}]},"53":{"loc":{"start":{"line":512,"column":12},"end":{"line":512,"column":null}},"type":"if","locations":[{"start":{"line":512,"column":12},"end":{"line":512,"column":null}}]},"54":{"loc":{"start":{"line":539,"column":7},"end":{"line":539,"column":null}},"type":"binary-expr","locations":[{"start":{"line":539,"column":7},"end":{"line":539,"column":null}}]},"55":{"loc":{"start":{"line":545,"column":23},"end":{"line":545,"column":null}},"type":"cond-expr","locations":[{"start":{"line":545,"column":35},"end":{"line":545,"column":75}},{"start":{"line":545,"column":75},"end":{"line":545,"column":null}}]},"56":{"loc":{"start":{"line":549,"column":11},"end":{"line":549,"column":null}},"type":"binary-expr","locations":[{"start":{"line":549,"column":11},"end":{"line":549,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0],"4":[0,0],"5":[0],"6":[0],"7":[0,0],"8":[0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0],"14":[0],"15":[0,0],"16":[0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0],"24":[0,0],"25":[0,0],"26":[0,0],"27":[0,0],"28":[0],"29":[0],"30":[0],"31":[0],"32":[0,0],"33":[0,0],"34":[0,0,0],"35":[0],"36":[0],"37":[0,0],"38":[0],"39":[0,0],"40":[0],"41":[0],"42":[0],"43":[0,0],"44":[0,0],"45":[0,0],"46":[0],"47":[0,0],"48":[0,0],"49":[0],"50":[0,0],"51":[0,0],"52":[0,0],"53":[0],"54":[0],"55":[0,0],"56":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/providers/AuthProvider.tsx","statementMap":{"0":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"1":{"start":{"line":22,"column":0},"end":{"line":22,"column":15}},"2":{"start":{"line":7,"column":32},"end":{"line":7,"column":null}},"3":{"start":{"line":22,"column":15},"end":{"line":22,"column":28}}},"fnMap":{"0":{"name":"AuthProvider","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":28}},"loc":{"start":{"line":18,"column":60},"end":{"line":20,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/providers/SessionErrorGuard.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/providers/SessionErrorGuard.tsx","statementMap":{"0":{"start":{"line":6,"column":16},"end":{"line":6,"column":null}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":7,"column":28},"end":{"line":7,"column":null}},"4":{"start":{"line":9,"column":2},"end":{"line":13,"column":null}},"5":{"start":{"line":10,"column":4},"end":{"line":12,"column":null}},"6":{"start":{"line":11,"column":6},"end":{"line":11,"column":null}},"7":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}}},"fnMap":{"0":{"name":"SessionErrorGuard","decl":{"start":{"line":6,"column":16},"end":{"line":6,"column":null}},"loc":{"start":{"line":6,"column":16},"end":{"line":16,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":9,"column":12},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":12},"end":{"line":13,"column":5}}}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":4},"end":{"line":12,"column":null}},"type":"if","locations":[{"start":{"line":10,"column":4},"end":{"line":12,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"f":{"0":0,"1":0},"b":{"0":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/CitationCard.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/CitationCard.tsx","statementMap":{"0":{"start":{"line":12,"column":16},"end":{"line":12,"column":29}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":13,"column":17},"end":{"line":13,"column":null}},"3":{"start":{"line":15,"column":24},"end":{"line":19,"column":null}},"4":{"start":{"line":21,"column":16},"end":{"line":21,"column":null}},"5":{"start":{"line":23,"column":4},"end":{"line":23,"column":91}},"6":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"7":{"start":{"line":26,"column":26},"end":{"line":26,"column":null}},"8":{"start":{"line":27,"column":19},"end":{"line":27,"column":null}},"9":{"start":{"line":28,"column":4},"end":{"line":29,"column":null}},"10":{"start":{"line":28,"column":23},"end":{"line":28,"column":null}},"11":{"start":{"line":29,"column":9},"end":{"line":29,"column":null}},"12":{"start":{"line":29,"column":29},"end":{"line":29,"column":null}},"13":{"start":{"line":30,"column":18},"end":{"line":30,"column":null}},"14":{"start":{"line":31,"column":4},"end":{"line":32,"column":null}}},"fnMap":{"0":{"name":"CitationCard","decl":{"start":{"line":12,"column":16},"end":{"line":12,"column":29}},"loc":{"start":{"line":12,"column":77},"end":{"line":60,"column":null}}},"1":{"name":"handleClick","decl":{"start":{"line":25,"column":11},"end":{"line":25,"column":null}},"loc":{"start":{"line":25,"column":11},"end":{"line":34,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":24},"end":{"line":19,"column":null}},"type":"cond-expr","locations":[{"start":{"line":16,"column":6},"end":{"line":16,"column":26}},{"start":{"line":17,"column":6},"end":{"line":19,"column":null}}]},"1":{"loc":{"start":{"line":17,"column":6},"end":{"line":19,"column":null}},"type":"cond-expr","locations":[{"start":{"line":18,"column":8},"end":{"line":18,"column":33}},{"start":{"line":19,"column":8},"end":{"line":19,"column":null}}]},"2":{"loc":{"start":{"line":21,"column":16},"end":{"line":21,"column":null}},"type":"binary-expr","locations":[{"start":{"line":21,"column":16},"end":{"line":21,"column":87}},{"start":{"line":21,"column":87},"end":{"line":21,"column":null}}]},"3":{"loc":{"start":{"line":21,"column":17},"end":{"line":21,"column":41}},"type":"binary-expr","locations":[{"start":{"line":21,"column":17},"end":{"line":21,"column":31}},{"start":{"line":21,"column":35},"end":{"line":21,"column":41}}]},"4":{"loc":{"start":{"line":23,"column":4},"end":{"line":23,"column":91}},"type":"cond-expr","locations":[{"start":{"line":23,"column":36},"end":{"line":23,"column":75}},{"start":{"line":23,"column":75},"end":{"line":23,"column":91}}]},"5":{"loc":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":26,"column":4},"end":{"line":26,"column":null}}]},"6":{"loc":{"start":{"line":28,"column":4},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":29,"column":null}},{"start":{"line":29,"column":9},"end":{"line":29,"column":null}}]},"7":{"loc":{"start":{"line":29,"column":9},"end":{"line":29,"column":null}},"type":"if","locations":[{"start":{"line":29,"column":9},"end":{"line":29,"column":null}}]},"8":{"loc":{"start":{"line":32,"column":66},"end":{"line":32,"column":91}},"type":"cond-expr","locations":[{"start":{"line":32,"column":74},"end":{"line":32,"column":85}},{"start":{"line":32,"column":88},"end":{"line":32,"column":91}}]},"9":{"loc":{"start":{"line":38,"column":15},"end":{"line":38,"column":null}},"type":"cond-expr","locations":[{"start":{"line":38,"column":33},"end":{"line":38,"column":47}},{"start":{"line":38,"column":47},"end":{"line":38,"column":null}}]},"10":{"loc":{"start":{"line":39,"column":50},"end":{"line":39,"column":129}},"type":"cond-expr","locations":[{"start":{"line":39,"column":68},"end":{"line":39,"column":126}},{"start":{"line":39,"column":126},"end":{"line":39,"column":129}}]},"11":{"loc":{"start":{"line":49,"column":7},"end":{"line":49,"column":23}},"type":"binary-expr","locations":[{"start":{"line":49,"column":7},"end":{"line":49,"column":23}}]},"12":{"loc":{"start":{"line":53,"column":7},"end":{"line":53,"column":22}},"type":"binary-expr","locations":[{"start":{"line":53,"column":7},"end":{"line":53,"column":22}}]}},"s":{"0":0,"1":2,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0],"6":[0,0],"7":[0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0],"12":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/ContentChunks.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/ContentChunks.tsx","statementMap":{"0":{"start":{"line":34,"column":16},"end":{"line":34,"column":30}},"1":{"start":{"line":3,"column":36},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":29},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":39},"end":{"line":5,"column":null}},"4":{"start":{"line":40,"column":34},"end":{"line":40,"column":null}},"5":{"start":{"line":41,"column":32},"end":{"line":41,"column":null}},"6":{"start":{"line":42,"column":28},"end":{"line":42,"column":null}},"7":{"start":{"line":44,"column":2},"end":{"line":78,"column":null}},"8":{"start":{"line":46,"column":6},"end":{"line":74,"column":null}},"9":{"start":{"line":47,"column":21},"end":{"line":47,"column":null}},"10":{"start":{"line":48,"column":26},"end":{"line":48,"column":null}},"11":{"start":{"line":48,"column":45},"end":{"line":48,"column":null}},"12":{"start":{"line":50,"column":25},"end":{"line":62,"column":null}},"13":{"start":{"line":52,"column":28},"end":{"line":52,"column":null}},"14":{"start":{"line":53,"column":12},"end":{"line":61,"column":null}},"15":{"start":{"line":56,"column":65},"end":{"line":56,"column":null}},"16":{"start":{"line":57,"column":63},"end":{"line":60,"column":null}},"17":{"start":{"line":65,"column":23},"end":{"line":67,"column":null}},"18":{"start":{"line":66,"column":27},"end":{"line":66,"column":68}},"19":{"start":{"line":67,"column":25},"end":{"line":67,"column":null}},"20":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"21":{"start":{"line":71,"column":8},"end":{"line":71,"column":null}},"22":{"start":{"line":73,"column":8},"end":{"line":73,"column":null}},"23":{"start":{"line":77,"column":4},"end":{"line":77,"column":null}},"24":{"start":{"line":80,"column":2},"end":{"line":94,"column":null}},"25":{"start":{"line":85,"column":12},"end":{"line":85,"column":25}},"26":{"start":{"line":96,"column":2},"end":{"line":102,"column":null}},"27":{"start":{"line":104,"column":2},"end":{"line":106,"column":null}},"28":{"start":{"line":105,"column":4},"end":{"line":105,"column":null}},"29":{"start":{"line":115,"column":26},"end":{"line":115,"column":null}},"30":{"start":{"line":116,"column":10},"end":{"line":118,"column":null}},"31":{"start":{"line":137,"column":20},"end":{"line":137,"column":34}},"32":{"start":{"line":147,"column":18},"end":{"line":147,"column":31}}},"fnMap":{"0":{"name":"ContentChunks","decl":{"start":{"line":34,"column":16},"end":{"line":34,"column":30}},"loc":{"start":{"line":39,"column":21},"end":{"line":168,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":44,"column":12},"end":{"line":44,"column":null}},"loc":{"start":{"line":44,"column":12},"end":{"line":78,"column":5}}},"2":{"name":"fetchContent","decl":{"start":{"line":45,"column":19},"end":{"line":45,"column":null}},"loc":{"start":{"line":45,"column":19},"end":{"line":75,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":48,"column":38},"end":{"line":48,"column":39}},"loc":{"start":{"line":48,"column":45},"end":{"line":48,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":51,"column":24},"end":{"line":51,"column":31}},"loc":{"start":{"line":51,"column":31},"end":{"line":62,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":56,"column":53},"end":{"line":56,"column":54}},"loc":{"start":{"line":56,"column":65},"end":{"line":56,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":57,"column":55},"end":{"line":57,"column":56}},"loc":{"start":{"line":57,"column":63},"end":{"line":60,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":66,"column":19},"end":{"line":66,"column":20}},"loc":{"start":{"line":66,"column":27},"end":{"line":66,"column":68}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":67,"column":18},"end":{"line":67,"column":19}},"loc":{"start":{"line":67,"column":25},"end":{"line":67,"column":null}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":84,"column":25},"end":{"line":84,"column":26}},"loc":{"start":{"line":85,"column":12},"end":{"line":85,"column":25}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":114,"column":22},"end":{"line":114,"column":23}},"loc":{"start":{"line":114,"column":23},"end":{"line":164,"column":null}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":136,"column":36},"end":{"line":136,"column":37}},"loc":{"start":{"line":137,"column":20},"end":{"line":137,"column":34}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":146,"column":32},"end":{"line":146,"column":33}},"loc":{"start":{"line":147,"column":18},"end":{"line":147,"column":31}}}},"branchMap":{"0":{"loc":{"start":{"line":56,"column":25},"end":{"line":56,"column":46}},"type":"binary-expr","locations":[{"start":{"line":56,"column":25},"end":{"line":56,"column":41}},{"start":{"line":56,"column":45},"end":{"line":56,"column":46}}]},"1":{"loc":{"start":{"line":57,"column":23},"end":{"line":57,"column":48}},"type":"binary-expr","locations":[{"start":{"line":57,"column":23},"end":{"line":57,"column":43}},{"start":{"line":57,"column":47},"end":{"line":57,"column":48}}]},"2":{"loc":{"start":{"line":59,"column":25},"end":{"line":59,"column":null}},"type":"binary-expr","locations":[{"start":{"line":59,"column":25},"end":{"line":59,"column":34}},{"start":{"line":59,"column":38},"end":{"line":59,"column":null}}]},"3":{"loc":{"start":{"line":66,"column":27},"end":{"line":66,"column":68}},"type":"cond-expr","locations":[{"start":{"line":66,"column":54},"end":{"line":66,"column":66}},{"start":{"line":66,"column":66},"end":{"line":66,"column":68}}]},"4":{"loc":{"start":{"line":67,"column":25},"end":{"line":67,"column":null}},"type":"binary-expr","locations":[{"start":{"line":67,"column":25},"end":{"line":67,"column":48}},{"start":{"line":67,"column":48},"end":{"line":67,"column":null}}]},"5":{"loc":{"start":{"line":71,"column":17},"end":{"line":71,"column":null}},"type":"cond-expr","locations":[{"start":{"line":71,"column":40},"end":{"line":71,"column":51}},{"start":{"line":71,"column":54},"end":{"line":71,"column":null}}]},"6":{"loc":{"start":{"line":80,"column":2},"end":{"line":94,"column":null}},"type":"if","locations":[{"start":{"line":80,"column":2},"end":{"line":94,"column":null}}]},"7":{"loc":{"start":{"line":96,"column":2},"end":{"line":102,"column":null}},"type":"if","locations":[{"start":{"line":96,"column":2},"end":{"line":102,"column":null}}]},"8":{"loc":{"start":{"line":104,"column":2},"end":{"line":106,"column":null}},"type":"if","locations":[{"start":{"line":104,"column":2},"end":{"line":106,"column":null}}]},"9":{"loc":{"start":{"line":119,"column":57},"end":{"line":119,"column":121}},"type":"cond-expr","locations":[{"start":{"line":119,"column":67},"end":{"line":119,"column":118}},{"start":{"line":119,"column":118},"end":{"line":119,"column":121}}]},"10":{"loc":{"start":{"line":122,"column":17},"end":{"line":122,"column":null}},"type":"binary-expr","locations":[{"start":{"line":122,"column":17},"end":{"line":122,"column":null}}]},"11":{"loc":{"start":{"line":134,"column":15},"end":{"line":134,"column":null}},"type":"binary-expr","locations":[{"start":{"line":134,"column":15},"end":{"line":134,"column":null}}]},"12":{"loc":{"start":{"line":138,"column":23},"end":{"line":138,"column":58}},"type":"binary-expr","locations":[{"start":{"line":138,"column":23},"end":{"line":138,"column":36}},{"start":{"line":138,"column":40},"end":{"line":138,"column":58}}]},"13":{"loc":{"start":{"line":154,"column":21},"end":{"line":154,"column":34}},"type":"binary-expr","locations":[{"start":{"line":154,"column":21},"end":{"line":154,"column":34}}]}},"s":{"0":0,"1":1,"2":1,"3":1,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0],"7":[0],"8":[0],"9":[0,0],"10":[0],"11":[0],"12":[0,0],"13":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/LessonNav.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/LessonNav.tsx","statementMap":{"0":{"start":{"line":14,"column":16},"end":{"line":14,"column":26}},"1":{"start":{"line":22,"column":2},"end":{"line":30,"column":null}},"2":{"start":{"line":26,"column":10},"end":{"line":26,"column":23}},"3":{"start":{"line":32,"column":2},"end":{"line":38,"column":null}},"4":{"start":{"line":44,"column":27},"end":{"line":44,"column":null}},"5":{"start":{"line":45,"column":29},"end":{"line":45,"column":61}},"6":{"start":{"line":46,"column":30},"end":{"line":46,"column":null}},"7":{"start":{"line":47,"column":28},"end":{"line":47,"column":null}},"8":{"start":{"line":49,"column":10},"end":{"line":50,"column":null}},"9":{"start":{"line":52,"column":31},"end":{"line":52,"column":null}}},"fnMap":{"0":{"name":"LessonNav","decl":{"start":{"line":14,"column":16},"end":{"line":14,"column":26}},"loc":{"start":{"line":21,"column":17},"end":{"line":103,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":23},"end":{"line":25,"column":24}},"loc":{"start":{"line":26,"column":10},"end":{"line":26,"column":23}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":43,"column":21},"end":{"line":43,"column":22}},"loc":{"start":{"line":43,"column":30},"end":{"line":99,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":52,"column":25},"end":{"line":52,"column":31}},"loc":{"start":{"line":52,"column":31},"end":{"line":52,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":19,"column":2},"end":{"line":19,"column":17}},"type":"default-arg","locations":[{"start":{"line":19,"column":12},"end":{"line":19,"column":17}}]},"1":{"loc":{"start":{"line":22,"column":2},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":22,"column":2},"end":{"line":30,"column":null}}]},"2":{"loc":{"start":{"line":32,"column":2},"end":{"line":38,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":2},"end":{"line":38,"column":null}}]},"3":{"loc":{"start":{"line":47,"column":28},"end":{"line":47,"column":null}},"type":"binary-expr","locations":[{"start":{"line":47,"column":28},"end":{"line":47,"column":60}},{"start":{"line":47,"column":60},"end":{"line":47,"column":null}}]},"4":{"loc":{"start":{"line":54,"column":18},"end":{"line":56,"column":null}},"type":"cond-expr","locations":[{"start":{"line":55,"column":22},"end":{"line":55,"column":null}},{"start":{"line":56,"column":22},"end":{"line":56,"column":null}}]},"5":{"loc":{"start":{"line":58,"column":30},"end":{"line":58,"column":null}},"type":"cond-expr","locations":[{"start":{"line":58,"column":43},"end":{"line":58,"column":52}},{"start":{"line":58,"column":52},"end":{"line":58,"column":null}}]},"6":{"loc":{"start":{"line":62,"column":20},"end":{"line":62,"column":null}},"type":"cond-expr","locations":[{"start":{"line":62,"column":34},"end":{"line":62,"column":49}},{"start":{"line":62,"column":49},"end":{"line":62,"column":null}}]},"7":{"loc":{"start":{"line":66,"column":19},"end":{"line":81,"column":null}},"type":"cond-expr","locations":[{"start":{"line":67,"column":20},"end":{"line":81,"column":28}},{"start":{"line":81,"column":20},"end":{"line":81,"column":null}}]},"8":{"loc":{"start":{"line":85,"column":17},"end":{"line":85,"column":null}},"type":"binary-expr","locations":[{"start":{"line":85,"column":17},"end":{"line":85,"column":null}}]},"9":{"loc":{"start":{"line":91,"column":17},"end":{"line":91,"column":null}},"type":"binary-expr","locations":[{"start":{"line":91,"column":17},"end":{"line":91,"column":null}}]}},"s":{"0":9,"1":9,"2":3,"3":8,"4":21,"5":21,"6":21,"7":21,"8":21,"9":1},"f":{"0":9,"1":3,"2":21,"3":1},"b":{"0":[8],"1":[1],"2":[1],"3":[21,0],"4":[1,20],"5":[1,20],"6":[4,17],"7":[4,17],"8":[21],"9":[21]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/OutputPane.tsx","statementMap":{"0":{"start":{"line":218,"column":16},"end":{"line":218,"column":27}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":64},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":47},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":32},"end":{"line":6,"column":null}},"5":{"start":{"line":9,"column":31},"end":{"line":9,"column":null}},"6":{"start":{"line":10,"column":28},"end":{"line":10,"column":null}},"7":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"8":{"start":{"line":65,"column":66},"end":{"line":65,"column":null}},"9":{"start":{"line":66,"column":2},"end":{"line":70,"column":null}},"10":{"start":{"line":67,"column":16},"end":{"line":67,"column":null}},"11":{"start":{"line":68,"column":4},"end":{"line":68,"column":null}},"12":{"start":{"line":68,"column":21},"end":{"line":68,"column":null}},"13":{"start":{"line":69,"column":4},"end":{"line":69,"column":null}},"14":{"start":{"line":71,"column":16},"end":{"line":71,"column":null}},"15":{"start":{"line":72,"column":18},"end":{"line":75,"column":null}},"16":{"start":{"line":72,"column":56},"end":{"line":75,"column":null}},"17":{"start":{"line":83,"column":12},"end":{"line":83,"column":25}},"18":{"start":{"line":115,"column":16},"end":{"line":115,"column":29}},"19":{"start":{"line":139,"column":16},"end":{"line":139,"column":null}},"20":{"start":{"line":160,"column":48},"end":{"line":160,"column":null}},"21":{"start":{"line":161,"column":16},"end":{"line":185,"column":null}},"22":{"start":{"line":175,"column":63},"end":{"line":175,"column":87}},"23":{"start":{"line":175,"column":99},"end":{"line":175,"column":107}},"24":{"start":{"line":176,"column":85},"end":{"line":176,"column":96}},"25":{"start":{"line":194,"column":10},"end":{"line":194,"column":27}},"26":{"start":{"line":210,"column":83},"end":{"line":214,"column":null}},"27":{"start":{"line":227,"column":34},"end":{"line":227,"column":null}},"28":{"start":{"line":228,"column":28},"end":{"line":228,"column":null}},"29":{"start":{"line":229,"column":32},"end":{"line":229,"column":null}},"30":{"start":{"line":230,"column":42},"end":{"line":230,"column":null}},"31":{"start":{"line":231,"column":42},"end":{"line":231,"column":null}},"32":{"start":{"line":232,"column":40},"end":{"line":232,"column":null}},"33":{"start":{"line":233,"column":20},"end":{"line":233,"column":null}},"34":{"start":{"line":234,"column":25},"end":{"line":234,"column":null}},"35":{"start":{"line":236,"column":2},"end":{"line":238,"column":null}},"36":{"start":{"line":237,"column":4},"end":{"line":237,"column":null}},"37":{"start":{"line":241,"column":27},"end":{"line":243,"column":null}},"38":{"start":{"line":243,"column":37},"end":{"line":243,"column":null}},"39":{"start":{"line":246,"column":21},"end":{"line":246,"column":null}},"40":{"start":{"line":247,"column":4},"end":{"line":247,"column":null}},"41":{"start":{"line":247,"column":30},"end":{"line":247,"column":null}},"42":{"start":{"line":248,"column":4},"end":{"line":248,"column":null}},"43":{"start":{"line":249,"column":4},"end":{"line":249,"column":null}},"44":{"start":{"line":249,"column":26},"end":{"line":249,"column":null}},"45":{"start":{"line":250,"column":4},"end":{"line":250,"column":null}},"46":{"start":{"line":251,"column":4},"end":{"line":251,"column":null}},"47":{"start":{"line":252,"column":4},"end":{"line":268,"column":null}},"48":{"start":{"line":253,"column":21},"end":{"line":253,"column":null}},"49":{"start":{"line":254,"column":6},"end":{"line":257,"column":null}},"50":{"start":{"line":254,"column":28},"end":{"line":257,"column":null}},"51":{"start":{"line":259,"column":6},"end":{"line":265,"column":null}},"52":{"start":{"line":259,"column":28},"end":{"line":265,"column":null}},"53":{"start":{"line":267,"column":6},"end":{"line":267,"column":null}},"54":{"start":{"line":272,"column":18},"end":{"line":272,"column":null}},"55":{"start":{"line":273,"column":4},"end":{"line":273,"column":null}},"56":{"start":{"line":274,"column":4},"end":{"line":274,"column":null}},"57":{"start":{"line":275,"column":4},"end":{"line":275,"column":null}},"58":{"start":{"line":275,"column":26},"end":{"line":275,"column":null}},"59":{"start":{"line":276,"column":15},"end":{"line":276,"column":null}},"60":{"start":{"line":277,"column":4},"end":{"line":297,"column":null}},"61":{"start":{"line":278,"column":21},"end":{"line":278,"column":null}},"62":{"start":{"line":279,"column":25},"end":{"line":279,"column":null}},"63":{"start":{"line":280,"column":6},"end":{"line":283,"column":null}},"64":{"start":{"line":280,"column":28},"end":{"line":283,"column":null}},"65":{"start":{"line":284,"column":6},"end":{"line":284,"column":null}},"66":{"start":{"line":284,"column":39},"end":{"line":284,"column":null}},"67":{"start":{"line":285,"column":6},"end":{"line":285,"column":null}},"68":{"start":{"line":287,"column":6},"end":{"line":293,"column":null}},"69":{"start":{"line":287,"column":28},"end":{"line":293,"column":null}},"70":{"start":{"line":295,"column":6},"end":{"line":295,"column":null}},"71":{"start":{"line":296,"column":6},"end":{"line":296,"column":null}},"72":{"start":{"line":301,"column":2},"end":{"line":315,"column":null}},"73":{"start":{"line":302,"column":4},"end":{"line":309,"column":null}},"74":{"start":{"line":309,"column":6},"end":{"line":309,"column":null}},"75":{"start":{"line":310,"column":4},"end":{"line":310,"column":null}},"76":{"start":{"line":311,"column":4},"end":{"line":311,"column":null}},"77":{"start":{"line":318,"column":4},"end":{"line":321,"column":null}},"78":{"start":{"line":319,"column":6},"end":{"line":319,"column":null}},"79":{"start":{"line":320,"column":6},"end":{"line":320,"column":null}},"80":{"start":{"line":324,"column":22},"end":{"line":326,"column":null}},"81":{"start":{"line":328,"column":28},"end":{"line":328,"column":69}},"82":{"start":{"line":342,"column":16},"end":{"line":342,"column":null}},"83":{"start":{"line":342,"column":39},"end":{"line":342,"column":null}},"84":{"start":{"line":343,"column":16},"end":{"line":343,"column":null}},"85":{"start":{"line":355,"column":16},"end":{"line":355,"column":null}},"86":{"start":{"line":355,"column":38},"end":{"line":355,"column":null}},"87":{"start":{"line":356,"column":16},"end":{"line":356,"column":null}},"88":{"start":{"line":407,"column":25},"end":{"line":407,"column":null}},"89":{"start":{"line":409,"column":10},"end":{"line":450,"column":null}},"90":{"start":{"line":410,"column":12},"end":{"line":411,"column":27}},"91":{"start":{"line":424,"column":47},"end":{"line":424,"column":null}},"92":{"start":{"line":435,"column":50},"end":{"line":435,"column":73}},"93":{"start":{"line":436,"column":24},"end":{"line":437,"column":null}},"94":{"start":{"line":438,"column":41},"end":{"line":438,"column":null}},"95":{"start":{"line":452,"column":10},"end":{"line":454,"column":null}},"96":{"start":{"line":471,"column":22},"end":{"line":472,"column":null}},"97":{"start":{"line":541,"column":29},"end":{"line":541,"column":null}}},"fnMap":{"0":{"name":"ScoreBar","decl":{"start":{"line":42,"column":9},"end":{"line":42,"column":18}},"loc":{"start":{"line":42,"column":71},"end":{"line":61,"column":null}}},"1":{"name":"EvidenceDrawer","decl":{"start":{"line":63,"column":9},"end":{"line":63,"column":24}},"loc":{"start":{"line":63,"column":70},"end":{"line":155,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":66,"column":20},"end":{"line":66,"column":21}},"loc":{"start":{"line":66,"column":21},"end":{"line":70,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":72,"column":44},"end":{"line":72,"column":45}},"loc":{"start":{"line":72,"column":56},"end":{"line":75,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":82,"column":25},"end":{"line":82,"column":26}},"loc":{"start":{"line":83,"column":12},"end":{"line":83,"column":25}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":114,"column":29},"end":{"line":114,"column":30}},"loc":{"start":{"line":115,"column":16},"end":{"line":115,"column":29}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":138,"column":27},"end":{"line":138,"column":28}},"loc":{"start":{"line":139,"column":16},"end":{"line":139,"column":null}}},"7":{"name":"InspectDrawer","decl":{"start":{"line":159,"column":9},"end":{"line":159,"column":23}},"loc":{"start":{"line":159,"column":54},"end":{"line":206,"column":null}}},"8":{"name":"(anonymous_10)","decl":{"start":{"line":175,"column":56},"end":{"line":175,"column":57}},"loc":{"start":{"line":175,"column":63},"end":{"line":175,"column":87}}},"9":{"name":"(anonymous_11)","decl":{"start":{"line":175,"column":92},"end":{"line":175,"column":93}},"loc":{"start":{"line":175,"column":99},"end":{"line":175,"column":107}}},"10":{"name":"(anonymous_12)","decl":{"start":{"line":176,"column":75},"end":{"line":176,"column":76}},"loc":{"start":{"line":176,"column":85},"end":{"line":176,"column":96}}},"11":{"name":"(anonymous_13)","decl":{"start":{"line":193,"column":35},"end":{"line":193,"column":36}},"loc":{"start":{"line":194,"column":10},"end":{"line":194,"column":27}}},"12":{"name":"OutputPane","decl":{"start":{"line":218,"column":16},"end":{"line":218,"column":27}},"loc":{"start":{"line":226,"column":18},"end":{"line":574,"column":null}}},"13":{"name":"(anonymous_15)","decl":{"start":{"line":236,"column":12},"end":{"line":236,"column":null}},"loc":{"start":{"line":236,"column":12},"end":{"line":238,"column":5}}},"14":{"name":"(anonymous_16)","decl":{"start":{"line":243,"column":10},"end":{"line":243,"column":11}},"loc":{"start":{"line":243,"column":37},"end":{"line":243,"column":null}}},"15":{"name":"handleSend","decl":{"start":{"line":245,"column":17},"end":{"line":245,"column":null}},"loc":{"start":{"line":245,"column":17},"end":{"line":269,"column":null}}},"16":{"name":"(anonymous_18)","decl":{"start":{"line":249,"column":16},"end":{"line":249,"column":17}},"loc":{"start":{"line":249,"column":26},"end":{"line":249,"column":null}}},"17":{"name":"(anonymous_19)","decl":{"start":{"line":254,"column":18},"end":{"line":254,"column":19}},"loc":{"start":{"line":254,"column":28},"end":{"line":257,"column":null}}},"18":{"name":"(anonymous_20)","decl":{"start":{"line":259,"column":18},"end":{"line":259,"column":19}},"loc":{"start":{"line":259,"column":28},"end":{"line":265,"column":null}}},"19":{"name":"handleAction","decl":{"start":{"line":271,"column":17},"end":{"line":271,"column":30}},"loc":{"start":{"line":271,"column":73},"end":{"line":298,"column":null}}},"20":{"name":"(anonymous_22)","decl":{"start":{"line":275,"column":16},"end":{"line":275,"column":17}},"loc":{"start":{"line":275,"column":26},"end":{"line":275,"column":null}}},"21":{"name":"(anonymous_23)","decl":{"start":{"line":280,"column":18},"end":{"line":280,"column":19}},"loc":{"start":{"line":280,"column":28},"end":{"line":283,"column":null}}},"22":{"name":"(anonymous_24)","decl":{"start":{"line":287,"column":18},"end":{"line":287,"column":19}},"loc":{"start":{"line":287,"column":28},"end":{"line":293,"column":null}}},"23":{"name":"(anonymous_25)","decl":{"start":{"line":301,"column":12},"end":{"line":301,"column":null}},"loc":{"start":{"line":301,"column":12},"end":{"line":315,"column":5}}},"24":{"name":"handleKeyDown","decl":{"start":{"line":317,"column":11},"end":{"line":317,"column":25}},"loc":{"start":{"line":317,"column":62},"end":{"line":322,"column":null}}},"25":{"name":"(anonymous_27)","decl":{"start":{"line":341,"column":23},"end":{"line":341,"column":null}},"loc":{"start":{"line":341,"column":23},"end":{"line":344,"column":null}}},"26":{"name":"(anonymous_28)","decl":{"start":{"line":342,"column":32},"end":{"line":342,"column":33}},"loc":{"start":{"line":342,"column":39},"end":{"line":342,"column":null}}},"27":{"name":"(anonymous_29)","decl":{"start":{"line":354,"column":23},"end":{"line":354,"column":null}},"loc":{"start":{"line":354,"column":23},"end":{"line":357,"column":null}}},"28":{"name":"(anonymous_30)","decl":{"start":{"line":355,"column":31},"end":{"line":355,"column":32}},"loc":{"start":{"line":355,"column":38},"end":{"line":355,"column":null}}},"29":{"name":"(anonymous_31)","decl":{"start":{"line":406,"column":22},"end":{"line":406,"column":23}},"loc":{"start":{"line":406,"column":28},"end":{"line":488,"column":null}}},"30":{"name":"(anonymous_32)","decl":{"start":{"line":424,"column":36},"end":{"line":424,"column":37}},"loc":{"start":{"line":424,"column":47},"end":{"line":424,"column":null}}},"31":{"name":"(anonymous_33)","decl":{"start":{"line":435,"column":43},"end":{"line":435,"column":44}},"loc":{"start":{"line":435,"column":50},"end":{"line":435,"column":73}}},"32":{"name":"(anonymous_34)","decl":{"start":{"line":435,"column":79},"end":{"line":435,"column":80}},"loc":{"start":{"line":436,"column":24},"end":{"line":437,"column":null}}},"33":{"name":"(anonymous_35)","decl":{"start":{"line":438,"column":35},"end":{"line":438,"column":41}},"loc":{"start":{"line":438,"column":41},"end":{"line":438,"column":null}}},"34":{"name":"(anonymous_36)","decl":{"start":{"line":470,"column":39},"end":{"line":470,"column":40}},"loc":{"start":{"line":471,"column":22},"end":{"line":472,"column":null}}},"35":{"name":"(anonymous_37)","decl":{"start":{"line":541,"column":22},"end":{"line":541,"column":23}},"loc":{"start":{"line":541,"column":29},"end":{"line":541,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":12},"end":{"line":43,"column":null}},"type":"binary-expr","locations":[{"start":{"line":43,"column":12},"end":{"line":43,"column":22}},{"start":{"line":43,"column":22},"end":{"line":43,"column":null}}]},"1":{"loc":{"start":{"line":67,"column":16},"end":{"line":67,"column":null}},"type":"binary-expr","locations":[{"start":{"line":67,"column":16},"end":{"line":67,"column":24}},{"start":{"line":67,"column":28},"end":{"line":67,"column":null}}]},"2":{"loc":{"start":{"line":68,"column":4},"end":{"line":68,"column":null}},"type":"if","locations":[{"start":{"line":68,"column":4},"end":{"line":68,"column":null}}]},"3":{"loc":{"start":{"line":68,"column":43},"end":{"line":68,"column":77}},"type":"binary-expr","locations":[{"start":{"line":68,"column":43},"end":{"line":68,"column":50}},{"start":{"line":68,"column":54},"end":{"line":68,"column":62}},{"start":{"line":68,"column":66},"end":{"line":68,"column":77}}]},"4":{"loc":{"start":{"line":71,"column":16},"end":{"line":71,"column":null}},"type":"binary-expr","locations":[{"start":{"line":71,"column":16},"end":{"line":71,"column":32}},{"start":{"line":71,"column":36},"end":{"line":71,"column":null}}]},"5":{"loc":{"start":{"line":87,"column":19},"end":{"line":87,"column":null}},"type":"binary-expr","locations":[{"start":{"line":87,"column":19},"end":{"line":87,"column":27}},{"start":{"line":87,"column":31},"end":{"line":87,"column":40}},{"start":{"line":87,"column":44},"end":{"line":87,"column":null}}]},"6":{"loc":{"start":{"line":89,"column":17},"end":{"line":89,"column":27}},"type":"binary-expr","locations":[{"start":{"line":89,"column":17},"end":{"line":89,"column":27}}]},"7":{"loc":{"start":{"line":96,"column":17},"end":{"line":96,"column":46}},"type":"binary-expr","locations":[{"start":{"line":96,"column":17},"end":{"line":96,"column":32}},{"start":{"line":96,"column":32},"end":{"line":96,"column":46}}]},"8":{"loc":{"start":{"line":97,"column":17},"end":{"line":97,"column":55}},"type":"binary-expr","locations":[{"start":{"line":97,"column":17},"end":{"line":97,"column":33}},{"start":{"line":97,"column":33},"end":{"line":97,"column":55}}]},"9":{"loc":{"start":{"line":167,"column":21},"end":{"line":167,"column":70}},"type":"cond-expr","locations":[{"start":{"line":167,"column":51},"end":{"line":167,"column":59}},{"start":{"line":167,"column":59},"end":{"line":167,"column":70}}]},"10":{"loc":{"start":{"line":169,"column":6},"end":{"line":169,"column":null}},"type":"cond-expr","locations":[{"start":{"line":169,"column":27},"end":{"line":169,"column":54}},{"start":{"line":169,"column":57},"end":{"line":169,"column":null}}]},"11":{"loc":{"start":{"line":175,"column":63},"end":{"line":175,"column":87}},"type":"binary-expr","locations":[{"start":{"line":175,"column":63},"end":{"line":175,"column":71}},{"start":{"line":175,"column":75},"end":{"line":175,"column":87}}]},"12":{"loc":{"start":{"line":176,"column":24},"end":{"line":176,"column":143}},"type":"cond-expr","locations":[{"start":{"line":176,"column":50},"end":{"line":176,"column":141}},{"start":{"line":176,"column":141},"end":{"line":176,"column":143}}]},"13":{"loc":{"start":{"line":222,"column":2},"end":{"line":222,"column":23}},"type":"default-arg","locations":[{"start":{"line":222,"column":19},"end":{"line":222,"column":23}}]},"14":{"loc":{"start":{"line":223,"column":2},"end":{"line":223,"column":19}},"type":"default-arg","locations":[{"start":{"line":223,"column":17},"end":{"line":223,"column":19}}]},"15":{"loc":{"start":{"line":224,"column":2},"end":{"line":224,"column":22}},"type":"default-arg","locations":[{"start":{"line":224,"column":18},"end":{"line":224,"column":22}}]},"16":{"loc":{"start":{"line":247,"column":4},"end":{"line":247,"column":null}},"type":"if","locations":[{"start":{"line":247,"column":4},"end":{"line":247,"column":null}}]},"17":{"loc":{"start":{"line":247,"column":8},"end":{"line":247,"column":30}},"type":"binary-expr","locations":[{"start":{"line":247,"column":8},"end":{"line":247,"column":21}},{"start":{"line":247,"column":21},"end":{"line":247,"column":30}}]},"18":{"loc":{"start":{"line":263,"column":19},"end":{"line":263,"column":null}},"type":"cond-expr","locations":[{"start":{"line":263,"column":42},"end":{"line":263,"column":65}},{"start":{"line":263,"column":68},"end":{"line":263,"column":null}}]},"19":{"loc":{"start":{"line":272,"column":18},"end":{"line":272,"column":null}},"type":"binary-expr","locations":[{"start":{"line":272,"column":18},"end":{"line":272,"column":35}},{"start":{"line":272,"column":35},"end":{"line":272,"column":51}},{"start":{"line":272,"column":51},"end":{"line":272,"column":76}},{"start":{"line":272,"column":76},"end":{"line":272,"column":null}}]},"20":{"loc":{"start":{"line":284,"column":6},"end":{"line":284,"column":null}},"type":"if","locations":[{"start":{"line":284,"column":6},"end":{"line":284,"column":null}}]},"21":{"loc":{"start":{"line":285,"column":31},"end":{"line":285,"column":null}},"type":"binary-expr","locations":[{"start":{"line":285,"column":31},"end":{"line":285,"column":49}},{"start":{"line":285,"column":49},"end":{"line":285,"column":null}}]},"22":{"loc":{"start":{"line":291,"column":19},"end":{"line":291,"column":null}},"type":"cond-expr","locations":[{"start":{"line":291,"column":42},"end":{"line":291,"column":65}},{"start":{"line":291,"column":68},"end":{"line":291,"column":null}}]},"23":{"loc":{"start":{"line":302,"column":4},"end":{"line":309,"column":null}},"type":"if","locations":[{"start":{"line":302,"column":4},"end":{"line":309,"column":null}}]},"24":{"loc":{"start":{"line":303,"column":6},"end":{"line":307,"column":null}},"type":"binary-expr","locations":[{"start":{"line":303,"column":6},"end":{"line":303,"column":28}},{"start":{"line":304,"column":6},"end":{"line":304,"column":null}},{"start":{"line":305,"column":6},"end":{"line":305,"column":null}},{"start":{"line":306,"column":6},"end":{"line":306,"column":null}},{"start":{"line":307,"column":6},"end":{"line":307,"column":null}}]},"25":{"loc":{"start":{"line":318,"column":4},"end":{"line":321,"column":null}},"type":"if","locations":[{"start":{"line":318,"column":4},"end":{"line":321,"column":null}}]},"26":{"loc":{"start":{"line":318,"column":8},"end":{"line":318,"column":40}},"type":"binary-expr","locations":[{"start":{"line":318,"column":8},"end":{"line":318,"column":29}},{"start":{"line":318,"column":29},"end":{"line":318,"column":40}}]},"27":{"loc":{"start":{"line":324,"column":22},"end":{"line":326,"column":null}},"type":"cond-expr","locations":[{"start":{"line":325,"column":6},"end":{"line":325,"column":68}},{"start":{"line":326,"column":6},"end":{"line":326,"column":null}}]},"28":{"loc":{"start":{"line":328,"column":28},"end":{"line":328,"column":69}},"type":"binary-expr","locations":[{"start":{"line":328,"column":28},"end":{"line":328,"column":67}},{"start":{"line":328,"column":67},"end":{"line":328,"column":69}}]},"29":{"loc":{"start":{"line":335,"column":9},"end":{"line":335,"column":null}},"type":"binary-expr","locations":[{"start":{"line":335,"column":9},"end":{"line":335,"column":null}}]},"30":{"loc":{"start":{"line":338,"column":9},"end":{"line":338,"column":null}},"type":"binary-expr","locations":[{"start":{"line":338,"column":9},"end":{"line":338,"column":null}}]},"31":{"loc":{"start":{"line":346,"column":16},"end":{"line":348,"column":null}},"type":"cond-expr","locations":[{"start":{"line":347,"column":20},"end":{"line":347,"column":null}},{"start":{"line":348,"column":20},"end":{"line":348,"column":null}}]},"32":{"loc":{"start":{"line":351,"column":15},"end":{"line":351,"column":40}},"type":"cond-expr","locations":[{"start":{"line":351,"column":30},"end":{"line":351,"column":36}},{"start":{"line":351,"column":36},"end":{"line":351,"column":40}}]},"33":{"loc":{"start":{"line":359,"column":16},"end":{"line":361,"column":null}},"type":"cond-expr","locations":[{"start":{"line":360,"column":20},"end":{"line":360,"column":null}},{"start":{"line":361,"column":20},"end":{"line":361,"column":null}}]},"34":{"loc":{"start":{"line":364,"column":15},"end":{"line":364,"column":39}},"type":"cond-expr","locations":[{"start":{"line":364,"column":29},"end":{"line":364,"column":35}},{"start":{"line":364,"column":35},"end":{"line":364,"column":39}}]},"35":{"loc":{"start":{"line":381,"column":9},"end":{"line":381,"column":34}},"type":"binary-expr","locations":[{"start":{"line":381,"column":9},"end":{"line":381,"column":34}},{"start":{"line":381,"column":34},"end":{"line":381,"column":null}}]},"36":{"loc":{"start":{"line":395,"column":9},"end":{"line":395,"column":34}},"type":"binary-expr","locations":[{"start":{"line":395,"column":9},"end":{"line":395,"column":34}},{"start":{"line":395,"column":34},"end":{"line":395,"column":null}}]},"37":{"loc":{"start":{"line":409,"column":10},"end":{"line":450,"column":null}},"type":"if","locations":[{"start":{"line":409,"column":10},"end":{"line":450,"column":null}}]},"38":{"loc":{"start":{"line":429,"column":17},"end":{"line":429,"column":null}},"type":"binary-expr","locations":[{"start":{"line":429,"column":17},"end":{"line":429,"column":null}}]},"39":{"loc":{"start":{"line":455,"column":33},"end":{"line":455,"column":87}},"type":"cond-expr","locations":[{"start":{"line":455,"column":55},"end":{"line":455,"column":71}},{"start":{"line":455,"column":71},"end":{"line":455,"column":87}}]},"40":{"loc":{"start":{"line":459,"column":18},"end":{"line":461,"column":null}},"type":"cond-expr","locations":[{"start":{"line":460,"column":22},"end":{"line":460,"column":null}},{"start":{"line":461,"column":22},"end":{"line":461,"column":null}}]},"41":{"loc":{"start":{"line":465,"column":17},"end":{"line":465,"column":45}},"type":"binary-expr","locations":[{"start":{"line":465,"column":17},"end":{"line":465,"column":45}},{"start":{"line":465,"column":45},"end":{"line":465,"column":58}},{"start":{"line":465,"column":62},"end":{"line":465,"column":null}}]},"42":{"loc":{"start":{"line":490,"column":9},"end":{"line":490,"column":null}},"type":"binary-expr","locations":[{"start":{"line":490,"column":9},"end":{"line":490,"column":null}}]},"43":{"loc":{"start":{"line":510,"column":7},"end":{"line":510,"column":23}},"type":"binary-expr","locations":[{"start":{"line":510,"column":7},"end":{"line":510,"column":23}},{"start":{"line":510,"column":23},"end":{"line":510,"column":null}}]},"44":{"loc":{"start":{"line":515,"column":7},"end":{"line":515,"column":22}},"type":"binary-expr","locations":[{"start":{"line":515,"column":7},"end":{"line":515,"column":22}},{"start":{"line":515,"column":22},"end":{"line":515,"column":null}}]},"45":{"loc":{"start":{"line":531,"column":11},"end":{"line":531,"column":null}},"type":"binary-expr","locations":[{"start":{"line":531,"column":11},"end":{"line":531,"column":null}}]},"46":{"loc":{"start":{"line":534,"column":15},"end":{"line":534,"column":null}},"type":"cond-expr","locations":[{"start":{"line":534,"column":53},"end":{"line":534,"column":87}},{"start":{"line":534,"column":90},"end":{"line":534,"column":null}}]},"47":{"loc":{"start":{"line":550,"column":22},"end":{"line":550,"column":null}},"type":"binary-expr","locations":[{"start":{"line":550,"column":22},"end":{"line":550,"column":39}},{"start":{"line":550,"column":39},"end":{"line":550,"column":null}}]}},"s":{"0":18,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":2,"27":18,"28":18,"29":18,"30":18,"31":18,"32":18,"33":18,"34":18,"35":18,"36":18,"37":18,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":18,"73":18,"74":18,"75":0,"76":0,"77":0,"78":0,"79":0,"80":18,"81":18,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":18,"13":18,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":18,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0,0,0],"4":[0,0],"5":[0,0,0],"6":[0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[18],"14":[18],"15":[18],"16":[0],"17":[0,0],"18":[0,0],"19":[0,0,0,0],"20":[0],"21":[0,0],"22":[0,0],"23":[18],"24":[18,18,0,0,0],"25":[0],"26":[0,0],"27":[1,17],"28":[18,18],"29":[18],"30":[18],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[18,18],"36":[18,18],"37":[0],"38":[0],"39":[0,0],"40":[0,0],"41":[0,0,0],"42":[18],"43":[18,0],"44":[18,0],"45":[18],"46":[0,0],"47":[18,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/SourcePane.tsx","statementMap":{"0":{"start":{"line":21,"column":16},"end":{"line":21,"column":27}},"1":{"start":{"line":3,"column":26},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":30},"end":{"line":4,"column":null}},"3":{"start":{"line":34,"column":22},"end":{"line":34,"column":null}},"4":{"start":{"line":97,"column":33},"end":{"line":97,"column":null}}},"fnMap":{"0":{"name":"SourcePane","decl":{"start":{"line":21,"column":16},"end":{"line":21,"column":27}},"loc":{"start":{"line":33,"column":18},"end":{"line":116,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":97,"column":27},"end":{"line":97,"column":33}},"loc":{"start":{"line":97,"column":33},"end":{"line":97,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":30,"column":2},"end":{"line":30,"column":24}},"type":"default-arg","locations":[{"start":{"line":30,"column":19},"end":{"line":30,"column":24}}]},"1":{"loc":{"start":{"line":34,"column":22},"end":{"line":34,"column":null}},"type":"cond-expr","locations":[{"start":{"line":34,"column":39},"end":{"line":34,"column":89}},{"start":{"line":34,"column":89},"end":{"line":34,"column":null}}]},"2":{"loc":{"start":{"line":41,"column":9},"end":{"line":41,"column":24}},"type":"binary-expr","locations":[{"start":{"line":41,"column":9},"end":{"line":41,"column":24}}]},"3":{"loc":{"start":{"line":59,"column":10},"end":{"line":106,"column":11}},"type":"cond-expr","locations":[{"start":{"line":59,"column":10},"end":{"line":106,"column":11}}]},"4":{"loc":{"start":{"line":65,"column":13},"end":{"line":65,"column":35}},"type":"binary-expr","locations":[{"start":{"line":65,"column":13},"end":{"line":65,"column":35}}]},"5":{"loc":{"start":{"line":80,"column":16},"end":{"line":96,"column":17}},"type":"cond-expr","locations":[{"start":{"line":80,"column":16},"end":{"line":96,"column":17}}]}},"s":{"0":0,"1":1,"2":1,"3":0,"4":0},"f":{"0":0,"1":0},"b":{"0":[0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/SplitPane.tsx","statementMap":{"0":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"1":{"start":{"line":3,"column":73},"end":{"line":3,"column":null}},"2":{"start":{"line":20,"column":40},"end":{"line":20,"column":null}},"3":{"start":{"line":21,"column":34},"end":{"line":21,"column":null}},"4":{"start":{"line":22,"column":36},"end":{"line":22,"column":null}},"5":{"start":{"line":23,"column":23},"end":{"line":23,"column":null}},"6":{"start":{"line":24,"column":19},"end":{"line":24,"column":null}},"7":{"start":{"line":26,"column":2},"end":{"line":32,"column":null}},"8":{"start":{"line":27,"column":15},"end":{"line":27,"column":null}},"9":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"10":{"start":{"line":29,"column":20},"end":{"line":29,"column":null}},"11":{"start":{"line":29,"column":48},"end":{"line":29,"column":null}},"12":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"13":{"start":{"line":31,"column":4},"end":{"line":31,"column":null}},"14":{"start":{"line":31,"column":17},"end":{"line":31,"column":null}},"15":{"start":{"line":34,"column":22},"end":{"line":59,"column":null}},"16":{"start":{"line":36,"column":6},"end":{"line":36,"column":null}},"17":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"18":{"start":{"line":39,"column":26},"end":{"line":44,"column":null}},"19":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"20":{"start":{"line":40,"column":56},"end":{"line":40,"column":null}},"21":{"start":{"line":41,"column":21},"end":{"line":41,"column":null}},"22":{"start":{"line":42,"column":24},"end":{"line":42,"column":null}},"23":{"start":{"line":43,"column":8},"end":{"line":43,"column":null}},"24":{"start":{"line":46,"column":24},"end":{"line":52,"column":null}},"25":{"start":{"line":47,"column":8},"end":{"line":47,"column":null}},"26":{"start":{"line":48,"column":8},"end":{"line":48,"column":null}},"27":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"28":{"start":{"line":50,"column":8},"end":{"line":50,"column":null}},"29":{"start":{"line":51,"column":8},"end":{"line":51,"column":null}},"30":{"start":{"line":54,"column":6},"end":{"line":54,"column":null}},"31":{"start":{"line":55,"column":6},"end":{"line":55,"column":null}},"32":{"start":{"line":56,"column":6},"end":{"line":56,"column":null}},"33":{"start":{"line":57,"column":6},"end":{"line":57,"column":null}},"34":{"start":{"line":62,"column":2},"end":{"line":92,"column":null}},"35":{"start":{"line":68,"column":27},"end":{"line":68,"column":null}},"36":{"start":{"line":78,"column":27},"end":{"line":78,"column":null}}},"fnMap":{"0":{"name":"SplitPane","decl":{"start":{"line":13,"column":16},"end":{"line":13,"column":26}},"loc":{"start":{"line":19,"column":17},"end":{"line":113,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":26,"column":12},"end":{"line":26,"column":null}},"loc":{"start":{"line":26,"column":12},"end":{"line":32,"column":5}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":29,"column":20},"end":{"line":29,"column":21}},"loc":{"start":{"line":29,"column":48},"end":{"line":29,"column":null}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":31,"column":11},"end":{"line":31,"column":17}},"loc":{"start":{"line":31,"column":17},"end":{"line":31,"column":null}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":35,"column":4},"end":{"line":35,"column":5}},"loc":{"start":{"line":35,"column":5},"end":{"line":58,"column":null}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":39,"column":26},"end":{"line":39,"column":27}},"loc":{"start":{"line":39,"column":27},"end":{"line":44,"column":null}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":46,"column":24},"end":{"line":46,"column":null}},"loc":{"start":{"line":46,"column":24},"end":{"line":52,"column":null}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":68,"column":21},"end":{"line":68,"column":27}},"loc":{"start":{"line":68,"column":27},"end":{"line":68,"column":null}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":78,"column":21},"end":{"line":78,"column":27}},"loc":{"start":{"line":78,"column":27},"end":{"line":78,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":25}},"type":"default-arg","locations":[{"start":{"line":16,"column":23},"end":{"line":16,"column":25}}]},"1":{"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":21}},"type":"default-arg","locations":[{"start":{"line":17,"column":19},"end":{"line":17,"column":21}}]},"2":{"loc":{"start":{"line":18,"column":2},"end":{"line":18,"column":21}},"type":"default-arg","locations":[{"start":{"line":18,"column":19},"end":{"line":18,"column":21}}]},"3":{"loc":{"start":{"line":40,"column":8},"end":{"line":40,"column":null}},"type":"if","locations":[{"start":{"line":40,"column":8},"end":{"line":40,"column":null}}]},"4":{"loc":{"start":{"line":40,"column":12},"end":{"line":40,"column":54}},"type":"binary-expr","locations":[{"start":{"line":40,"column":12},"end":{"line":40,"column":29}},{"start":{"line":40,"column":33},"end":{"line":40,"column":54}}]},"5":{"loc":{"start":{"line":62,"column":2},"end":{"line":92,"column":null}},"type":"if","locations":[{"start":{"line":62,"column":2},"end":{"line":92,"column":null}}]},"6":{"loc":{"start":{"line":70,"column":14},"end":{"line":72,"column":null}},"type":"cond-expr","locations":[{"start":{"line":71,"column":18},"end":{"line":71,"column":null}},{"start":{"line":72,"column":18},"end":{"line":72,"column":null}}]},"7":{"loc":{"start":{"line":80,"column":14},"end":{"line":82,"column":null}},"type":"cond-expr","locations":[{"start":{"line":81,"column":18},"end":{"line":81,"column":null}},{"start":{"line":82,"column":18},"end":{"line":82,"column":null}}]},"8":{"loc":{"start":{"line":89,"column":55},"end":{"line":89,"column":null}},"type":"cond-expr","locations":[{"start":{"line":89,"column":78},"end":{"line":89,"column":85}},{"start":{"line":89,"column":85},"end":{"line":89,"column":null}}]}},"s":{"0":0,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0,0],"5":[0],"6":[0,0],"7":[0,0],"8":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/StudyActionBar.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/StudyActionBar.tsx","statementMap":{"0":{"start":{"line":56,"column":16},"end":{"line":56,"column":31}},"1":{"start":{"line":13,"column":29},"end":{"line":46,"column":null}},"2":{"start":{"line":63,"column":21},"end":{"line":63,"column":null}},"3":{"start":{"line":77,"column":27},"end":{"line":77,"column":48}},"4":{"start":{"line":78,"column":28},"end":{"line":78,"column":null}},"5":{"start":{"line":79,"column":10},"end":{"line":81,"column":null}},"6":{"start":{"line":82,"column":29},"end":{"line":82,"column":null}}},"fnMap":{"0":{"name":"StudyActionBar","decl":{"start":{"line":56,"column":16},"end":{"line":56,"column":31}},"loc":{"start":{"line":62,"column":22},"end":{"line":127,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":76,"column":21},"end":{"line":76,"column":22}},"loc":{"start":{"line":76,"column":22},"end":{"line":123,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":82,"column":23},"end":{"line":82,"column":29}},"loc":{"start":{"line":82,"column":29},"end":{"line":82,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":61,"column":2},"end":{"line":61,"column":23}},"type":"default-arg","locations":[{"start":{"line":61,"column":19},"end":{"line":61,"column":23}}]},"1":{"loc":{"start":{"line":63,"column":21},"end":{"line":63,"column":null}},"type":"binary-expr","locations":[{"start":{"line":63,"column":21},"end":{"line":63,"column":32}},{"start":{"line":63,"column":32},"end":{"line":63,"column":44}},{"start":{"line":63,"column":44},"end":{"line":63,"column":null}}]},"2":{"loc":{"start":{"line":78,"column":28},"end":{"line":78,"column":null}},"type":"binary-expr","locations":[{"start":{"line":78,"column":28},"end":{"line":78,"column":40}},{"start":{"line":78,"column":40},"end":{"line":78,"column":null}}]},"3":{"loc":{"start":{"line":84,"column":21},"end":{"line":84,"column":null}},"type":"cond-expr","locations":[{"start":{"line":84,"column":38},"end":{"line":84,"column":43}},{"start":{"line":84,"column":46},"end":{"line":84,"column":null}}]},"4":{"loc":{"start":{"line":86,"column":16},"end":{"line":88,"column":null}},"type":"cond-expr","locations":[{"start":{"line":87,"column":20},"end":{"line":87,"column":null}},{"start":{"line":88,"column":20},"end":{"line":88,"column":null}}]},"5":{"loc":{"start":{"line":93,"column":18},"end":{"line":113,"column":19}},"type":"cond-expr","locations":[{"start":{"line":93,"column":18},"end":{"line":113,"column":19}}]}},"s":{"0":18,"1":2,"2":18,"3":144,"4":144,"5":144,"6":0},"f":{"0":18,"1":144,"2":0},"b":{"0":[0],"1":[18,18,18],"2":[144,0],"3":[144,0],"4":[0,144],"5":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/StudyOutput.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/study/StudyOutput.tsx","statementMap":{"0":{"start":{"line":14,"column":16},"end":{"line":14,"column":28}},"1":{"start":{"line":3,"column":34},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":26},"end":{"line":4,"column":null}},"3":{"start":{"line":6,"column":29},"end":{"line":6,"column":null}},"4":{"start":{"line":15,"column":40},"end":{"line":15,"column":null}},"5":{"start":{"line":17,"column":20},"end":{"line":17,"column":null}},"6":{"start":{"line":18,"column":23},"end":{"line":18,"column":null}},"7":{"start":{"line":51,"column":27},"end":{"line":51,"column":null}},"8":{"start":{"line":51,"column":49},"end":{"line":51,"column":null}},"9":{"start":{"line":69,"column":16},"end":{"line":69,"column":38}},"10":{"start":{"line":80,"column":12},"end":{"line":80,"column":34}},"11":{"start":{"line":97,"column":16},"end":{"line":100,"column":null}},"12":{"start":{"line":99,"column":16},"end":{"line":99,"column":null}},"13":{"start":{"line":101,"column":16},"end":{"line":101,"column":null}},"14":{"start":{"line":101,"column":34},"end":{"line":101,"column":null}},"15":{"start":{"line":102,"column":19},"end":{"line":102,"column":null}},"16":{"start":{"line":103,"column":18},"end":{"line":103,"column":null}},"17":{"start":{"line":103,"column":38},"end":{"line":103,"column":null}},"18":{"start":{"line":104,"column":21},"end":{"line":104,"column":null}},"19":{"start":{"line":104,"column":39},"end":{"line":104,"column":null}},"20":{"start":{"line":105,"column":24},"end":{"line":111,"column":null}},"21":{"start":{"line":112,"column":2},"end":{"line":112,"column":null}},"22":{"start":{"line":116,"column":34},"end":{"line":116,"column":null}},"23":{"start":{"line":117,"column":34},"end":{"line":117,"column":null}},"24":{"start":{"line":118,"column":47},"end":{"line":118,"column":null}},"25":{"start":{"line":118,"column":61},"end":{"line":118,"column":85}},"26":{"start":{"line":130,"column":27},"end":{"line":130,"column":null}},"27":{"start":{"line":131,"column":30},"end":{"line":131,"column":null}},"28":{"start":{"line":132,"column":31},"end":{"line":132,"column":null}},"29":{"start":{"line":133,"column":12},"end":{"line":135,"column":null}},"30":{"start":{"line":136,"column":31},"end":{"line":136,"column":null}},"31":{"start":{"line":162,"column":37},"end":{"line":162,"column":56}},"32":{"start":{"line":180,"column":27},"end":{"line":180,"column":null}},"33":{"start":{"line":180,"column":46},"end":{"line":180,"column":null}},"34":{"start":{"line":202,"column":20},"end":{"line":202,"column":null}},"35":{"start":{"line":202,"column":58},"end":{"line":202,"column":null}},"36":{"start":{"line":203,"column":2},"end":{"line":207,"column":null}},"37":{"start":{"line":211,"column":8},"end":{"line":211,"column":30}},"38":{"start":{"line":220,"column":32},"end":{"line":220,"column":null}},"39":{"start":{"line":221,"column":20},"end":{"line":221,"column":null}},"40":{"start":{"line":221,"column":61},"end":{"line":221,"column":null}},"41":{"start":{"line":224,"column":19},"end":{"line":224,"column":null}},"42":{"start":{"line":225,"column":4},"end":{"line":225,"column":null}},"43":{"start":{"line":225,"column":30},"end":{"line":225,"column":null}},"44":{"start":{"line":226,"column":4},"end":{"line":226,"column":null}},"45":{"start":{"line":234,"column":27},"end":{"line":234,"column":null}},"46":{"start":{"line":234,"column":49},"end":{"line":234,"column":null}},"47":{"start":{"line":240,"column":33},"end":{"line":240,"column":66}},"48":{"start":{"line":248,"column":2},"end":{"line":255,"column":null}},"49":{"start":{"line":260,"column":8},"end":{"line":260,"column":21}},"50":{"start":{"line":272,"column":16},"end":{"line":272,"column":null}},"51":{"start":{"line":272,"column":47},"end":{"line":272,"column":null}},"52":{"start":{"line":276,"column":8},"end":{"line":276,"column":19}}},"fnMap":{"0":{"name":"StudyOutput","decl":{"start":{"line":14,"column":16},"end":{"line":14,"column":28}},"loc":{"start":{"line":14,"column":82},"end":{"line":86,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":51,"column":21},"end":{"line":51,"column":27}},"loc":{"start":{"line":51,"column":27},"end":{"line":51,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":51,"column":42},"end":{"line":51,"column":43}},"loc":{"start":{"line":51,"column":49},"end":{"line":51,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":68,"column":36},"end":{"line":68,"column":37}},"loc":{"start":{"line":69,"column":16},"end":{"line":69,"column":38}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":79,"column":32},"end":{"line":79,"column":33}},"loc":{"start":{"line":80,"column":12},"end":{"line":80,"column":34}}},"5":{"name":"parseQuizQuestion","decl":{"start":{"line":96,"column":9},"end":{"line":96,"column":27}},"loc":{"start":{"line":96,"column":38},"end":{"line":113,"column":null}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":99,"column":9},"end":{"line":99,"column":10}},"loc":{"start":{"line":99,"column":16},"end":{"line":99,"column":null}}},"7":{"name":"(anonymous_9)","decl":{"start":{"line":101,"column":27},"end":{"line":101,"column":28}},"loc":{"start":{"line":101,"column":34},"end":{"line":101,"column":null}}},"8":{"name":"(anonymous_10)","decl":{"start":{"line":103,"column":31},"end":{"line":103,"column":32}},"loc":{"start":{"line":103,"column":38},"end":{"line":103,"column":null}}},"9":{"name":"(anonymous_11)","decl":{"start":{"line":104,"column":32},"end":{"line":104,"column":33}},"loc":{"start":{"line":104,"column":39},"end":{"line":104,"column":null}}},"10":{"name":"QuizQuestion","decl":{"start":{"line":115,"column":9},"end":{"line":115,"column":22}},"loc":{"start":{"line":115,"column":68},"end":{"line":199,"column":null}}},"11":{"name":"(anonymous_13)","decl":{"start":{"line":118,"column":55},"end":{"line":118,"column":61}},"loc":{"start":{"line":118,"column":61},"end":{"line":118,"column":85}}},"12":{"name":"(anonymous_14)","decl":{"start":{"line":129,"column":23},"end":{"line":129,"column":24}},"loc":{"start":{"line":129,"column":24},"end":{"line":158,"column":null}}},"13":{"name":"(anonymous_15)","decl":{"start":{"line":136,"column":25},"end":{"line":136,"column":31}},"loc":{"start":{"line":136,"column":31},"end":{"line":136,"column":null}}},"14":{"name":"(anonymous_16)","decl":{"start":{"line":162,"column":31},"end":{"line":162,"column":37}},"loc":{"start":{"line":162,"column":37},"end":{"line":162,"column":56}}},"15":{"name":"(anonymous_17)","decl":{"start":{"line":180,"column":21},"end":{"line":180,"column":27}},"loc":{"start":{"line":180,"column":27},"end":{"line":180,"column":null}}},"16":{"name":"(anonymous_18)","decl":{"start":{"line":180,"column":39},"end":{"line":180,"column":40}},"loc":{"start":{"line":180,"column":46},"end":{"line":180,"column":null}}},"17":{"name":"QuizOutput","decl":{"start":{"line":201,"column":9},"end":{"line":201,"column":20}},"loc":{"start":{"line":201,"column":46},"end":{"line":215,"column":null}}},"18":{"name":"(anonymous_20)","decl":{"start":{"line":202,"column":51},"end":{"line":202,"column":52}},"loc":{"start":{"line":202,"column":58},"end":{"line":202,"column":null}}},"19":{"name":"(anonymous_21)","decl":{"start":{"line":210,"column":21},"end":{"line":210,"column":22}},"loc":{"start":{"line":211,"column":8},"end":{"line":211,"column":30}}},"20":{"name":"OralOutput","decl":{"start":{"line":219,"column":9},"end":{"line":219,"column":20}},"loc":{"start":{"line":219,"column":92},"end":{"line":267,"column":null}}},"21":{"name":"(anonymous_23)","decl":{"start":{"line":221,"column":54},"end":{"line":221,"column":55}},"loc":{"start":{"line":221,"column":61},"end":{"line":221,"column":null}}},"22":{"name":"handleSubmit","decl":{"start":{"line":223,"column":11},"end":{"line":223,"column":24}},"loc":{"start":{"line":223,"column":57},"end":{"line":227,"column":null}}},"23":{"name":"answerBox","decl":{"start":{"line":229,"column":11},"end":{"line":229,"column":21}},"loc":{"start":{"line":229,"column":54},"end":{"line":246,"column":null}}},"24":{"name":"(anonymous_26)","decl":{"start":{"line":234,"column":20},"end":{"line":234,"column":21}},"loc":{"start":{"line":234,"column":27},"end":{"line":234,"column":null}}},"25":{"name":"(anonymous_27)","decl":{"start":{"line":234,"column":38},"end":{"line":234,"column":39}},"loc":{"start":{"line":234,"column":49},"end":{"line":234,"column":null}}},"26":{"name":"(anonymous_28)","decl":{"start":{"line":240,"column":27},"end":{"line":240,"column":33}},"loc":{"start":{"line":240,"column":33},"end":{"line":240,"column":66}}},"27":{"name":"(anonymous_29)","decl":{"start":{"line":259,"column":21},"end":{"line":259,"column":22}},"loc":{"start":{"line":260,"column":8},"end":{"line":260,"column":21}}},"28":{"name":"QuestionsOutput","decl":{"start":{"line":271,"column":9},"end":{"line":271,"column":25}},"loc":{"start":{"line":271,"column":51},"end":{"line":282,"column":null}}},"29":{"name":"(anonymous_31)","decl":{"start":{"line":272,"column":40},"end":{"line":272,"column":41}},"loc":{"start":{"line":272,"column":47},"end":{"line":272,"column":null}}},"30":{"name":"(anonymous_32)","decl":{"start":{"line":275,"column":17},"end":{"line":275,"column":18}},"loc":{"start":{"line":276,"column":8},"end":{"line":276,"column":19}}}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":20},"end":{"line":17,"column":null}},"type":"binary-expr","locations":[{"start":{"line":17,"column":20},"end":{"line":17,"column":33}},{"start":{"line":17,"column":37},"end":{"line":17,"column":null}}]},"1":{"loc":{"start":{"line":22,"column":7},"end":{"line":22,"column":21}},"type":"binary-expr","locations":[{"start":{"line":22,"column":7},"end":{"line":22,"column":21}},{"start":{"line":22,"column":21},"end":{"line":22,"column":null}}]},"2":{"loc":{"start":{"line":28,"column":7},"end":{"line":28,"column":21}},"type":"binary-expr","locations":[{"start":{"line":28,"column":7},"end":{"line":28,"column":21}},{"start":{"line":28,"column":21},"end":{"line":28,"column":37}},{"start":{"line":28,"column":37},"end":{"line":28,"column":null}}]},"3":{"loc":{"start":{"line":37,"column":7},"end":{"line":45,"column":null}},"type":"cond-expr","locations":[{"start":{"line":38,"column":8},"end":{"line":39,"column":23}},{"start":{"line":39,"column":10},"end":{"line":45,"column":null}}]},"4":{"loc":{"start":{"line":37,"column":7},"end":{"line":37,"column":null}},"type":"binary-expr","locations":[{"start":{"line":37,"column":7},"end":{"line":37,"column":20}},{"start":{"line":37,"column":20},"end":{"line":37,"column":null}}]},"5":{"loc":{"start":{"line":39,"column":10},"end":{"line":45,"column":null}},"type":"cond-expr","locations":[{"start":{"line":40,"column":8},"end":{"line":41,"column":23}},{"start":{"line":41,"column":10},"end":{"line":45,"column":null}}]},"6":{"loc":{"start":{"line":39,"column":10},"end":{"line":39,"column":null}},"type":"binary-expr","locations":[{"start":{"line":39,"column":10},"end":{"line":39,"column":23}},{"start":{"line":39,"column":23},"end":{"line":39,"column":null}}]},"7":{"loc":{"start":{"line":41,"column":10},"end":{"line":45,"column":null}},"type":"cond-expr","locations":[{"start":{"line":42,"column":8},"end":{"line":43,"column":null}},{"start":{"line":43,"column":10},"end":{"line":45,"column":null}}]},"8":{"loc":{"start":{"line":41,"column":10},"end":{"line":41,"column":null}},"type":"binary-expr","locations":[{"start":{"line":41,"column":10},"end":{"line":41,"column":23}},{"start":{"line":41,"column":23},"end":{"line":41,"column":null}}]},"9":{"loc":{"start":{"line":43,"column":10},"end":{"line":45,"column":null}},"type":"cond-expr","locations":[{"start":{"line":44,"column":8},"end":{"line":45,"column":null}},{"start":{"line":45,"column":10},"end":{"line":45,"column":null}}]},"10":{"loc":{"start":{"line":48,"column":7},"end":{"line":48,"column":23}},"type":"binary-expr","locations":[{"start":{"line":48,"column":7},"end":{"line":48,"column":23}},{"start":{"line":48,"column":23},"end":{"line":48,"column":null}}]},"11":{"loc":{"start":{"line":55,"column":57},"end":{"line":55,"column":88}},"type":"cond-expr","locations":[{"start":{"line":55,"column":71},"end":{"line":55,"column":85}},{"start":{"line":55,"column":85},"end":{"line":55,"column":88}}]},"12":{"loc":{"start":{"line":63,"column":13},"end":{"line":63,"column":43}},"type":"cond-expr","locations":[{"start":{"line":63,"column":27},"end":{"line":63,"column":36}},{"start":{"line":63,"column":36},"end":{"line":63,"column":43}}]},"13":{"loc":{"start":{"line":64,"column":13},"end":{"line":64,"column":null}},"type":"cond-expr","locations":[{"start":{"line":64,"column":45},"end":{"line":64,"column":51}},{"start":{"line":64,"column":51},"end":{"line":64,"column":null}}]},"14":{"loc":{"start":{"line":66,"column":11},"end":{"line":66,"column":null}},"type":"binary-expr","locations":[{"start":{"line":66,"column":11},"end":{"line":66,"column":null}}]},"15":{"loc":{"start":{"line":77,"column":7},"end":{"line":77,"column":39}},"type":"binary-expr","locations":[{"start":{"line":77,"column":7},"end":{"line":77,"column":39}},{"start":{"line":77,"column":39},"end":{"line":77,"column":null}}]},"16":{"loc":{"start":{"line":102,"column":19},"end":{"line":102,"column":null}},"type":"cond-expr","locations":[{"start":{"line":102,"column":27},"end":{"line":102,"column":64}},{"start":{"line":102,"column":64},"end":{"line":102,"column":null}}]},"17":{"loc":{"start":{"line":105,"column":24},"end":{"line":111,"column":null}},"type":"cond-expr","locations":[{"start":{"line":106,"column":6},"end":{"line":110,"column":null}},{"start":{"line":111,"column":6},"end":{"line":111,"column":null}}]},"18":{"loc":{"start":{"line":128,"column":8},"end":{"line":177,"column":63}},"type":"cond-expr","locations":[{"start":{"line":128,"column":8},"end":{"line":177,"column":63}}]},"19":{"loc":{"start":{"line":136,"column":31},"end":{"line":136,"column":null}},"type":"binary-expr","locations":[{"start":{"line":136,"column":31},"end":{"line":136,"column":44}},{"start":{"line":136,"column":44},"end":{"line":136,"column":null}}]},"20":{"loc":{"start":{"line":139,"column":18},"end":{"line":145,"column":null}},"type":"cond-expr","locations":[{"start":{"line":140,"column":22},"end":{"line":140,"column":null}},{"start":{"line":141,"column":22},"end":{"line":145,"column":null}}]},"21":{"loc":{"start":{"line":139,"column":18},"end":{"line":139,"column":null}},"type":"binary-expr","locations":[{"start":{"line":139,"column":18},"end":{"line":139,"column":30}},{"start":{"line":139,"column":30},"end":{"line":139,"column":null}}]},"22":{"loc":{"start":{"line":141,"column":22},"end":{"line":145,"column":null}},"type":"cond-expr","locations":[{"start":{"line":142,"column":24},"end":{"line":142,"column":null}},{"start":{"line":143,"column":24},"end":{"line":145,"column":null}}]},"23":{"loc":{"start":{"line":141,"column":22},"end":{"line":141,"column":null}},"type":"binary-expr","locations":[{"start":{"line":141,"column":22},"end":{"line":141,"column":34}},{"start":{"line":141,"column":34},"end":{"line":141,"column":48}},{"start":{"line":141,"column":48},"end":{"line":141,"column":null}}]},"24":{"loc":{"start":{"line":143,"column":24},"end":{"line":145,"column":null}},"type":"cond-expr","locations":[{"start":{"line":144,"column":26},"end":{"line":144,"column":null}},{"start":{"line":145,"column":26},"end":{"line":145,"column":null}}]},"25":{"loc":{"start":{"line":148,"column":18},"end":{"line":152,"column":null}},"type":"cond-expr","locations":[{"start":{"line":149,"column":22},"end":{"line":149,"column":null}},{"start":{"line":150,"column":22},"end":{"line":152,"column":null}}]},"26":{"loc":{"start":{"line":148,"column":18},"end":{"line":148,"column":null}},"type":"binary-expr","locations":[{"start":{"line":148,"column":18},"end":{"line":148,"column":30}},{"start":{"line":148,"column":30},"end":{"line":148,"column":null}}]},"27":{"loc":{"start":{"line":150,"column":22},"end":{"line":152,"column":null}},"type":"cond-expr","locations":[{"start":{"line":151,"column":24},"end":{"line":151,"column":null}},{"start":{"line":152,"column":24},"end":{"line":152,"column":null}}]},"28":{"loc":{"start":{"line":150,"column":22},"end":{"line":150,"column":null}},"type":"binary-expr","locations":[{"start":{"line":150,"column":22},"end":{"line":150,"column":34}},{"start":{"line":150,"column":34},"end":{"line":150,"column":48}},{"start":{"line":150,"column":48},"end":{"line":150,"column":null}}]},"29":{"loc":{"start":{"line":161,"column":13},"end":{"line":161,"column":26}},"type":"binary-expr","locations":[{"start":{"line":161,"column":13},"end":{"line":161,"column":26}},{"start":{"line":161,"column":26},"end":{"line":161,"column":null}}]},"30":{"loc":{"start":{"line":166,"column":13},"end":{"line":166,"column":null}},"type":"binary-expr","locations":[{"start":{"line":166,"column":13},"end":{"line":166,"column":null}}]},"31":{"loc":{"start":{"line":169,"column":32},"end":{"line":169,"column":83}},"type":"cond-expr","locations":[{"start":{"line":169,"column":61},"end":{"line":169,"column":73}},{"start":{"line":169,"column":73},"end":{"line":169,"column":83}}]},"32":{"loc":{"start":{"line":171,"column":17},"end":{"line":171,"column":89}},"type":"cond-expr","locations":[{"start":{"line":171,"column":46},"end":{"line":171,"column":60}},{"start":{"line":171,"column":60},"end":{"line":171,"column":89}}]},"33":{"loc":{"start":{"line":183,"column":13},"end":{"line":183,"column":null}},"type":"cond-expr","locations":[{"start":{"line":183,"column":24},"end":{"line":183,"column":40}},{"start":{"line":183,"column":40},"end":{"line":183,"column":null}}]},"34":{"loc":{"start":{"line":185,"column":11},"end":{"line":185,"column":23}},"type":"binary-expr","locations":[{"start":{"line":185,"column":11},"end":{"line":185,"column":23}},{"start":{"line":185,"column":23},"end":{"line":185,"column":null}}]},"35":{"loc":{"start":{"line":203,"column":2},"end":{"line":207,"column":null}},"type":"if","locations":[{"start":{"line":203,"column":2},"end":{"line":207,"column":null}}]},"36":{"loc":{"start":{"line":224,"column":20},"end":{"line":224,"column":37}},"type":"binary-expr","locations":[{"start":{"line":224,"column":20},"end":{"line":224,"column":32}},{"start":{"line":224,"column":36},"end":{"line":224,"column":37}}]},"37":{"loc":{"start":{"line":225,"column":4},"end":{"line":225,"column":null}},"type":"if","locations":[{"start":{"line":225,"column":4},"end":{"line":225,"column":null}}]},"38":{"loc":{"start":{"line":225,"column":8},"end":{"line":225,"column":30}},"type":"binary-expr","locations":[{"start":{"line":225,"column":8},"end":{"line":225,"column":19}},{"start":{"line":225,"column":19},"end":{"line":225,"column":30}}]},"39":{"loc":{"start":{"line":233,"column":17},"end":{"line":233,"column":null}},"type":"binary-expr","locations":[{"start":{"line":233,"column":17},"end":{"line":233,"column":29}},{"start":{"line":233,"column":33},"end":{"line":233,"column":null}}]},"40":{"loc":{"start":{"line":239,"column":10},"end":{"line":239,"column":40}},"type":"binary-expr","locations":[{"start":{"line":239,"column":10},"end":{"line":239,"column":40}},{"start":{"line":239,"column":40},"end":{"line":239,"column":null}}]},"41":{"loc":{"start":{"line":239,"column":10},"end":{"line":239,"column":27}},"type":"binary-expr","locations":[{"start":{"line":239,"column":10},"end":{"line":239,"column":22}},{"start":{"line":239,"column":26},"end":{"line":239,"column":27}}]},"42":{"loc":{"start":{"line":248,"column":2},"end":{"line":255,"column":null}},"type":"if","locations":[{"start":{"line":248,"column":2},"end":{"line":255,"column":null}}]}},"s":{"0":0,"1":2,"2":2,"3":2,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0,0],"24":[0,0],"25":[0,0],"26":[0,0],"27":[0,0],"28":[0,0,0],"29":[0,0],"30":[0],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[0],"36":[0,0],"37":[0],"38":[0,0],"39":[0,0],"40":[0,0],"41":[0,0],"42":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/BadgeDisplay.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/BadgeDisplay.tsx","statementMap":{"0":{"start":{"line":10,"column":16},"end":{"line":10,"column":29}},"1":{"start":{"line":11,"column":19},"end":{"line":11,"column":null}},"2":{"start":{"line":12,"column":18},"end":{"line":12,"column":null}}},"fnMap":{"0":{"name":"BadgeDisplay","decl":{"start":{"line":10,"column":16},"end":{"line":10,"column":29}},"loc":{"start":{"line":10,"column":70},"end":{"line":30,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":38},"end":{"line":10,"column":49}},"type":"default-arg","locations":[{"start":{"line":10,"column":45},"end":{"line":10,"column":49}}]},"1":{"loc":{"start":{"line":11,"column":19},"end":{"line":11,"column":null}},"type":"cond-expr","locations":[{"start":{"line":11,"column":35},"end":{"line":11,"column":47}},{"start":{"line":11,"column":47},"end":{"line":11,"column":null}}]},"2":{"loc":{"start":{"line":12,"column":18},"end":{"line":12,"column":null}},"type":"cond-expr","locations":[{"start":{"line":12,"column":34},"end":{"line":12,"column":48}},{"start":{"line":12,"column":48},"end":{"line":12,"column":null}}]},"3":{"loc":{"start":{"line":26,"column":9},"end":{"line":26,"column":26}},"type":"binary-expr","locations":[{"start":{"line":26,"column":9},"end":{"line":26,"column":26}}]}},"s":{"0":0,"1":0,"2":0},"f":{"0":0},"b":{"0":[0],"1":[0,0],"2":[0,0],"3":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/BrandMark.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/BrandMark.tsx","statementMap":{"0":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}},"1":{"start":{"line":1,"column":17},"end":{"line":1,"column":null}}},"fnMap":{"0":{"name":"BrandMark","decl":{"start":{"line":3,"column":16},"end":{"line":3,"column":null}},"loc":{"start":{"line":3,"column":16},"end":{"line":30,"column":null}}}},"branchMap":{},"s":{"0":0,"1":0},"f":{"0":0},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/ErrorBoundary.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/ErrorBoundary.tsx","statementMap":{"0":{"start":{"line":14,"column":13},"end":{"line":14,"column":35}},"1":{"start":{"line":3,"column":58},"end":{"line":3,"column":null}},"2":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"3":{"start":{"line":22,"column":4},"end":{"line":22,"column":null}},"4":{"start":{"line":30,"column":4},"end":{"line":57,"column":null}},"5":{"start":{"line":31,"column":6},"end":{"line":31,"column":null}},"6":{"start":{"line":31,"column":31},"end":{"line":31,"column":null}},"7":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"8":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"9":{"start":{"line":25,"column":2},"end":{"line":27,"column":null}},"10":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":17,"column":2},"end":{"line":17,"column":9}},"loc":{"start":{"line":17,"column":43},"end":{"line":19,"column":null}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":21,"column":2},"end":{"line":21,"column":20}},"loc":{"start":{"line":21,"column":51},"end":{"line":23,"column":null}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":29,"column":2},"end":{"line":29,"column":11}},"loc":{"start":{"line":29,"column":11},"end":{"line":60,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":25,"column":16},"end":{"line":25,"column":null}},"loc":{"start":{"line":25,"column":16},"end":{"line":27,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":30,"column":4},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":30,"column":4},"end":{"line":57,"column":null}}]},"1":{"loc":{"start":{"line":31,"column":6},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":31,"column":6},"end":{"line":31,"column":null}}]}},"s":{"0":0,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/ProgressBar.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/ProgressBar.tsx","statementMap":{"0":{"start":{"line":11,"column":16},"end":{"line":11,"column":28}},"1":{"start":{"line":18,"column":18},"end":{"line":18,"column":null}},"2":{"start":{"line":19,"column":20},"end":{"line":19,"column":null}},"3":{"start":{"line":20,"column":19},"end":{"line":20,"column":null}}},"fnMap":{"0":{"name":"ProgressBar","decl":{"start":{"line":11,"column":16},"end":{"line":11,"column":28}},"loc":{"start":{"line":17,"column":19},"end":{"line":45,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":2},"end":{"line":14,"column":20}},"type":"default-arg","locations":[{"start":{"line":14,"column":16},"end":{"line":14,"column":20}}]},"1":{"loc":{"start":{"line":15,"column":2},"end":{"line":15,"column":13}},"type":"default-arg","locations":[{"start":{"line":15,"column":9},"end":{"line":15,"column":13}}]},"2":{"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":16}},"type":"default-arg","locations":[{"start":{"line":16,"column":14},"end":{"line":16,"column":16}}]},"3":{"loc":{"start":{"line":19,"column":20},"end":{"line":19,"column":null}},"type":"cond-expr","locations":[{"start":{"line":19,"column":36},"end":{"line":19,"column":46}},{"start":{"line":19,"column":46},"end":{"line":19,"column":null}}]},"4":{"loc":{"start":{"line":20,"column":19},"end":{"line":20,"column":null}},"type":"cond-expr","locations":[{"start":{"line":20,"column":37},"end":{"line":20,"column":54}},{"start":{"line":20,"column":54},"end":{"line":20,"column":null}}]},"5":{"loc":{"start":{"line":24,"column":8},"end":{"line":24,"column":17}},"type":"binary-expr","locations":[{"start":{"line":24,"column":8},"end":{"line":24,"column":17}},{"start":{"line":24,"column":17},"end":{"line":24,"column":27}}]},"6":{"loc":{"start":{"line":26,"column":11},"end":{"line":26,"column":20}},"type":"binary-expr","locations":[{"start":{"line":26,"column":11},"end":{"line":26,"column":20}}]},"7":{"loc":{"start":{"line":27,"column":11},"end":{"line":27,"column":26}},"type":"binary-expr","locations":[{"start":{"line":27,"column":11},"end":{"line":27,"column":26}}]},"8":{"loc":{"start":{"line":36,"column":20},"end":{"line":36,"column":52}},"type":"binary-expr","locations":[{"start":{"line":36,"column":20},"end":{"line":36,"column":29}},{"start":{"line":36,"column":29},"end":{"line":36,"column":52}}]}},"s":{"0":13,"1":13,"2":13,"3":13},"f":{"0":13},"b":{"0":[12],"1":[13],"2":[13],"3":[13,0],"4":[3,10],"5":[13,12],"6":[12],"7":[12],"8":[13,12]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/Toast.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/Toast.tsx","statementMap":{"0":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"1":{"start":{"line":23,"column":16},"end":{"line":23,"column":24}},"2":{"start":{"line":3,"column":73},"end":{"line":3,"column":null}},"3":{"start":{"line":21,"column":21},"end":{"line":21,"column":null}},"4":{"start":{"line":24,"column":2},"end":{"line":24,"column":null}},"5":{"start":{"line":29,"column":69},"end":{"line":33,"column":null}},"6":{"start":{"line":38,"column":30},"end":{"line":38,"column":null}},"7":{"start":{"line":39,"column":18},"end":{"line":39,"column":null}},"8":{"start":{"line":41,"column":20},"end":{"line":47,"column":null}},"9":{"start":{"line":42,"column":15},"end":{"line":42,"column":32}},"10":{"start":{"line":43,"column":4},"end":{"line":43,"column":null}},"11":{"start":{"line":43,"column":24},"end":{"line":43,"column":null}},"12":{"start":{"line":44,"column":4},"end":{"line":46,"column":null}},"13":{"start":{"line":45,"column":6},"end":{"line":45,"column":null}},"14":{"start":{"line":45,"column":26},"end":{"line":45,"column":null}},"15":{"start":{"line":45,"column":45},"end":{"line":45,"column":null}},"16":{"start":{"line":49,"column":18},"end":{"line":51,"column":null}},"17":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"18":{"start":{"line":50,"column":24},"end":{"line":50,"column":null}},"19":{"start":{"line":50,"column":43},"end":{"line":50,"column":null}},"20":{"start":{"line":63,"column":33},"end":{"line":63,"column":52}},"21":{"start":{"line":64,"column":12},"end":{"line":66,"column":null}},"22":{"start":{"line":76,"column":33},"end":{"line":76,"column":null}}},"fnMap":{"0":{"name":"(anonymous_3)","decl":{"start":{"line":21,"column":67},"end":{"line":21,"column":74}},"loc":{"start":{"line":21,"column":67},"end":{"line":21,"column":76}}},"1":{"name":"useToast","decl":{"start":{"line":23,"column":16},"end":{"line":23,"column":24}},"loc":{"start":{"line":23,"column":16},"end":{"line":25,"column":null}}},"2":{"name":"ToastProvider","decl":{"start":{"line":37,"column":16},"end":{"line":37,"column":29}},"loc":{"start":{"line":37,"column":73},"end":{"line":89,"column":null}}},"3":{"name":"(anonymous_6)","decl":{"start":{"line":41,"column":32},"end":{"line":41,"column":33}},"loc":{"start":{"line":41,"column":72},"end":{"line":47,"column":5}}},"4":{"name":"(anonymous_7)","decl":{"start":{"line":43,"column":14},"end":{"line":43,"column":15}},"loc":{"start":{"line":43,"column":24},"end":{"line":43,"column":null}}},"5":{"name":"(anonymous_8)","decl":{"start":{"line":44,"column":15},"end":{"line":44,"column":null}},"loc":{"start":{"line":44,"column":15},"end":{"line":46,"column":7}}},"6":{"name":"(anonymous_9)","decl":{"start":{"line":45,"column":16},"end":{"line":45,"column":17}},"loc":{"start":{"line":45,"column":26},"end":{"line":45,"column":null}}},"7":{"name":"(anonymous_10)","decl":{"start":{"line":45,"column":38},"end":{"line":45,"column":39}},"loc":{"start":{"line":45,"column":45},"end":{"line":45,"column":null}}},"8":{"name":"(anonymous_11)","decl":{"start":{"line":49,"column":30},"end":{"line":49,"column":31}},"loc":{"start":{"line":49,"column":31},"end":{"line":51,"column":5}}},"9":{"name":"(anonymous_12)","decl":{"start":{"line":50,"column":14},"end":{"line":50,"column":15}},"loc":{"start":{"line":50,"column":24},"end":{"line":50,"column":null}}},"10":{"name":"(anonymous_13)","decl":{"start":{"line":50,"column":36},"end":{"line":50,"column":37}},"loc":{"start":{"line":50,"column":43},"end":{"line":50,"column":null}}},"11":{"name":"(anonymous_14)","decl":{"start":{"line":62,"column":22},"end":{"line":62,"column":23}},"loc":{"start":{"line":62,"column":23},"end":{"line":84,"column":null}}},"12":{"name":"(anonymous_15)","decl":{"start":{"line":76,"column":27},"end":{"line":76,"column":33}},"loc":{"start":{"line":76,"column":33},"end":{"line":76,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":41,"column":50},"end":{"line":41,"column":72}},"type":"default-arg","locations":[{"start":{"line":41,"column":68},"end":{"line":41,"column":72}}]},"1":{"loc":{"start":{"line":56,"column":7},"end":{"line":56,"column":null}},"type":"binary-expr","locations":[{"start":{"line":56,"column":7},"end":{"line":56,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"b":{"0":[0],"1":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/TopBar.tsx": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/components/ui/TopBar.tsx","statementMap":{"0":{"start":{"line":9,"column":16},"end":{"line":9,"column":null}},"1":{"start":{"line":3,"column":17},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":36},"end":{"line":4,"column":null}},"3":{"start":{"line":5,"column":28},"end":{"line":5,"column":null}},"4":{"start":{"line":6,"column":27},"end":{"line":6,"column":null}},"5":{"start":{"line":7,"column":26},"end":{"line":7,"column":null}},"6":{"start":{"line":10,"column":19},"end":{"line":10,"column":null}},"7":{"start":{"line":11,"column":28},"end":{"line":11,"column":null}},"8":{"start":{"line":12,"column":26},"end":{"line":12,"column":null}},"9":{"start":{"line":15,"column":2},"end":{"line":21,"column":null}},"10":{"start":{"line":16,"column":18},"end":{"line":16,"column":null}},"11":{"start":{"line":17,"column":24},"end":{"line":17,"column":81}},"12":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"13":{"start":{"line":19,"column":4},"end":{"line":19,"column":null}},"14":{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},"15":{"start":{"line":24,"column":17},"end":{"line":24,"column":null}},"16":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"17":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"18":{"start":{"line":27,"column":4},"end":{"line":27,"column":null}},"19":{"start":{"line":30,"column":22},"end":{"line":30,"column":null}},"20":{"start":{"line":31,"column":19},"end":{"line":31,"column":35}},"21":{"start":{"line":33,"column":18},"end":{"line":33,"column":null}},"22":{"start":{"line":34,"column":20},"end":{"line":34,"column":null}},"23":{"start":{"line":35,"column":22},"end":{"line":35,"column":null}},"24":{"start":{"line":36,"column":20},"end":{"line":36,"column":null}},"25":{"start":{"line":38,"column":15},"end":{"line":51,"column":null}},"26":{"start":{"line":53,"column":18},"end":{"line":53,"column":null}},"27":{"start":{"line":55,"column":15},"end":{"line":55,"column":null}},"28":{"start":{"line":57,"column":4},"end":{"line":63,"column":null}},"29":{"start":{"line":60,"column":26},"end":{"line":60,"column":30}},"30":{"start":{"line":72,"column":12},"end":{"line":73,"column":null}}},"fnMap":{"0":{"name":"TopBar","decl":{"start":{"line":9,"column":16},"end":{"line":9,"column":null}},"loc":{"start":{"line":9,"column":16},"end":{"line":141,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":15,"column":12},"end":{"line":15,"column":null}},"loc":{"start":{"line":15,"column":12},"end":{"line":21,"column":5}}},"2":{"name":"toggleDark","decl":{"start":{"line":23,"column":11},"end":{"line":23,"column":null}},"loc":{"start":{"line":23,"column":11},"end":{"line":28,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":60,"column":11},"end":{"line":60,"column":12}},"loc":{"start":{"line":60,"column":26},"end":{"line":60,"column":30}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":71,"column":20},"end":{"line":71,"column":21}},"loc":{"start":{"line":72,"column":12},"end":{"line":73,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":19},"end":{"line":18,"column":null}},"type":"binary-expr","locations":[{"start":{"line":18,"column":19},"end":{"line":18,"column":40}},{"start":{"line":18,"column":40},"end":{"line":18,"column":50}},{"start":{"line":18,"column":50},"end":{"line":18,"column":null}}]},"1":{"loc":{"start":{"line":27,"column":34},"end":{"line":27,"column":null}},"type":"cond-expr","locations":[{"start":{"line":27,"column":41},"end":{"line":27,"column":50}},{"start":{"line":27,"column":50},"end":{"line":27,"column":null}}]},"2":{"loc":{"start":{"line":33,"column":18},"end":{"line":33,"column":null}},"type":"binary-expr","locations":[{"start":{"line":33,"column":18},"end":{"line":33,"column":32}},{"start":{"line":33,"column":32},"end":{"line":33,"column":null}}]},"3":{"loc":{"start":{"line":34,"column":20},"end":{"line":34,"column":null}},"type":"binary-expr","locations":[{"start":{"line":34,"column":20},"end":{"line":34,"column":34}},{"start":{"line":34,"column":34},"end":{"line":34,"column":null}}]},"4":{"loc":{"start":{"line":35,"column":22},"end":{"line":35,"column":null}},"type":"binary-expr","locations":[{"start":{"line":35,"column":22},"end":{"line":35,"column":36}},{"start":{"line":35,"column":36},"end":{"line":35,"column":48}},{"start":{"line":35,"column":48},"end":{"line":35,"column":null}}]},"5":{"loc":{"start":{"line":40,"column":8},"end":{"line":50,"column":10}},"type":"cond-expr","locations":[{"start":{"line":41,"column":8},"end":{"line":49,"column":null}},{"start":{"line":50,"column":8},"end":{"line":50,"column":10}}]},"6":{"loc":{"start":{"line":53,"column":18},"end":{"line":53,"column":null}},"type":"binary-expr","locations":[{"start":{"line":53,"column":18},"end":{"line":53,"column":32}},{"start":{"line":53,"column":32},"end":{"line":53,"column":null}}]},"7":{"loc":{"start":{"line":55,"column":15},"end":{"line":55,"column":null}},"type":"binary-expr","locations":[{"start":{"line":55,"column":15},"end":{"line":55,"column":38}},{"start":{"line":55,"column":38},"end":{"line":55,"column":62}},{"start":{"line":55,"column":62},"end":{"line":55,"column":null}}]},"8":{"loc":{"start":{"line":57,"column":4},"end":{"line":63,"column":null}},"type":"binary-expr","locations":[{"start":{"line":57,"column":4},"end":{"line":63,"column":24}},{"start":{"line":63,"column":24},"end":{"line":63,"column":null}}]},"9":{"loc":{"start":{"line":76,"column":16},"end":{"line":78,"column":null}},"type":"cond-expr","locations":[{"start":{"line":77,"column":20},"end":{"line":77,"column":null}},{"start":{"line":78,"column":20},"end":{"line":78,"column":null}}]},"10":{"loc":{"start":{"line":86,"column":9},"end":{"line":86,"column":51}},"type":"binary-expr","locations":[{"start":{"line":86,"column":9},"end":{"line":86,"column":51}},{"start":{"line":86,"column":51},"end":{"line":86,"column":null}}]},"11":{"loc":{"start":{"line":90,"column":14},"end":{"line":92,"column":null}},"type":"cond-expr","locations":[{"start":{"line":91,"column":18},"end":{"line":91,"column":null}},{"start":{"line":92,"column":18},"end":{"line":92,"column":null}}]},"12":{"loc":{"start":{"line":104,"column":19},"end":{"line":104,"column":null}},"type":"cond-expr","locations":[{"start":{"line":104,"column":26},"end":{"line":104,"column":51}},{"start":{"line":104,"column":51},"end":{"line":104,"column":null}}]},"13":{"loc":{"start":{"line":109,"column":14},"end":{"line":123,"column":27}},"type":"cond-expr","locations":[{"start":{"line":109,"column":14},"end":{"line":123,"column":27}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0,0],"5":[0,0],"6":[0,0],"7":[0,0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api.ts","statementMap":{"0":{"start":{"line":7,"column":13},"end":{"line":7,"column":21}},"1":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"2":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"4":{"start":{"line":9,"column":11},"end":{"line":9,"column":25}},"5":{"start":{"line":11,"column":11},"end":{"line":11,"column":44}},"6":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"7":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"8":{"start":{"line":25,"column":22},"end":{"line":25,"column":null}},"9":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}},"10":{"start":{"line":26,"column":4},"end":{"line":29,"column":null}},"11":{"start":{"line":32,"column":2},"end":{"line":32,"column":null}},"12":{"start":{"line":36,"column":65},"end":{"line":36,"column":null}},"13":{"start":{"line":38,"column":42},"end":{"line":40,"column":null}},"14":{"start":{"line":42,"column":2},"end":{"line":44,"column":null}},"15":{"start":{"line":43,"column":4},"end":{"line":43,"column":null}},"16":{"start":{"line":46,"column":2},"end":{"line":48,"column":null}},"17":{"start":{"line":47,"column":4},"end":{"line":47,"column":null}},"18":{"start":{"line":50,"column":19},"end":{"line":54,"column":null}},"19":{"start":{"line":56,"column":2},"end":{"line":56,"column":null}}},"fnMap":{"0":{"name":"(anonymous_3)","decl":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"loc":{"start":{"line":12,"column":4},"end":{"line":15,"column":null}}},"1":{"name":"handleResponse","decl":{"start":{"line":23,"column":15},"end":{"line":23,"column":33}},"loc":{"start":{"line":23,"column":51},"end":{"line":33,"column":null}}},"2":{"name":"(anonymous_5)","decl":{"start":{"line":25,"column":50},"end":{"line":25,"column":57}},"loc":{"start":{"line":25,"column":57},"end":{"line":25,"column":null}}},"3":{"name":"apiFetch","decl":{"start":{"line":35,"column":22},"end":{"line":35,"column":30}},"loc":{"start":{"line":35,"column":78},"end":{"line":57,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":2},"end":{"line":5,"column":null}},"type":"binary-expr","locations":[{"start":{"line":2,"column":2},"end":{"line":2,"column":38}},{"start":{"line":3,"column":3},"end":{"line":5,"column":32}}]},"1":{"loc":{"start":{"line":3,"column":3},"end":{"line":5,"column":32}},"type":"cond-expr","locations":[{"start":{"line":4,"column":6},"end":{"line":4,"column":46}},{"start":{"line":5,"column":6},"end":{"line":5,"column":32}}]},"2":{"loc":{"start":{"line":24,"column":2},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":31,"column":null}}]},"3":{"loc":{"start":{"line":28,"column":6},"end":{"line":28,"column":84}},"type":"binary-expr","locations":[{"start":{"line":28,"column":6},"end":{"line":28,"column":22}},{"start":{"line":28,"column":26},"end":{"line":28,"column":43}},{"start":{"line":28,"column":47},"end":{"line":28,"column":84}}]},"4":{"loc":{"start":{"line":35,"column":52},"end":{"line":35,"column":78}},"type":"default-arg","locations":[{"start":{"line":35,"column":76},"end":{"line":35,"column":78}}]},"5":{"loc":{"start":{"line":42,"column":2},"end":{"line":44,"column":null}},"type":"if","locations":[{"start":{"line":42,"column":2},"end":{"line":44,"column":null}}]},"6":{"loc":{"start":{"line":46,"column":2},"end":{"line":48,"column":null}},"type":"if","locations":[{"start":{"line":46,"column":2},"end":{"line":48,"column":null}}]},"7":{"loc":{"start":{"line":46,"column":6},"end":{"line":46,"column":57}},"type":"binary-expr","locations":[{"start":{"line":46,"column":6},"end":{"line":46,"column":28}},{"start":{"line":46,"column":28},"end":{"line":46,"column":57}}]},"8":{"loc":{"start":{"line":53,"column":10},"end":{"line":53,"column":null}},"type":"cond-expr","locations":[{"start":{"line":53,"column":37},"end":{"line":53,"column":44}},{"start":{"line":53,"column":44},"end":{"line":53,"column":null}}]},"9":{"loc":{"start":{"line":53,"column":44},"end":{"line":53,"column":null}},"type":"cond-expr","locations":[{"start":{"line":53,"column":65},"end":{"line":53,"column":88}},{"start":{"line":53,"column":88},"end":{"line":53,"column":null}}]}},"s":{"0":0,"1":0,"2":2,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[2,2],"1":[0,2],"2":[0],"3":[0,0,0],"4":[0],"5":[0],"6":[0],"7":[0,0],"8":[0,0],"9":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/adapters.ts","statementMap":{"0":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"1":{"start":{"line":26,"column":16},"end":{"line":26,"column":33}},"2":{"start":{"line":73,"column":16},"end":{"line":73,"column":37}},"3":{"start":{"line":12,"column":2},"end":{"line":19,"column":null}},"4":{"start":{"line":13,"column":16},"end":{"line":13,"column":34}},"5":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"6":{"start":{"line":14,"column":23},"end":{"line":14,"column":null}},"7":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"8":{"start":{"line":15,"column":81},"end":{"line":15,"column":null}},"9":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"10":{"start":{"line":16,"column":79},"end":{"line":16,"column":null}},"11":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"12":{"start":{"line":17,"column":25},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"14":{"start":{"line":18,"column":13},"end":{"line":18,"column":null}},"15":{"start":{"line":20,"column":14},"end":{"line":20,"column":null}},"16":{"start":{"line":21,"column":2},"end":{"line":21,"column":null}},"17":{"start":{"line":24,"column":55},"end":{"line":24,"column":null}},"18":{"start":{"line":27,"column":2},"end":{"line":40,"column":null}},"19":{"start":{"line":44,"column":59},"end":{"line":44,"column":null}},"20":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"21":{"start":{"line":46,"column":4},"end":{"line":50,"column":null}},"22":{"start":{"line":47,"column":6},"end":{"line":47,"column":null}},"23":{"start":{"line":49,"column":6},"end":{"line":49,"column":null}},"24":{"start":{"line":53,"column":2},"end":{"line":70,"column":null}},"25":{"start":{"line":74,"column":2},"end":{"line":81,"column":null}}},"fnMap":{"0":{"name":"mimeToLabel","decl":{"start":{"line":11,"column":9},"end":{"line":11,"column":21}},"loc":{"start":{"line":11,"column":58},"end":{"line":22,"column":null}}},"1":{"name":"toDocumentListRow","decl":{"start":{"line":26,"column":16},"end":{"line":26,"column":33}},"loc":{"start":{"line":26,"column":59},"end":{"line":41,"column":null}}},"2":{"name":"toDocumentDetailView","decl":{"start":{"line":43,"column":16},"end":{"line":43,"column":36}},"loc":{"start":{"line":43,"column":60},"end":{"line":71,"column":null}}},"3":{"name":"toDocumentPreviewView","decl":{"start":{"line":73,"column":16},"end":{"line":73,"column":37}},"loc":{"start":{"line":73,"column":62},"end":{"line":82,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":2},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":12,"column":2},"end":{"line":19,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":4},"end":{"line":14,"column":null}}]},"2":{"loc":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":4},"end":{"line":15,"column":null}}]},"3":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":16,"column":4},"end":{"line":16,"column":null}}]},"4":{"loc":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":17,"column":null}}]},"5":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":null}}]},"6":{"loc":{"start":{"line":21,"column":9},"end":{"line":21,"column":null}},"type":"binary-expr","locations":[{"start":{"line":21,"column":9},"end":{"line":21,"column":16}},{"start":{"line":21,"column":16},"end":{"line":21,"column":null}}]},"7":{"loc":{"start":{"line":37,"column":18},"end":{"line":37,"column":null}},"type":"binary-expr","locations":[{"start":{"line":37,"column":18},"end":{"line":37,"column":36}},{"start":{"line":37,"column":40},"end":{"line":37,"column":null}}]},"8":{"loc":{"start":{"line":45,"column":2},"end":{"line":51,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":2},"end":{"line":51,"column":null}}]},"9":{"loc":{"start":{"line":62,"column":18},"end":{"line":62,"column":null}},"type":"binary-expr","locations":[{"start":{"line":62,"column":18},"end":{"line":62,"column":36}},{"start":{"line":62,"column":40},"end":{"line":62,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0,0],"7":[0,0],"8":[0],"9":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/courses.ts","statementMap":{"0":{"start":{"line":25,"column":22},"end":{"line":25,"column":34}},"1":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"2":{"start":{"line":18,"column":22},"end":{"line":18,"column":40}},"3":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"4":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"5":{"start":{"line":9,"column":2},"end":{"line":11,"column":null}},"6":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"7":{"start":{"line":22,"column":2},"end":{"line":22,"column":null}},"8":{"start":{"line":29,"column":2},"end":{"line":33,"column":null}}},"fnMap":{"0":{"name":"fetchCourses","decl":{"start":{"line":4,"column":22},"end":{"line":4,"column":34}},"loc":{"start":{"line":7,"column":22},"end":{"line":12,"column":null}}},"1":{"name":"fetchCourse","decl":{"start":{"line":14,"column":22},"end":{"line":14,"column":33}},"loc":{"start":{"line":14,"column":72},"end":{"line":16,"column":null}}},"2":{"name":"fetchCourseLessons","decl":{"start":{"line":18,"column":22},"end":{"line":18,"column":40}},"loc":{"start":{"line":20,"column":22},"end":{"line":23,"column":null}}},"3":{"name":"createCourse","decl":{"start":{"line":25,"column":22},"end":{"line":25,"column":34}},"loc":{"start":{"line":27,"column":22},"end":{"line":34,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":5,"column":2},"end":{"line":5,"column":10}},"type":"default-arg","locations":[{"start":{"line":5,"column":9},"end":{"line":5,"column":10}}]},"1":{"loc":{"start":{"line":6,"column":2},"end":{"line":6,"column":13}},"type":"default-arg","locations":[{"start":{"line":6,"column":10},"end":{"line":6,"column":13}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0],"1":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/documents.ts","statementMap":{"0":{"start":{"line":83,"column":22},"end":{"line":83,"column":36}},"1":{"start":{"line":39,"column":22},"end":{"line":39,"column":41}},"2":{"start":{"line":48,"column":22},"end":{"line":48,"column":42}},"3":{"start":{"line":30,"column":22},"end":{"line":30,"column":41}},"4":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"5":{"start":{"line":149,"column":22},"end":{"line":149,"column":43}},"6":{"start":{"line":141,"column":22},"end":{"line":141,"column":41}},"7":{"start":{"line":157,"column":22},"end":{"line":157,"column":44}},"8":{"start":{"line":167,"column":22},"end":{"line":167,"column":47}},"9":{"start":{"line":73,"column":22},"end":{"line":73,"column":35}},"10":{"start":{"line":57,"column":22},"end":{"line":57,"column":36}},"11":{"start":{"line":93,"column":16},"end":{"line":93,"column":42}},"12":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"13":{"start":{"line":17,"column":79},"end":{"line":17,"column":null}},"14":{"start":{"line":4,"column":2},"end":{"line":7,"column":null}},"15":{"start":{"line":25,"column":2},"end":{"line":27,"column":null}},"16":{"start":{"line":34,"column":2},"end":{"line":36,"column":null}},"17":{"start":{"line":43,"column":2},"end":{"line":45,"column":null}},"18":{"start":{"line":52,"column":2},"end":{"line":54,"column":null}},"19":{"start":{"line":63,"column":19},"end":{"line":63,"column":null}},"20":{"start":{"line":64,"column":2},"end":{"line":64,"column":null}},"21":{"start":{"line":65,"column":2},"end":{"line":65,"column":null}},"22":{"start":{"line":66,"column":2},"end":{"line":70,"column":null}},"23":{"start":{"line":77,"column":2},"end":{"line":80,"column":null}},"24":{"start":{"line":87,"column":2},"end":{"line":90,"column":null}},"25":{"start":{"line":100,"column":2},"end":{"line":136,"column":null}},"26":{"start":{"line":101,"column":21},"end":{"line":101,"column":null}},"27":{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},"28":{"start":{"line":103,"column":4},"end":{"line":103,"column":null}},"29":{"start":{"line":105,"column":16},"end":{"line":105,"column":null}},"30":{"start":{"line":106,"column":4},"end":{"line":106,"column":null}},"31":{"start":{"line":107,"column":4},"end":{"line":107,"column":null}},"32":{"start":{"line":107,"column":21},"end":{"line":107,"column":null}},"33":{"start":{"line":109,"column":4},"end":{"line":111,"column":null}},"34":{"start":{"line":110,"column":6},"end":{"line":110,"column":null}},"35":{"start":{"line":110,"column":30},"end":{"line":110,"column":null}},"36":{"start":{"line":113,"column":4},"end":{"line":130,"column":null}},"37":{"start":{"line":114,"column":6},"end":{"line":129,"column":null}},"38":{"start":{"line":115,"column":8},"end":{"line":119,"column":null}},"39":{"start":{"line":116,"column":10},"end":{"line":116,"column":null}},"40":{"start":{"line":118,"column":10},"end":{"line":118,"column":null}},"41":{"start":{"line":121,"column":22},"end":{"line":121,"column":53}},"42":{"start":{"line":122,"column":8},"end":{"line":127,"column":null}},"43":{"start":{"line":123,"column":23},"end":{"line":123,"column":null}},"44":{"start":{"line":124,"column":10},"end":{"line":124,"column":null}},"45":{"start":{"line":124,"column":27},"end":{"line":124,"column":null}},"46":{"start":{"line":128,"column":8},"end":{"line":128,"column":null}},"47":{"start":{"line":132,"column":4},"end":{"line":132,"column":null}},"48":{"start":{"line":132,"column":40},"end":{"line":132,"column":null}},"49":{"start":{"line":133,"column":4},"end":{"line":133,"column":null}},"50":{"start":{"line":133,"column":40},"end":{"line":133,"column":null}},"51":{"start":{"line":135,"column":4},"end":{"line":135,"column":null}},"52":{"start":{"line":145,"column":16},"end":{"line":145,"column":null}},"53":{"start":{"line":146,"column":2},"end":{"line":146,"column":null}},"54":{"start":{"line":153,"column":15},"end":{"line":153,"column":null}},"55":{"start":{"line":154,"column":2},"end":{"line":154,"column":null}},"56":{"start":{"line":161,"column":15},"end":{"line":161,"column":null}},"57":{"start":{"line":162,"column":2},"end":{"line":162,"column":null}},"58":{"start":{"line":173,"column":2},"end":{"line":187,"column":null}},"59":{"start":{"line":173,"column":15},"end":{"line":173,"column":18}},"60":{"start":{"line":174,"column":4},"end":{"line":185,"column":null}},"61":{"start":{"line":175,"column":21},"end":{"line":175,"column":null}},"62":{"start":{"line":176,"column":6},"end":{"line":178,"column":null}},"63":{"start":{"line":177,"column":8},"end":{"line":177,"column":null}},"64":{"start":{"line":180,"column":6},"end":{"line":184,"column":null}},"65":{"start":{"line":183,"column":8},"end":{"line":183,"column":null}},"66":{"start":{"line":186,"column":4},"end":{"line":186,"column":null}},"67":{"start":{"line":186,"column":35},"end":{"line":186,"column":null}},"68":{"start":{"line":188,"column":2},"end":{"line":188,"column":null}}},"fnMap":{"0":{"name":"fetchDocumentsList","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":40}},"loc":{"start":{"line":23,"column":22},"end":{"line":28,"column":null}}},"1":{"name":"fetchDocumentStatus","decl":{"start":{"line":30,"column":22},"end":{"line":30,"column":41}},"loc":{"start":{"line":32,"column":22},"end":{"line":37,"column":null}}},"2":{"name":"fetchDocumentDetail","decl":{"start":{"line":39,"column":22},"end":{"line":39,"column":41}},"loc":{"start":{"line":41,"column":22},"end":{"line":46,"column":null}}},"3":{"name":"fetchDocumentPreview","decl":{"start":{"line":48,"column":22},"end":{"line":48,"column":42}},"loc":{"start":{"line":50,"column":22},"end":{"line":55,"column":null}}},"4":{"name":"uploadDocument","decl":{"start":{"line":57,"column":22},"end":{"line":57,"column":36}},"loc":{"start":{"line":61,"column":26},"end":{"line":71,"column":null}}},"5":{"name":"retryDocument","decl":{"start":{"line":73,"column":22},"end":{"line":73,"column":35}},"loc":{"start":{"line":75,"column":22},"end":{"line":81,"column":null}}},"6":{"name":"deleteDocument","decl":{"start":{"line":83,"column":22},"end":{"line":83,"column":36}},"loc":{"start":{"line":85,"column":22},"end":{"line":91,"column":null}}},"7":{"name":"uploadDocumentWithProgress","decl":{"start":{"line":93,"column":16},"end":{"line":93,"column":42}},"loc":{"start":{"line":98,"column":35},"end":{"line":137,"column":null}}},"8":{"name":"(anonymous_21)","decl":{"start":{"line":100,"column":21},"end":{"line":100,"column":22}},"loc":{"start":{"line":100,"column":31},"end":{"line":136,"column":null}}},"9":{"name":"(anonymous_22)","decl":{"start":{"line":109,"column":44},"end":{"line":109,"column":45}},"loc":{"start":{"line":109,"column":45},"end":{"line":111,"column":null}}},"10":{"name":"(anonymous_23)","decl":{"start":{"line":113,"column":33},"end":{"line":113,"column":null}},"loc":{"start":{"line":113,"column":33},"end":{"line":130,"column":null}}},"11":{"name":"(anonymous_24)","decl":{"start":{"line":132,"column":34},"end":{"line":132,"column":40}},"loc":{"start":{"line":132,"column":40},"end":{"line":132,"column":null}}},"12":{"name":"(anonymous_25)","decl":{"start":{"line":133,"column":34},"end":{"line":133,"column":40}},"loc":{"start":{"line":133,"column":40},"end":{"line":133,"column":null}}},"13":{"name":"getDocumentListRows","decl":{"start":{"line":141,"column":22},"end":{"line":141,"column":41}},"loc":{"start":{"line":143,"column":22},"end":{"line":147,"column":null}}},"14":{"name":"getDocumentDetailView","decl":{"start":{"line":149,"column":22},"end":{"line":149,"column":43}},"loc":{"start":{"line":151,"column":22},"end":{"line":155,"column":null}}},"15":{"name":"getDocumentPreviewView","decl":{"start":{"line":157,"column":22},"end":{"line":157,"column":44}},"loc":{"start":{"line":159,"column":22},"end":{"line":163,"column":null}}},"16":{"name":"pollDocumentUntilTerminal","decl":{"start":{"line":167,"column":22},"end":{"line":167,"column":47}},"loc":{"start":{"line":171,"column":18},"end":{"line":189,"column":null}}},"17":{"name":"(anonymous_30)","decl":{"start":{"line":186,"column":22},"end":{"line":186,"column":23}},"loc":{"start":{"line":186,"column":35},"end":{"line":186,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":4,"column":2},"end":{"line":7,"column":null}},"type":"binary-expr","locations":[{"start":{"line":4,"column":2},"end":{"line":4,"column":38}},{"start":{"line":5,"column":3},"end":{"line":7,"column":32}}]},"1":{"loc":{"start":{"line":5,"column":3},"end":{"line":7,"column":32}},"type":"cond-expr","locations":[{"start":{"line":6,"column":6},"end":{"line":6,"column":46}},{"start":{"line":7,"column":6},"end":{"line":7,"column":32}}]},"2":{"loc":{"start":{"line":61,"column":2},"end":{"line":61,"column":26}},"type":"default-arg","locations":[{"start":{"line":61,"column":17},"end":{"line":61,"column":26}}]},"3":{"loc":{"start":{"line":107,"column":4},"end":{"line":107,"column":null}},"type":"if","locations":[{"start":{"line":107,"column":4},"end":{"line":107,"column":null}}]},"4":{"loc":{"start":{"line":110,"column":6},"end":{"line":110,"column":null}},"type":"if","locations":[{"start":{"line":110,"column":6},"end":{"line":110,"column":null}}]},"5":{"loc":{"start":{"line":114,"column":6},"end":{"line":129,"column":null}},"type":"if","locations":[{"start":{"line":114,"column":6},"end":{"line":129,"column":null}},{"start":{"line":120,"column":13},"end":{"line":129,"column":null}}]},"6":{"loc":{"start":{"line":114,"column":10},"end":{"line":114,"column":49}},"type":"binary-expr","locations":[{"start":{"line":114,"column":10},"end":{"line":114,"column":31}},{"start":{"line":114,"column":31},"end":{"line":114,"column":49}}]},"7":{"loc":{"start":{"line":124,"column":10},"end":{"line":124,"column":null}},"type":"if","locations":[{"start":{"line":124,"column":10},"end":{"line":124,"column":null}}]},"8":{"loc":{"start":{"line":170,"column":2},"end":{"line":170,"column":19}},"type":"default-arg","locations":[{"start":{"line":170,"column":15},"end":{"line":170,"column":19}}]},"9":{"loc":{"start":{"line":171,"column":2},"end":{"line":171,"column":18}},"type":"default-arg","locations":[{"start":{"line":171,"column":16},"end":{"line":171,"column":18}}]},"10":{"loc":{"start":{"line":176,"column":6},"end":{"line":178,"column":null}},"type":"if","locations":[{"start":{"line":176,"column":6},"end":{"line":178,"column":null}}]},"11":{"loc":{"start":{"line":176,"column":10},"end":{"line":176,"column":66}},"type":"binary-expr","locations":[{"start":{"line":176,"column":10},"end":{"line":176,"column":39}},{"start":{"line":176,"column":39},"end":{"line":176,"column":66}}]},"12":{"loc":{"start":{"line":180,"column":6},"end":{"line":184,"column":null}},"type":"if","locations":[{"start":{"line":180,"column":6},"end":{"line":184,"column":null}},{"start":{"line":182,"column":13},"end":{"line":184,"column":null}}]},"13":{"loc":{"start":{"line":180,"column":10},"end":{"line":180,"column":56}},"type":"binary-expr","locations":[{"start":{"line":180,"column":10},"end":{"line":180,"column":33}},{"start":{"line":180,"column":37},"end":{"line":180,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0},"b":{"0":[0,0],"1":[0,0],"2":[0],"3":[0],"4":[0],"5":[0,0],"6":[0,0],"7":[0],"8":[0],"9":[0],"10":[0],"11":[0,0],"12":[0,0],"13":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/index.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/api/index.ts","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":null}},"1":{"start":{"line":2,"column":14},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":14},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":14},"end":{"line":4,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{},"b":{}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/auth/config.ts","statementMap":{"0":{"start":{"line":32,"column":13},"end":{"line":32,"column":40}},"1":{"start":{"line":3,"column":32},"end":{"line":3,"column":null}},"2":{"start":{"line":5,"column":16},"end":{"line":5,"column":null}},"3":{"start":{"line":6,"column":28},"end":{"line":6,"column":44}},"4":{"start":{"line":7,"column":26},"end":{"line":7,"column":37}},"5":{"start":{"line":10,"column":2},"end":{"line":29,"column":null}},"6":{"start":{"line":11,"column":16},"end":{"line":15,"column":null}},"7":{"start":{"line":17,"column":17},"end":{"line":17,"column":null}},"8":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"9":{"start":{"line":18,"column":17},"end":{"line":18,"column":null}},"10":{"start":{"line":20,"column":4},"end":{"line":26,"column":null}},"11":{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},"12":{"start":{"line":32,"column":40},"end":{"line":117,"column":null}},"13":{"start":{"line":41,"column":8},"end":{"line":43,"column":null}},"14":{"start":{"line":42,"column":10},"end":{"line":42,"column":null}},"15":{"start":{"line":45,"column":8},"end":{"line":74,"column":null}},"16":{"start":{"line":46,"column":22},"end":{"line":53,"column":null}},"17":{"start":{"line":55,"column":10},"end":{"line":58,"column":null}},"18":{"start":{"line":56,"column":26},"end":{"line":56,"column":null}},"19":{"start":{"line":56,"column":56},"end":{"line":56,"column":null}},"20":{"start":{"line":57,"column":12},"end":{"line":57,"column":null}},"21":{"start":{"line":60,"column":23},"end":{"line":60,"column":null}},"22":{"start":{"line":62,"column":10},"end":{"line":70,"column":null}},"23":{"start":{"line":72,"column":10},"end":{"line":72,"column":null}},"24":{"start":{"line":72,"column":38},"end":{"line":72,"column":null}},"25":{"start":{"line":73,"column":10},"end":{"line":73,"column":null}},"26":{"start":{"line":80,"column":6},"end":{"line":89,"column":null}},"27":{"start":{"line":81,"column":8},"end":{"line":88,"column":null}},"28":{"start":{"line":92,"column":6},"end":{"line":94,"column":null}},"29":{"start":{"line":93,"column":8},"end":{"line":93,"column":null}},"30":{"start":{"line":97,"column":6},"end":{"line":97,"column":null}},"31":{"start":{"line":100,"column":6},"end":{"line":100,"column":null}},"32":{"start":{"line":101,"column":6},"end":{"line":101,"column":null}},"33":{"start":{"line":102,"column":6},"end":{"line":102,"column":null}},"34":{"start":{"line":103,"column":6},"end":{"line":103,"column":null}},"35":{"start":{"line":104,"column":6},"end":{"line":104,"column":null}},"36":{"start":{"line":105,"column":6},"end":{"line":105,"column":null}}},"fnMap":{"0":{"name":"refreshAccessToken","decl":{"start":{"line":9,"column":15},"end":{"line":9,"column":34}},"loc":{"start":{"line":9,"column":44},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":40,"column":6},"end":{"line":40,"column":12}},"loc":{"start":{"line":40,"column":33},"end":{"line":75,"column":null}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":56,"column":49},"end":{"line":56,"column":56}},"loc":{"start":{"line":56,"column":56},"end":{"line":56,"column":null}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":79,"column":4},"end":{"line":79,"column":10}},"loc":{"start":{"line":79,"column":29},"end":{"line":98,"column":null}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":99,"column":4},"end":{"line":99,"column":10}},"loc":{"start":{"line":99,"column":36},"end":{"line":106,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":5,"column":16},"end":{"line":5,"column":null}},"type":"binary-expr","locations":[{"start":{"line":5,"column":16},"end":{"line":5,"column":47}},{"start":{"line":5,"column":51},"end":{"line":5,"column":null}}]},"1":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":null}}]},"2":{"loc":{"start":{"line":23,"column":20},"end":{"line":23,"column":60}},"type":"binary-expr","locations":[{"start":{"line":23,"column":20},"end":{"line":23,"column":38}},{"start":{"line":23,"column":42},"end":{"line":23,"column":60}}]},"3":{"loc":{"start":{"line":41,"column":8},"end":{"line":43,"column":null}},"type":"if","locations":[{"start":{"line":41,"column":8},"end":{"line":43,"column":null}}]},"4":{"loc":{"start":{"line":41,"column":12},"end":{"line":41,"column":59}},"type":"binary-expr","locations":[{"start":{"line":41,"column":12},"end":{"line":41,"column":35}},{"start":{"line":41,"column":35},"end":{"line":41,"column":59}}]},"5":{"loc":{"start":{"line":55,"column":10},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":10},"end":{"line":58,"column":null}}]},"6":{"loc":{"start":{"line":57,"column":28},"end":{"line":57,"column":null}},"type":"binary-expr","locations":[{"start":{"line":57,"column":28},"end":{"line":57,"column":40}},{"start":{"line":57,"column":44},"end":{"line":57,"column":null}}]},"7":{"loc":{"start":{"line":63,"column":16},"end":{"line":63,"column":40}},"type":"binary-expr","locations":[{"start":{"line":63,"column":16},"end":{"line":63,"column":33}},{"start":{"line":63,"column":33},"end":{"line":63,"column":40}}]},"8":{"loc":{"start":{"line":64,"column":19},"end":{"line":64,"column":56}},"type":"binary-expr","locations":[{"start":{"line":64,"column":19},"end":{"line":64,"column":39}},{"start":{"line":64,"column":39},"end":{"line":64,"column":56}}]},"9":{"loc":{"start":{"line":65,"column":18},"end":{"line":65,"column":null}},"type":"binary-expr","locations":[{"start":{"line":65,"column":18},"end":{"line":65,"column":45}},{"start":{"line":65,"column":45},"end":{"line":65,"column":null}}]},"10":{"loc":{"start":{"line":66,"column":25},"end":{"line":66,"column":null}},"type":"binary-expr","locations":[{"start":{"line":66,"column":25},"end":{"line":66,"column":42}},{"start":{"line":66,"column":46},"end":{"line":66,"column":null}}]},"11":{"loc":{"start":{"line":67,"column":26},"end":{"line":67,"column":null}},"type":"binary-expr","locations":[{"start":{"line":67,"column":26},"end":{"line":67,"column":44}},{"start":{"line":67,"column":48},"end":{"line":67,"column":null}}]},"12":{"loc":{"start":{"line":68,"column":18},"end":{"line":68,"column":null}},"type":"binary-expr","locations":[{"start":{"line":68,"column":18},"end":{"line":68,"column":37}},{"start":{"line":68,"column":37},"end":{"line":68,"column":null}}]},"13":{"loc":{"start":{"line":69,"column":25},"end":{"line":69,"column":null}},"type":"binary-expr","locations":[{"start":{"line":69,"column":25},"end":{"line":69,"column":52}},{"start":{"line":69,"column":52},"end":{"line":69,"column":null}}]},"14":{"loc":{"start":{"line":72,"column":10},"end":{"line":72,"column":null}},"type":"if","locations":[{"start":{"line":72,"column":10},"end":{"line":72,"column":null}}]},"15":{"loc":{"start":{"line":80,"column":6},"end":{"line":89,"column":null}},"type":"if","locations":[{"start":{"line":80,"column":6},"end":{"line":89,"column":null}}]},"16":{"loc":{"start":{"line":92,"column":6},"end":{"line":94,"column":null}},"type":"if","locations":[{"start":{"line":92,"column":6},"end":{"line":94,"column":null}}]},"17":{"loc":{"start":{"line":92,"column":24},"end":{"line":92,"column":57}},"type":"binary-expr","locations":[{"start":{"line":92,"column":24},"end":{"line":92,"column":48}},{"start":{"line":92,"column":52},"end":{"line":92,"column":57}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0],"2":[0,0],"3":[0],"4":[0,0],"5":[0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0],"15":[0],"16":[0],"17":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/middleware/auth-guard.ts","statementMap":{"0":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"1":{"start":{"line":32,"column":13},"end":{"line":32,"column":19}},"2":{"start":{"line":1,"column":29},"end":{"line":1,"column":null}},"3":{"start":{"line":2,"column":25},"end":{"line":2,"column":null}},"4":{"start":{"line":5,"column":21},"end":{"line":5,"column":null}},"5":{"start":{"line":8,"column":23},"end":{"line":8,"column":38}},"6":{"start":{"line":10,"column":19},"end":{"line":11,"column":null}},"7":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"8":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"9":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"10":{"start":{"line":18,"column":16},"end":{"line":21,"column":null}},"11":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"12":{"start":{"line":24,"column":21},"end":{"line":24,"column":null}},"13":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"14":{"start":{"line":26,"column":4},"end":{"line":26,"column":null}},"15":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}},"16":{"start":{"line":32,"column":22},"end":{"line":34,"column":null}}},"fnMap":{"0":{"name":"authMiddleware","decl":{"start":{"line":7,"column":22},"end":{"line":7,"column":36}},"loc":{"start":{"line":7,"column":57},"end":{"line":30,"column":null}}},"1":{"name":"(anonymous_4)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":5}},"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":14},"end":{"line":11,"column":null}},"type":"binary-expr","locations":[{"start":{"line":11,"column":14},"end":{"line":11,"column":35}},{"start":{"line":11,"column":35},"end":{"line":11,"column":null}}]},"1":{"loc":{"start":{"line":14,"column":2},"end":{"line":16,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":16,"column":null}}]},"2":{"loc":{"start":{"line":14,"column":6},"end":{"line":14,"column":84}},"type":"binary-expr","locations":[{"start":{"line":14,"column":6},"end":{"line":14,"column":18}},{"start":{"line":14,"column":18},"end":{"line":14,"column":51}},{"start":{"line":14,"column":51},"end":{"line":14,"column":84}}]},"3":{"loc":{"start":{"line":23,"column":2},"end":{"line":27,"column":null}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":27,"column":null}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0],"2":[0,0,0],"3":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/chat.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/chat.ts","statementMap":{"0":{"start":{"line":14,"column":22},"end":{"line":14,"column":null}},"1":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"2":{"start":{"line":19,"column":14},"end":{"line":23,"column":null}},"3":{"start":{"line":24,"column":2},"end":{"line":28,"column":null}}},"fnMap":{"0":{"name":"sendChatMessage","decl":{"start":{"line":14,"column":22},"end":{"line":14,"column":null}},"loc":{"start":{"line":17,"column":22},"end":{"line":29,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":15},"end":{"line":26,"column":50}},"type":"binary-expr","locations":[{"start":{"line":26,"column":15},"end":{"line":26,"column":29}},{"start":{"line":26,"column":48},"end":{"line":26,"column":50}}]}},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0},"b":{"0":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/courses.ts","statementMap":{"0":{"start":{"line":5,"column":13},"end":{"line":5,"column":30}},"1":{"start":{"line":41,"column":22},"end":{"line":41,"column":34}},"2":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"3":{"start":{"line":33,"column":22},"end":{"line":33,"column":38}},"4":{"start":{"line":23,"column":22},"end":{"line":23,"column":32}},"5":{"start":{"line":37,"column":22},"end":{"line":37,"column":31}},"6":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"7":{"start":{"line":5,"column":33},"end":{"line":5,"column":null}},"8":{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},"9":{"start":{"line":30,"column":2},"end":{"line":30,"column":null}},"10":{"start":{"line":34,"column":2},"end":{"line":34,"column":null}},"11":{"start":{"line":38,"column":2},"end":{"line":38,"column":null}},"12":{"start":{"line":42,"column":2},"end":{"line":45,"column":null}}},"fnMap":{"0":{"name":"getCourses","decl":{"start":{"line":23,"column":22},"end":{"line":23,"column":32}},"loc":{"start":{"line":23,"column":76},"end":{"line":27,"column":null}}},"1":{"name":"getCourse","decl":{"start":{"line":29,"column":22},"end":{"line":29,"column":31}},"loc":{"start":{"line":29,"column":70},"end":{"line":31,"column":null}}},"2":{"name":"getCourseLessons","decl":{"start":{"line":33,"column":22},"end":{"line":33,"column":38}},"loc":{"start":{"line":33,"column":77},"end":{"line":35,"column":null}}},"3":{"name":"getLesson","decl":{"start":{"line":37,"column":22},"end":{"line":37,"column":31}},"loc":{"start":{"line":37,"column":70},"end":{"line":39,"column":null}}},"4":{"name":"createCourse","decl":{"start":{"line":41,"column":22},"end":{"line":41,"column":34}},"loc":{"start":{"line":41,"column":70},"end":{"line":46,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":23,"column":33},"end":{"line":23,"column":41}},"type":"default-arg","locations":[{"start":{"line":23,"column":40},"end":{"line":23,"column":41}}]},"1":{"loc":{"start":{"line":23,"column":43},"end":{"line":23,"column":54}},"type":"default-arg","locations":[{"start":{"line":23,"column":51},"end":{"line":23,"column":54}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/debug.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/debug.ts","statementMap":{"0":{"start":{"line":16,"column":22},"end":{"line":16,"column":39}},"1":{"start":{"line":50,"column":22},"end":{"line":50,"column":37}},"2":{"start":{"line":25,"column":22},"end":{"line":25,"column":37}},"3":{"start":{"line":12,"column":22},"end":{"line":12,"column":39}},"4":{"start":{"line":32,"column":22},"end":{"line":32,"column":35}},"5":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"6":{"start":{"line":13,"column":2},"end":{"line":13,"column":null}},"7":{"start":{"line":20,"column":2},"end":{"line":22,"column":null}},"8":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}},"9":{"start":{"line":43,"column":17},"end":{"line":43,"column":null}},"10":{"start":{"line":44,"column":2},"end":{"line":47,"column":null}},"11":{"start":{"line":56,"column":17},"end":{"line":56,"column":null}},"12":{"start":{"line":57,"column":2},"end":{"line":57,"column":null}}},"fnMap":{"0":{"name":"getPipelineHealth","decl":{"start":{"line":12,"column":22},"end":{"line":12,"column":39}},"loc":{"start":{"line":12,"column":60},"end":{"line":14,"column":null}}},"1":{"name":"getDocumentChunks","decl":{"start":{"line":16,"column":22},"end":{"line":16,"column":39}},"loc":{"start":{"line":18,"column":22},"end":{"line":23,"column":null}}},"2":{"name":"getParsedOutput","decl":{"start":{"line":25,"column":22},"end":{"line":25,"column":37}},"loc":{"start":{"line":27,"column":22},"end":{"line":30,"column":null}}},"3":{"name":"testRetrieval","decl":{"start":{"line":32,"column":22},"end":{"line":32,"column":35}},"loc":{"start":{"line":36,"column":22},"end":{"line":48,"column":null}}},"4":{"name":"getEvidencePack","decl":{"start":{"line":50,"column":22},"end":{"line":50,"column":37}},"loc":{"start":{"line":54,"column":22},"end":{"line":58,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":35,"column":2},"end":{"line":35,"column":10}},"type":"default-arg","locations":[{"start":{"line":35,"column":9},"end":{"line":35,"column":10}}]},"1":{"loc":{"start":{"line":53,"column":2},"end":{"line":53,"column":20}},"type":"default-arg","locations":[{"start":{"line":53,"column":11},"end":{"line":53,"column":20}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/documents.ts","statementMap":{"0":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"1":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"2":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"3":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"4":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"5":{"start":{"line":1,"column":35},"end":{"line":1,"column":null}},"6":{"start":{"line":25,"column":2},"end":{"line":27,"column":null}},"7":{"start":{"line":35,"column":19},"end":{"line":35,"column":null}},"8":{"start":{"line":36,"column":2},"end":{"line":36,"column":null}},"9":{"start":{"line":38,"column":2},"end":{"line":42,"column":null}},"10":{"start":{"line":49,"column":2},"end":{"line":51,"column":null}},"11":{"start":{"line":58,"column":2},"end":{"line":61,"column":null}},"12":{"start":{"line":74,"column":2},"end":{"line":88,"column":null}},"13":{"start":{"line":74,"column":15},"end":{"line":74,"column":18}},"14":{"start":{"line":75,"column":4},"end":{"line":86,"column":null}},"15":{"start":{"line":76,"column":21},"end":{"line":76,"column":null}},"16":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"17":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"18":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"19":{"start":{"line":84,"column":8},"end":{"line":84,"column":null}},"20":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"21":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}},"22":{"start":{"line":89,"column":2},"end":{"line":89,"column":null}}},"fnMap":{"0":{"name":"getDocuments","decl":{"start":{"line":21,"column":22},"end":{"line":21,"column":34}},"loc":{"start":{"line":23,"column":22},"end":{"line":28,"column":null}}},"1":{"name":"uploadDocument","decl":{"start":{"line":30,"column":22},"end":{"line":30,"column":36}},"loc":{"start":{"line":33,"column":22},"end":{"line":43,"column":null}}},"2":{"name":"getDocumentStatus","decl":{"start":{"line":45,"column":22},"end":{"line":45,"column":39}},"loc":{"start":{"line":47,"column":22},"end":{"line":52,"column":null}}},"3":{"name":"deleteDocument","decl":{"start":{"line":54,"column":22},"end":{"line":54,"column":36}},"loc":{"start":{"line":56,"column":22},"end":{"line":62,"column":null}}},"4":{"name":"pollDocumentStatus","decl":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"loc":{"start":{"line":72,"column":18},"end":{"line":90,"column":null}}},"5":{"name":"(anonymous_11)","decl":{"start":{"line":87,"column":22},"end":{"line":87,"column":23}},"loc":{"start":{"line":87,"column":35},"end":{"line":87,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":71,"column":2},"end":{"line":71,"column":19}},"type":"default-arg","locations":[{"start":{"line":71,"column":15},"end":{"line":71,"column":19}}]},"1":{"loc":{"start":{"line":72,"column":2},"end":{"line":72,"column":18}},"type":"default-arg","locations":[{"start":{"line":72,"column":16},"end":{"line":72,"column":18}}]},"2":{"loc":{"start":{"line":77,"column":6},"end":{"line":79,"column":null}},"type":"if","locations":[{"start":{"line":77,"column":6},"end":{"line":79,"column":null}}]},"3":{"loc":{"start":{"line":77,"column":10},"end":{"line":77,"column":66}},"type":"binary-expr","locations":[{"start":{"line":77,"column":10},"end":{"line":77,"column":39}},{"start":{"line":77,"column":39},"end":{"line":77,"column":66}}]},"4":{"loc":{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":85,"column":null}},{"start":{"line":83,"column":13},"end":{"line":85,"column":null}}]},"5":{"loc":{"start":{"line":81,"column":10},"end":{"line":81,"column":56}},"type":"binary-expr","locations":[{"start":{"line":81,"column":10},"end":{"line":81,"column":33}},{"start":{"line":81,"column":37},"end":{"line":81,"column":56}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0],"1":[0],"2":[0],"3":[0,0],"4":[0,0],"5":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/progress.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/progress.ts","statementMap":{"0":{"start":{"line":60,"column":22},"end":{"line":60,"column":39}},"1":{"start":{"line":94,"column":22},"end":{"line":94,"column":35}},"2":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"3":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"4":{"start":{"line":39,"column":2},"end":{"line":47,"column":null}},"5":{"start":{"line":51,"column":2},"end":{"line":57,"column":null}},"6":{"start":{"line":64,"column":14},"end":{"line":64,"column":null}},"7":{"start":{"line":65,"column":2},"end":{"line":65,"column":null}},"8":{"start":{"line":73,"column":14},"end":{"line":77,"column":null}},"9":{"start":{"line":79,"column":13},"end":{"line":79,"column":32}},"10":{"start":{"line":80,"column":13},"end":{"line":80,"column":32}},"11":{"start":{"line":81,"column":13},"end":{"line":81,"column":64}},"12":{"start":{"line":83,"column":2},"end":{"line":91,"column":null}},"13":{"start":{"line":95,"column":14},"end":{"line":95,"column":null}},"14":{"start":{"line":96,"column":2},"end":{"line":101,"column":null}},"15":{"start":{"line":96,"column":28},"end":{"line":101,"column":null}}},"fnMap":{"0":{"name":"mapProgress","decl":{"start":{"line":38,"column":9},"end":{"line":38,"column":21}},"loc":{"start":{"line":38,"column":49},"end":{"line":48,"column":null}}},"1":{"name":"mapBadge","decl":{"start":{"line":50,"column":9},"end":{"line":50,"column":18}},"loc":{"start":{"line":50,"column":46},"end":{"line":58,"column":null}}},"2":{"name":"getCourseProgress","decl":{"start":{"line":60,"column":22},"end":{"line":60,"column":39}},"loc":{"start":{"line":62,"column":22},"end":{"line":66,"column":null}}},"3":{"name":"markLessonComplete","decl":{"start":{"line":68,"column":22},"end":{"line":68,"column":40}},"loc":{"start":{"line":71,"column":22},"end":{"line":92,"column":null}}},"4":{"name":"getUserBadges","decl":{"start":{"line":94,"column":22},"end":{"line":94,"column":35}},"loc":{"start":{"line":94,"column":56},"end":{"line":102,"column":null}}},"5":{"name":"(anonymous_9)","decl":{"start":{"line":96,"column":17},"end":{"line":96,"column":18}},"loc":{"start":{"line":96,"column":28},"end":{"line":101,"column":null}}}},"branchMap":{"0":{"loc":{"start":{"line":46,"column":24},"end":{"line":46,"column":68}},"type":"binary-expr","locations":[{"start":{"line":46,"column":24},"end":{"line":46,"column":49}},{"start":{"line":46,"column":66},"end":{"line":46,"column":68}}]},"1":{"loc":{"start":{"line":81,"column":13},"end":{"line":81,"column":64}},"type":"binary-expr","locations":[{"start":{"line":81,"column":13},"end":{"line":81,"column":28}},{"start":{"line":81,"column":62},"end":{"line":81,"column":64}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"b":{"0":[0,0],"1":[0,0]}} +,"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/study.ts": {"path":"/Volumes/TOSHIBA EXT/BitPolito/Academy/bitcoin-academy/apps/web/src/lib/services/study.ts","statementMap":{"0":{"start":{"line":6,"column":22},"end":{"line":6,"column":null}},"1":{"start":{"line":1,"column":25},"end":{"line":1,"column":null}},"2":{"start":{"line":12,"column":32},"end":{"line":12,"column":null}},"3":{"start":{"line":13,"column":2},"end":{"line":17,"column":null}}},"fnMap":{"0":{"name":"sendStudyAction","decl":{"start":{"line":6,"column":22},"end":{"line":6,"column":null}},"loc":{"start":{"line":10,"column":22},"end":{"line":18,"column":null}}}},"branchMap":{},"s":{"0":0,"1":2,"2":0,"3":0},"f":{"0":0},"b":{}} } diff --git a/apps/web/coverage/lcov-report/index.html b/apps/web/coverage/lcov-report/index.html index 31ce679..52bbdcf 100644 --- a/apps/web/coverage/lcov-report/index.html +++ b/apps/web/coverage/lcov-report/index.html @@ -23,30 +23,30 @@

All files

- 15.73% + 12.12% Statements - 101/642 + 167/1377
- 27.47% + 15.33% Branches - 86/313 + 136/887
- 8.82% + 6.28% Functions - 12/136 + 24/382
- 16.88% + 13.26% Lines - 101/598 + 165/1244
@@ -99,13 +99,13 @@

All files

0% - 0/18 + 0/25 0% 0/6 0% - 0/3 + 0/5 0% - 0/17 + 0/23 @@ -114,43 +114,43 @@

All files

0% - 0/1 + 0/2 100% 0/0 0% 0/1 0% - 0/1 + 0/2 src/app/(auth)/login - +
- 97.56% - 40/41 - 100% - 30/30 + 97.87% + 46/47 + 96.96% + 32/33 100% - 5/5 - 97.56% - 40/41 + 6/6 + 97.82% + 45/46 - src/app/(auth)/signup - -
+ src/app/(auth)/signup + +
- 93.84% - 61/65 - 84.84% - 56/66 - 100% - 7/7 - 93.84% - 61/65 + 68.49% + 50/73 + 66.66% + 48/72 + 87.5% + 7/8 + 68.05% + 49/72 @@ -174,13 +174,13 @@

All files

0% - 0/28 + 0/74 0% - 0/10 + 0/26 0% - 0/6 + 0/23 0% - 0/27 + 0/71 @@ -189,43 +189,58 @@

All files

0% - 0/44 + 0/95 0% - 0/15 + 0/68 0% - 0/12 + 0/31 0% - 0/42 + 0/81 - src/app/courses/[courseId]/documents/[documentId]/preview + src/app/courses/[courseId]/debug
0% - 0/29 + 0/46 0% - 0/20 + 0/24 0% - 0/7 + 0/11 0% - 0/29 + 0/44 - src/app/courses/[courseId]/study + src/app/courses/[courseId]/documents/[documentId]/preview
0% - 0/22 + 0/72 0% - 0/2 + 0/57 0% - 0/3 + 0/26 0% - 0/21 + 0/65 + + + + src/app/courses/[courseId]/study + +
+ + 19.71% + 14/71 + 0% + 0/18 + 8.33% + 1/12 + 21.87% + 14/64 @@ -234,13 +249,13 @@

All files

0% - 0/24 + 0/28 0% 0/11 0% - 0/4 + 0/6 0% - 0/23 + 0/27 @@ -249,13 +264,13 @@

All files

0% - 0/64 - 0% 0/39 0% - 0/18 + 0/40 0% - 0/57 + 0/12 + 0% + 0/36 @@ -264,13 +279,13 @@

All files

0% - 0/94 + 0/198 0% - 0/27 + 0/107 0% - 0/24 + 0/52 0% - 0/81 + 0/169 @@ -279,43 +294,58 @@

All files

0% - 0/4 - 100% - 0/0 + 0/12 0% 0/1 0% 0/3 + 0% + 0/11 src/components/study - -
+ +
- 0% - 0/26 - 0% - 0/7 - 0% - 0/6 - 0% - 0/25 + 19.37% + 50/258 + 15.64% + 41/262 + 9% + 9/100 + 22.02% + 50/227 + + + + src/components/ui + +
+ + 6.75% + 5/74 + 22.64% + 12/53 + 4% + 1/25 + 7.35% + 5/68 src/lib - -
+ +
- 0% - 0/20 - 0% - 0/17 + 5% + 1/20 + 17.64% + 3/17 0% 0/4 - 0% - 0/19 + 5.26% + 1/19 @@ -324,13 +354,13 @@

All files

0% - 0/76 + 0/108 0% - 0/20 + 0/36 0% - 0/19 + 0/26 0% - 0/69 + 0/96 @@ -339,13 +369,13 @@

All files

0% - 0/29 + 0/37 0% - 0/25 + 0/30 0% - 0/4 + 0/5 0% - 0/27 + 0/33 @@ -365,17 +395,17 @@

All files

src/lib/services - -
+ +
+ 1.36% + 1/73 0% - 0/32 - 0% - 0/11 - 0% - 0/10 + 0/19 0% - 0/30 + 0/24 + 1.44% + 1/69 @@ -386,7 +416,7 @@

All files

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html new file mode 100644 index 0000000..bc86fe6 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html @@ -0,0 +1,802 @@ + + + + + + Code coverage report for src/app/courses/[courseId]/debug/page.tsx + + + + + + + + + +
+
+

All files / src/app/courses/[courseId]/debug page.tsx

+
+ +
+ 0% + Statements + 0/46 +
+ + +
+ 0% + Branches + 0/24 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 0% + Lines + 0/44 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import { useCallback, useEffect, useState } from 'react';
+import { useParams, useRouter } from 'next/navigation';
+import { useSession } from 'next-auth/react';
+import {
+  getPipelineHealth,
+  testRetrieval,
+  getEvidencePack,
+  type PipelineHealth,
+} from '@/lib/services/debug';
+import type { EvidencePack } from '@/lib/api/types';
+ 
+export default function DebugPage() {
+  const params = useParams();
+  const router = useRouter();
+  const courseId = params.courseId as string;
+  const { data: session } = useSession();
+  const accessToken = session?.user?.accessToken;
+ 
+  const [health, setHealth] = useState<PipelineHealth | null>(null);
+  const [healthError, setHealthError] = useState<string | null>(null);
+ 
+  const [query, setQuery] = useState('');
+  const [action, setAction] = useState('explain');
+  const [retrievalResult, setRetrievalResult] = useState<Record<string, unknown> | null>(null);
+  const [evidencePack, setEvidencePack] = useState<EvidencePack | null>(null);
+  const [querying, setQuerying] = useState(false);
+ 
+  const loadHealth = useCallback(async () => {
+    try {
+      const h = await getPipelineHealth(accessToken);
+      setHealth(h);
+    } catch (err) {
+      setHealthError(err instanceof Error ? err.message : 'Failed to load health');
+    }
+  }, [accessToken]);
+ 
+  useEffect(() => {
+    loadHealth();
+  }, [loadHealth]);
+ 
+  async function handleTestRetrieval() {
+    Iif (!query.trim()) return;
+    setQuerying(true);
+    try {
+      const result = await testRetrieval(courseId, query, 5, accessToken);
+      setRetrievalResult(result as unknown as Record<string, unknown>);
+    } catch (err) {
+      setRetrievalResult({ error: err instanceof Error ? err.message : 'Failed' });
+    } finally {
+      setQuerying(false);
+    }
+  }
+ 
+  async function handleGetEvidence() {
+    Iif (!query.trim()) return;
+    setQuerying(true);
+    try {
+      const pack = await getEvidencePack(courseId, query, action, accessToken);
+      setEvidencePack(pack);
+    } catch (err) {
+      setEvidencePack(null);
+    } finally {
+      setQuerying(false);
+    }
+  }
+ 
+  return (
+    <main className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-8">
+      <div className="flex items-center gap-3">
+        <button
+          onClick={() => router.push(`/courses/${courseId}`)}
+          className="text-sm text-gray-500 hover:text-gray-700 flex items-center gap-1"
+        >
+          <svg
+            className="h-4 w-4"
+            fill="none"
+            viewBox="0 0 24 24"
+            strokeWidth={2}
+            stroke="currentColor"
+          >
+            <path
+              strokeLinecap="round"
+              strokeLinejoin="round"
+              d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
+            />
+          </svg>
+          Back
+        </button>
+        <h1 className="text-xl font-bold text-gray-900">Debug Inspector</h1>
+        <span className="px-2 py-0.5 text-xs bg-yellow-100 text-yellow-800 rounded font-medium">
+          DEV ONLY
+        </span>
+      </div>
+ 
+      {/* Pipeline health */}
+      <section className="bg-white rounded-lg shadow">
+        <div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
+          <h2 className="text-base font-semibold text-gray-900">Pipeline Health</h2>
+          <button
+            onClick={loadHealth}
+            className="text-xs text-gray-500 hover:text-gray-700 underline"
+          >
+            Refresh
+          </button>
+        </div>
+        <div className="p-6">
+          {healthError && <p className="text-sm text-red-600">{healthError}</p>}
+          {health && (
+            <div className="space-y-3 text-sm">
+              <div className="flex items-center gap-2">
+                <span
+                  className={`h-2 w-2 rounded-full ${health.chroma_status === 'ok' ? 'bg-green-500' : 'bg-red-500'}`}
+                />
+                <span className="font-medium">ChromaDB:</span>
+                <span className="text-gray-600">
+                  {health.chroma_status} · {health.chroma_db_path}
+                </span>
+              </div>
+              <div>
+                <span className="font-medium">Collections:</span>
+                <div className="mt-1 flex flex-wrap gap-2">
+                  {Object.entries(health.collection_sizes).map(([name, count]) => (
+                    <span key={name} className="px-2 py-0.5 bg-gray-100 rounded text-xs">
+                      {name}: {count} chunks
+                    </span>
+                  ))}
+                  {Object.keys(health.collection_sizes).length === 0 && (
+                    <span className="text-gray-400 text-xs">No collections</span>
+                  )}
+                </div>
+              </div>
+              <div className="flex gap-4 text-xs text-gray-500">
+                <span>Uploads: {health.uploads_dir_size_mb} MB</span>
+                <span>Python: {health.python_version.split(' ')[0]}</span>
+              </div>
+            </div>
+          )}
+          {!health && !healthError && (
+            <p className="text-sm text-gray-400 animate-pulse">Loading…</p>
+          )}
+        </div>
+      </section>
+ 
+      {/* Retrieval test */}
+      <section className="bg-white rounded-lg shadow">
+        <div className="px-6 py-4 border-b border-gray-100">
+          <h2 className="text-base font-semibold text-gray-900">Retrieval Test</h2>
+        </div>
+        <div className="p-6 space-y-4">
+          <div className="flex gap-3">
+            <input
+              type="text"
+              value={query}
+              onChange={(e) => setQuery(e.target.value)}
+              placeholder="Enter a query…"
+              className="flex-1 rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-orange-500 focus:outline-none focus:ring-1 focus:ring-orange-500"
+            />
+            <select
+              value={action}
+              onChange={(e) => setAction(e.target.value)}
+              className="rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-orange-500 focus:outline-none"
+            >
+              {['explain', 'summarize', 'retrieve', 'open_questions', 'quiz', 'oral'].map((a) => (
+                <option key={a} value={a}>
+                  {a}
+                </option>
+              ))}
+            </select>
+          </div>
+          <div className="flex gap-2">
+            <button
+              onClick={handleTestRetrieval}
+              disabled={querying || !query.trim()}
+              className="px-4 py-2 text-sm font-medium text-white bg-gray-700 rounded-md hover:bg-gray-800 disabled:opacity-40"
+            >
+              Raw Retrieval
+            </button>
+            <button
+              onClick={handleGetEvidence}
+              disabled={querying || !query.trim()}
+              className="px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700 disabled:opacity-40"
+            >
+              Evidence Pack
+            </button>
+          </div>
+ 
+          {retrievalResult && (
+            <div>
+              <p className="text-xs font-medium text-gray-500 mb-2">
+                Raw candidates ({(retrievalResult as any).total ?? 0}):
+              </p>
+              <pre className="bg-gray-50 rounded-md p-4 text-xs text-gray-700 overflow-auto max-h-80">
+                {JSON.stringify(retrievalResult, null, 2)}
+              </pre>
+            </div>
+          )}
+ 
+          {evidencePack && (
+            <div>
+              <p className="text-xs font-medium text-gray-500 mb-2">
+                Evidence pack · {evidencePack.chunks.length} chunks (from{' '}
+                {evidencePack.total_candidates} candidates):
+              </p>
+              <div className="space-y-2">
+                {evidencePack.chunks.map((chunk, i) => (
+                  <div
+                    key={chunk.chunk_id}
+                    className="rounded-md border border-gray-200 p-3 text-xs"
+                  >
+                    <div className="flex justify-between mb-1">
+                      <span className="font-medium text-gray-700">
+                        [{i + 1}] {chunk.anchor.doc_name}
+                      </span>
+                      <span className="text-orange-600 font-medium">
+                        {Math.round(chunk.score * 100)}%
+                      </span>
+                    </div>
+                    {chunk.anchor.section && (
+                      <p className="text-gray-500 mb-1">{chunk.anchor.section}</p>
+                    )}
+                    <p className="text-gray-700">
+                      {chunk.text.slice(0, 300)}
+                      {chunk.text.length > 300 ? '…' : ''}
+                    </p>
+                    <p className="mt-1 text-gray-400">
+                      {chunk.chunk_id} · {chunk.anchor.chunk_type}
+                    </p>
+                  </div>
+                ))}
+              </div>
+            </div>
+          )}
+        </div>
+      </section>
+    </main>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html index 833d4f2..ece2f84 100644 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html +++ b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html @@ -25,28 +25,28 @@

All files src/app/courses/[cou
0% Statements - 0/29 + 0/72
0% Branches - 0/20 + 0/57
0% Functions - 0/7 + 0/26
0% Lines - 0/29 + 0/65
@@ -84,13 +84,13 @@

All files src/app/courses/[cou
0% - 0/29 + 0/72 0% - 0/20 + 0/57 0% - 0/7 + 0/26 0% - 0/29 + 0/65 @@ -101,7 +101,7 @@

All files src/app/courses/[cou + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/index.html b/apps/web/coverage/lcov-report/src/app/courses/index.html index 8442eba..74f7dd1 100644 --- a/apps/web/coverage/lcov-report/src/app/courses/index.html +++ b/apps/web/coverage/lcov-report/src/app/courses/index.html @@ -25,28 +25,28 @@

All files src/app/courses

0% Statements - 0/28 + 0/74
0% Branches - 0/10 + 0/26
0% Functions - 0/6 + 0/23
0% Lines - 0/27 + 0/71
@@ -79,18 +79,48 @@

All files src/app/courses

+ error.tsx + +
+ + 0% + 0/4 + 100% + 0/0 + 0% + 0/2 + 0% + 0/4 + + + + layout.tsx + +
+ + 0% + 0/3 + 100% + 0/0 + 0% + 0/1 + 0% + 0/3 + + + page.tsx
0% - 0/28 + 0/67 0% - 0/10 + 0/26 0% - 0/6 + 0/20 0% - 0/27 + 0/64 @@ -101,7 +131,7 @@

All files src/app/courses

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html index b1f303a..c22d56a 100644 --- a/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html +++ b/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html @@ -25,28 +25,28 @@

All files / src/app/c
0% Statements - 0/28 + 0/67
0% Branches - 0/10 + 0/26
0% Functions - 0/6 + 0/20
0% Lines - 0/27 + 0/64
@@ -167,7 +167,144 @@

All files / src/app/c 102 103 104 -105  +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242        @@ -175,7 +312,21 @@

All files / src/app/c       +  +  +  +  +  +    +  +  +  +  +  +  +  +        @@ -184,38 +335,132 @@

All files / src/app/c       +          +  +              +  +  +        +  +  +      +  +        +  +  +  +  +  +  +    +  +        +  +  +  +      +  +    +    +      +          +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +        @@ -259,10 +504,17 @@

All files / src/app/c       +          +  +  +  +  +  +        @@ -271,21 +523,68 @@

All files / src/app/c       +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +   
'use client';
  
 import { useEffect, useState } from 'react';
 import { useSession } from 'next-auth/react';
 import { useRouter } from 'next/navigation';
-import Link from 'next/link';
-import { getCourses, type Course } from '@/lib/services/courses';
+import { getCourses, createCourse, MVP_COURSES_LIMIT, type Course } from '@/lib/services/courses';
+import { getDocumentListRows } from '@/lib/api/documents';
 import { CourseCard } from '@/components/courses/CourseCard';
+import { CreateCourseModal } from '@/components/courses/CreateCourseModal';
+ 
+type Filter = 'all';
+ 
+interface DocStats {
+  total: number;
+  ready: number;
+  processing: number;
+  error: number;
+}
  
 export default function CoursesPage() {
   const { data: session, status } = useSession();
   const router = useRouter();
   const [courses, setCourses] = useState<Course[]>([]);
+  const [docStats, setDocStats] = useState<Record<string | number, DocStats>>({});
+  const [globalStats, setGlobalStats] = useState({ docs: 0, indexed: 0, processing: 0 });
   const [loading, setLoading] = useState(true);
   const [error, setError] = useState<string | null>(null);
+  const [_filter] = useState<Filter>('all');
+  const [showCreate, setShowCreate] = useState(false);
+ 
+  useEffect(() => {
+    function onKey(e: KeyboardEvent) {
+      Iif ((e.metaKey || e.ctrlKey) && e.key === 'n') {
+        e.preventDefault();
+        setShowCreate(true);
+      }
+    }
+    window.addEventListener('keydown', onKey);
+    return () => window.removeEventListener('keydown', onKey);
+  }, []);
  
   useEffect(() => {
     Iif (status === 'unauthenticated') {
@@ -294,84 +593,196 @@ 

All files / src/app/c } Iif (status !== 'authenticated') return;   - async function fetchCourses() { + const token = session?.user?.accessToken; +  + async function fetchAll() { try { - const data = await getCourses(0, 100, (session?.user as any)?.accessToken); + const data = await getCourses(0, MVP_COURSES_LIMIT, token); setCourses(data); +  + const docsResults = await Promise.allSettled( + data.map((c) => getDocumentListRows(String(c.id), token)) + ); +  + const statsMap: Record<string | number, DocStats> = {}; + let totalDocs = 0, + totalIndexed = 0, + totalProcessing = 0; + docsResults.forEach((r, i) => { + Iif (r.status === 'fulfilled') { + const docs = r.value; + const stats: DocStats = { + total: docs.length, + ready: docs.filter((d) => d.status === 'ready').length, + processing: docs.filter((d) => d.status === 'processing' || d.status === 'uploading') + .length, + error: docs.filter((d) => d.status === 'error').length, + }; + statsMap[data[i].id] = stats; + totalDocs += stats.total; + totalIndexed += stats.ready; + totalProcessing += stats.processing; + } + }); + setDocStats(statsMap); + setGlobalStats({ docs: totalDocs, indexed: totalIndexed, processing: totalProcessing }); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load courses'); } finally { setLoading(false); } } -  - fetchCourses(); + fetchAll(); }, [status, session, router]); +  + async function handleCreate(title: string, description?: string) { + const created = await createCourse(title, description); + setCourses((prev) => [...prev, created]); + router.push(`/courses/${created.id}`); + }   Iif (status === 'loading' || (status === 'authenticated' && loading)) { return ( - <div className="min-h-screen bg-gray-50"> - <header className="bg-white shadow-sm"> - <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> - <div className="h-7 w-48 bg-gray-200 rounded animate-pulse" /> - </div> - </header> - <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> + <main className="page-fade max-w-8xl mx-auto px-6 py-8"> + <div className="animate-pulse space-y-8"> + <div className="h-10 w-1/2 bg-blue-dark/10 rounded" /> + <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5"> {[1, 2, 3].map((i) => ( - <div key={i} className="bg-white rounded-lg shadow p-6 animate-pulse"> - <div className="h-5 w-3/4 bg-gray-200 rounded" /> - <div className="mt-3 h-4 w-full bg-gray-100 rounded" /> - <div className="mt-1 h-4 w-2/3 bg-gray-100 rounded" /> - <div className="mt-5 h-4 w-1/3 bg-orange-100 rounded" /> + <div key={i} className="b-hard rounded-lg p-5 space-y-4" style={{ minHeight: 238 }}> + <div className="h-3 w-1/3 bg-blue-dark/10 rounded" /> + <div className="h-20 bg-blue-dark/5 rounded" /> + <div className="h-5 w-3/4 bg-blue-dark/10 rounded" /> </div> ))} </div> - </main> - </div> + </div> + </main> ); }   return ( - <div className="min-h-screen bg-gray-50"> - <header className="bg-white shadow-sm"> - <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center"> - <div> - <Link href="/dashboard" className="text-sm text-gray-500 hover:text-gray-700"> - &larr; Dashboard - </Link> - <h1 className="text-2xl font-bold text-gray-900 mt-1">Courses</h1> + <main className="page-fade max-w-8xl mx-auto px-6 py-8"> + {/* Hero */} + <div className="grid grid-cols-12 gap-6 mb-10"> + <div className="col-span-12 lg:col-span-8"> + <div className="flex items-center gap-2 font-mono text-[11px] tracking-[0.12em] uppercase opacity-70 mb-6"> + <span>Academy</span> + <span className="opacity-40">/</span> + <span className="font-semibold opacity-100">Courses</span> + </div> + <h1 className="text-5xl lg:text-6xl font-medium tracking-tight leading-[1.05] mb-5"> + Study, grounded in your + <br className="hidden lg:block" /> own course material. + </h1> + <p className="text-lg leading-relaxed max-w-[58ch] opacity-80"> + Each course is an isolated workspace. Drop in slides, notes and past exams — Academy + indexes everything and keeps every answer anchored to its source. + </p> + <div className="flex items-center gap-3 mt-6"> + <span className="font-mono text-[11px] opacity-60"> + {courses.length} {courses.length === 1 ? 'course' : 'courses'} · {globalStats.docs}{' '} + documents · {globalStats.indexed} indexed + </span> </div> </div> - </header>   - <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> - {error ? ( - <div className="rounded-lg border border-red-200 bg-red-50 p-6 text-center"> - <p className="text-sm text-red-700">{error}</p> - <button - onClick={() => window.location.reload()} - className="mt-3 text-sm font-medium text-red-700 hover:text-red-800 underline" - > - Retry - </button> - </div> - ) : courses.length === 0 ? ( - <div className="text-center py-16"> - <svg className="mx-auto h-12 w-12 text-gray-300" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor"> - <path strokeLinecap="round" strokeLinejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" /> - </svg> - <h3 className="mt-4 text-lg font-medium text-gray-900">No courses available</h3> - <p className="mt-1 text-sm text-gray-500">Courses will appear here once they are created.</p> + {/* Stats widget */} + <div className="col-span-12 lg:col-span-4"> + <div className="b-hard rounded-lg p-5 bg-white dark:bg-blue-dark/40 tick-corners"> + <div className="flex items-end justify-between b-thin-b pb-1.5 mb-3"> + <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70"> + Local index · QVAC + </span> + <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-60"> + v0.1 MVP + </span> + </div> + <div className="grid grid-cols-2 gap-3"> + <StatBox n={String(courses.length)} k="courses" /> + <StatBox n={String(globalStats.docs)} k="documents" /> + <StatBox n={String(globalStats.indexed)} k="indexed" /> + <StatBox + n={String(globalStats.processing)} + k="processing" + warn={globalStats.processing > 0} + /> + </div> + <div className="mt-4 pt-4 b-thin-t flex items-center justify-between"> + <span className="font-mono text-[11px] opacity-70"> + Local-first · all data on device + </span> + <span className="font-mono text-[10px] tracking-[0.2em] uppercase">v0.1</span> + </div> </div> - ) : ( - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> - {courses.map((course) => ( - <CourseCard key={course.id} course={course} /> - ))} + </div> + </div> +  + {/* Filter rail */} + <div className="flex items-center gap-2 mb-4"> + <button className="font-mono text-[11px] tracking-[0.18em] uppercase px-3 h-8 rounded-md bg-blue-dark text-white dark:bg-white dark:text-blue-dark"> + All <span className="opacity-60 ml-1">{courses.length}</span> + </button> + <div className="ml-auto font-mono text-[11px] opacity-60">sorted · last updated</div> + </div> +  + {error ? ( + <div + className="b-hard rounded-lg p-6 text-center" + style={{ borderColor: '#b3261e', color: '#b3261e' }} + > + <p className="text-sm">{error}</p> + <button + onClick={() => window.location.reload()} + className="mt-3 text-sm font-medium underline" + > + Retry + </button> + </div> + ) : courses.length === 0 ? ( + <div className="b-hard rounded-lg p-10 text-center bg-white dark:bg-blue-dark"> + <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" /> + <div className="font-medium text-lg">No courses yet</div> + <div className="opacity-70 text-sm mt-1 mb-5"> + Create your first course to get started. </div> - )} - </main> + <button className="btn-primary" onClick={() => setShowCreate(true)}> + Create workspace → + </button> + </div> + ) : ( + <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5"> + {courses.map((course) => ( + <CourseCard key={course.id} course={course} stats={docStats[course.id] ?? null} /> + ))} + {/* Create course card */} + <button + onClick={() => setShowCreate(true)} + className="b-hard rounded-lg p-6 stripes hover-card flex flex-col items-center justify-center min-h-[238px] text-center w-full" + > + <div className="font-mono text-3xl leading-none mb-2">+</div> + <div className="font-medium">Create new course</div> + <div className="font-mono text-[11px] opacity-70 mt-1">⌘N</div> + </button> + </div> + )} +  + {showCreate && ( + <CreateCourseModal onClose={() => setShowCreate(false)} onCreate={handleCreate} /> + )} + </main> + ); +} +  +function StatBox({ n, k, warn }: { n: string; k: string; warn?: boolean }) { + return ( + <div className="b-thin rounded-md p-3"> + <div + className={`text-2xl font-medium tnum ${warn ? '' : ''}`} + style={warn ? { color: '#a55a00' } : {}} + > + {n} + </div> + <div className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70 mt-1">{k}</div> </div> ); } @@ -382,7 +793,7 @@

All files / src/app/c + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/dashboard/index.html b/apps/web/coverage/lcov-report/src/app/dashboard/index.html index 33f75fc..be14600 100644 --- a/apps/web/coverage/lcov-report/src/app/dashboard/index.html +++ b/apps/web/coverage/lcov-report/src/app/dashboard/index.html @@ -25,7 +25,7 @@

All files src/app/dashboard

0% Statements - 0/24 + 0/28
@@ -39,14 +39,14 @@

All files src/app/dashboard

0% Functions - 0/4 + 0/6
0% Lines - 0/23 + 0/27
@@ -79,6 +79,21 @@

All files src/app/dashboard

+ error.tsx + +
+ + 0% + 0/4 + 100% + 0/0 + 0% + 0/2 + 0% + 0/4 + + + page.tsx
@@ -101,7 +116,7 @@

All files src/app/dashboard

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/index.html b/apps/web/coverage/lcov-report/src/app/index.html index 40cc476..b473467 100644 --- a/apps/web/coverage/lcov-report/src/app/index.html +++ b/apps/web/coverage/lcov-report/src/app/index.html @@ -25,7 +25,7 @@

All files src/app

0% Statements - 0/18 + 0/25
@@ -39,14 +39,14 @@

All files src/app

0% Functions - 0/3 + 0/5
0% Lines - 0/17 + 0/23
@@ -79,18 +79,33 @@

All files src/app

+ error.tsx + +
+ + 0% + 0/4 + 100% + 0/0 + 0% + 0/2 + 0% + 0/4 + + + layout.tsx
0% - 0/5 + 0/8 100% 0/0 0% 0/1 0% - 0/4 + 0/6 @@ -116,7 +131,7 @@

All files src/app

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html b/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html index 9a7497e..1522396 100644 --- a/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html +++ b/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html @@ -32,7 +32,7 @@

All files / src/compo
0% Branches - 0/8 + 0/7
@@ -127,7 +127,11 @@

All files / src/compo 62 63 64 -65  +65 +66  +  +  +        @@ -189,8 +193,6 @@

All files / src/compo       -  -   
'use client';
  
 import type { DocumentStatus, ProcessingStage } from '@/lib/api/types';
@@ -201,7 +203,10 @@ 

All files / src/compo className?: string; }   -const STATUS_CONFIG: Record<DocumentStatus, { label: string; bg: string; text: string; dot: string }> = { +const STATUS_CONFIG: Record< + DocumentStatus, + { label: string; bg: string; text: string; dot: string } +> = { uploading: { label: 'Uploading', bg: 'bg-blue-50', @@ -249,9 +254,7 @@

All files / src/compo > <span className={`h-1.5 w-1.5 rounded-full ${config.dot}`} /> {config.label} - {stageLabel && status === 'processing' && ( - <span className="opacity-75">· {stageLabel}</span> - )} + {stageLabel && <span className="opacity-75">· {stageLabel}</span>} </span> ); } @@ -262,7 +265,7 @@

All files / src/compo + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/providers/index.html b/apps/web/coverage/lcov-report/src/components/providers/index.html index 7a659c4..680132f 100644 --- a/apps/web/coverage/lcov-report/src/components/providers/index.html +++ b/apps/web/coverage/lcov-report/src/components/providers/index.html @@ -25,28 +25,28 @@

All files src/components/providers

0% Statements - 0/4 + 0/12
- 100% + 0% Branches - 0/0 + 0/1
0% Functions - 0/1 + 0/3
0% Lines - 0/3 + 0/11
@@ -93,6 +93,21 @@

All files src/components/providers

0/3 + + SessionErrorGuard.tsx + +
+ + 0% + 0/8 + 0% + 0/1 + 0% + 0/2 + 0% + 0/8 + +

@@ -101,7 +116,7 @@

All files src/components/providers

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html b/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html new file mode 100644 index 0000000..3cf4107 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html @@ -0,0 +1,589 @@ + + + + + + Code coverage report for src/components/study/ContentChunks.tsx + + + + + + + + + +
+
+

All files / src/components/study ContentChunks.tsx

+
+ +
+ 9.09% + Statements + 3/33 +
+ + +
+ 0% + Branches + 0/22 +
+ + +
+ 0% + Functions + 0/13 +
+ + +
+ 9.37% + Lines + 3/32 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169  +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import { useEffect, useState } from 'react';
+import { getDocuments } from '@/lib/services/documents';
+import { getDocumentPreviewView } from '@/lib/api/documents';
+ 
+// Typed shapes for document preview data returned by the backend
+interface Section {
+  title?: string;
+  level?: number;
+  page?: number;
+}
+ 
+interface Chunk {
+  text: string;
+  section?: string;
+  page?: number;
+}
+ 
+interface DocumentContent {
+  documentId: string;
+  filename: string;
+  sections: Section[];
+  chunks: Chunk[];
+}
+ 
+interface ContentChunksProps {
+  courseId: string;
+  accessToken?: string;
+  className?: string;
+  activeCitationDocIds?: Set<string>;
+}
+ 
+export function ContentChunks({
+  courseId,
+  accessToken,
+  className,
+  activeCitationDocIds,
+}: ContentChunksProps) {
+  const [contents, setContents] = useState<DocumentContent[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [error, setError] = useState<string | null>(null);
+ 
+  useEffect(() => {
+    async function fetchContent() {
+      try {
+        const docs = await getDocuments(courseId, accessToken);
+        const readyDocs = docs.filter((d) => d.status === 'ready');
+ 
+        const previews = await Promise.allSettled(
+          readyDocs.map(async (doc) => {
+            const preview = await getDocumentPreviewView(doc.id, accessToken);
+            return {
+              documentId: doc.id,
+              filename: doc.filename,
+              sections: (preview.sections ?? []).map((title) => ({ title })),
+              chunks: (preview.sampleChunks ?? []).map((c) => ({
+                text: c.text,
+                section: c.section ?? undefined,
+              })),
+            };
+          })
+        );
+ 
+        const loaded = previews
+          .flatMap((r) => (r.status === 'fulfilled' ? [r.value] : []))
+          .filter((d) => d.chunks.length > 0 || d.sections.length > 0);
+ 
+        setContents(loaded);
+      } catch (err) {
+        setError(err instanceof Error ? err.message : 'Failed to load course material');
+      } finally {
+        setLoading(false);
+      }
+    }
+ 
+    fetchContent();
+  }, [courseId, accessToken]);
+ 
+  Iif (loading) {
+    return (
+      <div className={className} aria-label="Loading course material">
+        <div className="space-y-3">
+          {[1, 2, 3].map((i) => (
+            <div key={i} className="animate-pulse">
+              <div className="h-3 w-1/3 bg-gray-200 rounded mb-2" />
+              <div className="h-3 w-full bg-gray-100 rounded mb-1" />
+              <div className="h-3 w-4/5 bg-gray-100 rounded" />
+            </div>
+          ))}
+        </div>
+      </div>
+    );
+  }
+ 
+  Iif (error) {
+    return (
+      <div className={className}>
+        <p className="text-xs text-red-500">{error}</p>
+      </div>
+    );
+  }
+ 
+  Iif (contents.length === 0) {
+    return null;
+  }
+ 
+  return (
+    <div className={className}>
+      <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-3">
+        Course Material
+      </div>
+      <div className="space-y-5">
+        {contents.map((doc) => {
+          const isCited = activeCitationDocIds?.has(doc.documentId);
+          return (
+            <div
+              key={doc.documentId}
+              className={`rounded-md transition-colors ${isCited ? 'b-hard bg-blue-dark/5 dark:bg-blue-dark/20 p-2' : ''}`}
+            >
+              <div className="flex items-center gap-2 mb-2">
+                {isCited && (
+                  <span className="inline-block w-1.5 h-1.5 rounded-full bg-blue-dark dark:bg-white flex-shrink-0" />
+                )}
+                <p
+                  className="font-mono text-[10px] tracking-wide truncate opacity-80"
+                  title={doc.filename}
+                >
+                  {doc.filename}
+                </p>
+              </div>
+ 
+              {/* Section chips */}
+              {doc.sections.length > 0 && (
+                <div className="flex flex-wrap gap-1.5 mb-2">
+                  {doc.sections.map((section, i) => (
+                    <span key={i} className="chip" style={{ border: '1px solid currentColor' }}>
+                      {section.title ?? `Section ${i + 1}`}
+                    </span>
+                  ))}
+                </div>
+              )}
+ 
+              {/* Sample chunks */}
+              <div className="space-y-1.5">
+                {doc.chunks.map((chunk, i) => (
+                  <div key={i} className="b-thin rounded-md p-2.5 text-[12px] leading-relaxed">
+                    <div className="flex items-start gap-2">
+                      <span className="flex-shrink-0 font-mono text-[10px] opacity-50 mt-0.5">
+                        {i + 1}
+                      </span>
+                      <p className="flex-1 opacity-90">{chunk.text}</p>
+                    </div>
+                    {chunk.section && (
+                      <p className="mt-1 font-mono text-[10px] opacity-50 pl-5">
+                        § {chunk.section}
+                      </p>
+                    )}
+                  </div>
+                ))}
+              </div>
+            </div>
+          );
+        })}
+      </div>
+    </div>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html b/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html new file mode 100644 index 0000000..aee94fe --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html @@ -0,0 +1,394 @@ + + + + + + Code coverage report for src/components/study/LessonNav.tsx + + + + + + + + + +
+
+

All files / src/components/study LessonNav.tsx

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 93.33% + Branches + 14/15 +
+ + +
+ 100% + Functions + 4/4 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104  +  +  +  +  +  +  +  +  +  +  +  +  +9x +  +  +  +  +  +  +  +9x +  +  +  +3x +  +  +  +  +  +8x +  +  +  +  +  +  +  +  +  +  +  +21x +21x +21x +21x +  +21x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import type { Lesson } from '@/lib/services/courses';
+ 
+interface LessonNavProps {
+  lessons: Lesson[];
+  selectedLesson: Lesson | null;
+  completedLessons: Set<string>;
+  onSelect: (lesson: Lesson) => void;
+  loading?: boolean;
+  studiedLessonId?: string | null;
+}
+ 
+export function LessonNav({
+  lessons,
+  selectedLesson,
+  completedLessons,
+  onSelect,
+  loading = false,
+  studiedLessonId,
+}: LessonNavProps) {
+  if (loading) {
+    return (
+      <div className="space-y-1 p-3">
+        {[1, 2, 3].map((i) => (
+          <div key={i} className="h-9 bg-blue-dark/5 rounded animate-pulse" />
+        ))}
+      </div>
+    );
+  }
+ 
+  if (lessons.length === 0) {
+    return (
+      <div className="px-4 py-5 text-center font-mono text-[11px] opacity-50">
+        No lessons available yet.
+      </div>
+    );
+  }
+ 
+  return (
+    <nav aria-label="Course lessons">
+      <ul>
+        {lessons.map((lesson, index) => {
+          const lessonId = String(lesson.id);
+          const isSelected = selectedLesson?.id === lesson.id;
+          const isCompleted = completedLessons.has(lessonId);
+          const isStudied = studiedLessonId === lessonId && !isSelected;
+ 
+          return (
+            <li key={lesson.id}>
+              <button
+                onClick={() => onSelect(lesson)}
+                className={`w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors text-[13px] ${
+                  isSelected
+                    ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
+                    : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
+                }`}
+                aria-current={isSelected ? 'true' : undefined}
+              >
+                <span
+                  className={`flex-shrink-0 flex items-center justify-center h-5 w-5 rounded-sm font-mono text-[10px] font-semibold b-thin ${
+                    isCompleted ? 'opacity-60' : ''
+                  }`}
+                  aria-hidden="true"
+                >
+                  {isCompleted ? (
+                    <svg
+                      className="h-3 w-3"
+                      fill="none"
+                      viewBox="0 0 24 24"
+                      strokeWidth={2.5}
+                      stroke="currentColor"
+                    >
+                      <path
+                        strokeLinecap="round"
+                        strokeLinejoin="round"
+                        d="M4.5 12.75l6 6 9-13.5"
+                      />
+                    </svg>
+                  ) : (
+                    index + 1
+                  )}
+                </span>
+                <span className="flex-1 min-w-0 font-medium truncate">{lesson.title}</span>
+                {isStudied && (
+                  <span
+                    className="flex-shrink-0 inline-block w-1.5 h-1.5 rounded-full bg-blue-dark dark:bg-white opacity-60"
+                    title="Last studied"
+                  />
+                )}
+                {isCompleted && (
+                  <span className="flex-shrink-0 font-mono text-[9px] tracking-[0.18em] uppercase opacity-60">
+                    done
+                  </span>
+                )}
+              </button>
+            </li>
+          );
+        })}
+      </ul>
+    </nav>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html b/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html index 7434b2f..6a00bc7 100644 --- a/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html +++ b/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html @@ -23,30 +23,30 @@

All files / src/compo
- 0% + 24.48% Statements - 0/1 + 24/98
- 100% + 23.59% Branches - 0/0 + 21/89
- 0% + 8.33% Functions - 0/1 + 3/36
- 0% + 28.23% Lines - 0/1 + 24/85
@@ -101,7 +101,1011 @@

All files / src/compo 36 37 38 -39  +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575  +  +2x +2x +2x +2x +  +  +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +18x +18x +18x +18x +18x +18x +18x +18x +  +18x +18x +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +18x +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +        @@ -139,40 +1143,644 @@

All files / src/compo       +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +   
'use client';
  
-export function OutputPane() {
+import Link from 'next/link';
+import { useEffect, useRef, useState, type KeyboardEvent } from 'react';
+import { sendChatMessage, type Citation } from '@/lib/services/chat';
+import { sendStudyAction } from '@/lib/services/study';
+import type { ApiCitationOut, ApiStudyResponse, StudyAction } from '@/lib/api/types';
+import type { Lesson } from '@/lib/services/courses';
+import { StudyActionBar } from './StudyActionBar';
+import { StudyOutput } from './StudyOutput';
+ 
+// ── Types ─────────────────────────────────────────────────────────────────────
+ 
+interface ChatMessage {
+  role: 'user' | 'assistant';
+  content: string;
+  citations?: Citation[];
+}
+ 
+interface ActionMessage {
+  role: 'action-result';
+  action: StudyAction;
+  query: string;
+  result: ApiStudyResponse;
+  durationMs?: number;
+}
+ 
+type Message = ChatMessage | ActionMessage;
+ 
+interface OutputPaneProps {
+  courseId: string;
+  accessToken?: string;
+  selectedLesson?: Lesson | null;
+  hasIndexedDocs?: boolean;
+  initialQuery?: string;
+  initialAction?: StudyAction | null;
+  onActionResult?: (result: ApiStudyResponse, lesson: Lesson | null) => void;
+}
+ 
+// ── Evidence Drawer ───────────────────────────────────────────────────────────
+ 
+function ScoreBar({ score, rerank }: { score: number; rerank?: number }) {
+  const r = rerank ?? score;
+  return (
+    <div className="flex items-center gap-2">
+      <div className="flex-1 h-3 b-thin relative overflow-hidden">
+        <div
+          className="absolute inset-y-0 left-0 h-full opacity-30"
+          style={{ width: `${score * 100}%`, background: '#001CE0' }}
+        />
+        <div
+          className="absolute inset-y-0 left-0 h-full"
+          style={{ width: `${r * 100}%`, background: '#001CE0' }}
+        />
+      </div>
+      <span className="font-mono text-[10px] opacity-70 w-10 text-right tabular-nums">
+        {r.toFixed(3)}
+      </span>
+    </div>
+  );
+}
+ 
+function EvidenceDrawer({ citations }: { citations: ApiCitationOut[] }) {
+  // Group by doc_id for "By source" section
+  const byDoc: Record<string, { label: string; count: number }> = {};
+  citations.forEach((c) => {
+    const key = c.doc_id || 'unknown';
+    Iif (!byDoc[key]) byDoc[key] = { label: c.label || c.doc_id || 'Unknown', count: 0 };
+    byDoc[key].count++;
+  });
+  const total = citations.length || 1;
+  const sources = Object.entries(byDoc).map(([, v]) => ({
+    label: v.label,
+    pct: Math.round((v.count / total) * 100),
+  }));
+ 
+  return (
+    <div className="b-thin rounded-lg bg-white dark:bg-blue-dark/20 p-4">
+      <div className="grid grid-cols-12 gap-3">
+        {/* Passage cards */}
+        <div className="col-span-12 lg:col-span-7 space-y-2">
+          {citations.map((ev, i) => (
+            <div key={i} className="b-thin rounded-md p-3">
+              <div className="flex items-center gap-3 mb-1.5">
+                <span className="font-mono text-[10px] opacity-70 w-5">[{i + 1}]</span>
+                <span className="text-[12.5px] font-medium truncate flex-1">
+                  {ev.label || ev.doc_id || 'Source'}
+                </span>
+                {ev.section && (
+                  <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
+                    {ev.section}
+                  </span>
+                )}
+              </div>
+              <div className="font-mono text-[10px] opacity-60 mb-2">
+                {ev.page > 0 && `p.${ev.page}`}
+                {ev.slide > 0 && ` · slide ${ev.slide}`}
+                {` · score ${ev.score.toFixed(3)}`}
+              </div>
+              <p className="text-[12.5px] leading-snug opacity-90">{ev.snippet}</p>
+              <ScoreBar score={ev.score} />
+            </div>
+          ))}
+        </div>
+ 
+        {/* Charts */}
+        <div className="col-span-12 lg:col-span-5 space-y-3">
+          {/* Score bars legend */}
+          <div className="b-thin rounded-md p-3">
+            <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
+              Score
+            </div>
+            <div className="space-y-1.5">
+              {citations.map((ev, i) => (
+                <div key={i} className="flex items-center gap-2">
+                  <span className="font-mono text-[10px] opacity-70 w-5">[{i + 1}]</span>
+                  <ScoreBar score={ev.score} />
+                </div>
+              ))}
+            </div>
+            <div className="flex items-center gap-3 pt-2 b-thin-t mt-2 font-mono text-[10px] opacity-70">
+              <span>
+                <span
+                  className="inline-block w-2.5 h-2.5 align-middle mr-1"
+                  style={{ background: '#001CE0' }}
+                />
+                retrieval score
+              </span>
+            </div>
+          </div>
+ 
+          {/* By source */}
+          <div className="b-thin rounded-md p-3">
+            <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
+              By source
+            </div>
+            <ul className="space-y-1.5">
+              {sources.map((s, i) => (
+                <li key={i}>
+                  <div className="flex items-center justify-between font-mono text-[10px] mb-0.5">
+                    <span className="truncate opacity-90">{s.label}</span>
+                    <span className="opacity-70 tabular-nums ml-2">{s.pct}%</span>
+                  </div>
+                  <div className="h-1.5 b-thin overflow-hidden">
+                    <div className="h-full" style={{ width: `${s.pct}%`, background: '#001CE0' }} />
+                  </div>
+                </li>
+              ))}
+            </ul>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+ 
+// ── Inspect Drawer ────────────────────────────────────────────────────────────
+ 
+function InspectDrawer({ msg }: { msg: ActionMessage }) {
+  const { action, query, result, durationMs } = msg;
+  const lines = {
+    'retrieval.trace': [
+      `action: ${action}`,
+      `query_length: ${query.length} chars`,
+      `retrieval_used: ${result.retrieval_used}`,
+      `chunks_found: ${result.citations.length}`,
+      `generation: ${result.citations.length > 0 ? 'ran' : 'fallback'}`,
+      `output_length: ${result.answer.length} chars`,
+      durationMs != null ? `duration: ${durationMs}ms` : '(duration not tracked)',
+    ],
+    'evidence.json': [
+      '{',
+      `  "action": "${action}",`,
+      `  "k": ${result.citations.length},`,
+      `  "sources": [${[...new Set(result.citations.map((c) => c.doc_id || 'unknown'))].map((d) => `"${d}"`).join(', ')}],`,
+      `  "avg_score": ${result.citations.length ? (result.citations.reduce((s, c) => s + c.score, 0) / result.citations.length).toFixed(3) : 0}`,
+      '}',
+    ],
+    'output.meta': [
+      `model: qvac-rag`,
+      `answer_length: ${result.answer.length} chars`,
+      `citations: ${result.citations.length}`,
+      `retrieval_used: ${result.retrieval_used}`,
+    ],
+  };
+ 
   return (
-    <div className="h-full flex flex-col">
-      <div className="flex-shrink-0 px-6 py-4 border-b border-gray-200 bg-white">
-        <h2 className="text-sm font-semibold text-gray-900 uppercase tracking-wide">
-          Explanation
-        </h2>
+    <div className="b-thin rounded-lg bg-white dark:bg-blue-dark/20 p-4 mt-2">
+      <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-3">
+        Inspect · debug · MVP-only
       </div>
+      <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
+        {Object.entries(lines).map(([title, content]) => (
+          <div key={title} className="b-thin rounded-md overflow-hidden">
+            <div className="px-3 py-2 b-thin-b font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
+              {title}
+            </div>
+            <pre className="font-mono text-[11px] leading-relaxed p-3 whitespace-pre-wrap m-0">
+              {content.join('\n')}
+            </pre>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+ 
+// ── Suggested next actions ────────────────────────────────────────────────────
+ 
+const NEXT_ACTIONS: Array<{ action: StudyAction; glyph: string; label: string }> = [
+  { action: 'derive', glyph: '∂', label: 'Derive / prove' },
+  { action: 'quiz', glyph: '▢', label: 'Quiz me' },
+  { action: 'oral', glyph: '◉', label: 'Oral follow-ups' },
+];
+ 
+// ── Main component ────────────────────────────────────────────────────────────
+ 
+export function OutputPane({
+  courseId,
+  accessToken,
+  selectedLesson,
+  hasIndexedDocs = true,
+  initialQuery = '',
+  initialAction = null,
+  onActionResult,
+}: OutputPaneProps) {
+  const [messages, setMessages] = useState<Message[]>([]);
+  const [input, setInput] = useState(initialQuery);
+  const [loading, setLoading] = useState(false);
+  const [activeAction, setActiveAction] = useState<StudyAction | null>(null);
+  const [showEvidence, setShowEvidence] = useState(false);
+  const [showInspect, setShowInspect] = useState(false);
+  const bottomRef = useRef<HTMLDivElement>(null);
+  const didAutoFireRef = useRef(false);
+ 
+  useEffect(() => {
+    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
+  }, [messages, loading]);
+ 
+  // Last action result for drawers
+  const lastActionResult = [...messages]
+    .reverse()
+    .find((m): m is ActionMessage => m.role === 'action-result');
+ 
+  async function handleSend() {
+    const question = input.trim();
+    Iif (!question || loading) return;
+    setInput('');
+    setMessages((prev) => [...prev, { role: 'user', content: question }]);
+    setLoading(true);
+    setActiveAction(null);
+    try {
+      const result = await sendChatMessage(courseId, question, accessToken);
+      setMessages((prev) => [
+        ...prev,
+        { role: 'assistant', content: result.answer, citations: result.citations },
+      ]);
+    } catch (err) {
+      setMessages((prev) => [
+        ...prev,
+        {
+          role: 'assistant',
+          content: err instanceof Error ? `Error: ${err.message}` : 'Could not fetch a response.',
+        },
+      ]);
+    } finally {
+      setLoading(false);
+    }
+  }
+ 
+  async function handleAction(action: StudyAction, queryOverride?: string) {
+    const query = queryOverride || input.trim() || selectedLesson?.title || 'this course material';
+    setLoading(true);
+    setActiveAction(action);
+    setMessages((prev) => [...prev, { role: 'user', content: `[${action}] ${query}` }]);
+    const t0 = Date.now();
+    try {
+      const result = await sendStudyAction(courseId, action, query, accessToken);
+      const durationMs = Date.now() - t0;
+      setMessages((prev) => [
+        ...prev,
+        { role: 'action-result', action, query, result, durationMs },
+      ]);
+      Iif (result.citations.length > 0) setShowEvidence(true);
+      onActionResult?.(result, selectedLesson ?? null);
+    } catch (err) {
+      setMessages((prev) => [
+        ...prev,
+        {
+          role: 'assistant',
+          content: err instanceof Error ? `Error: ${err.message}` : 'Study action failed.',
+        },
+      ]);
+    } finally {
+      setLoading(false);
+      setActiveAction(null);
+    }
+  }
+ 
+  // Auto-fire when arriving from preview quick actions (?q=...&action=...)
+  useEffect(() => {
+    if (
+      didAutoFireRef.current ||
+      !initialQuery ||
+      !initialAction ||
+      !hasIndexedDocs ||
+      !accessToken
+    )
+      return;
+    didAutoFireRef.current = true;
+    handleAction(initialAction, initialQuery);
+    // handleAction is recreated each render — intentionally not listed to avoid loops.
+    // This effect re-evaluates only when auth/docs status changes.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [hasIndexedDocs, accessToken]);
+ 
+  function handleKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
+    Iif (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault();
+      handleSend();
+    }
+  }
+ 
+  const placeholder = selectedLesson
+    ? `Ask about "${selectedLesson.title}" or pick an action above…`
+    : 'Ask a question, or type a topic and pick a study action above…';
+ 
+  const evidenceCitations = lastActionResult?.result?.citations ?? [];
  
-      <div className="flex-1 flex items-center justify-center p-8 bg-white">
-        <div className="text-center max-w-sm">
-          <svg
-            className="mx-auto h-12 w-12 text-gray-300"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={1}
-            stroke="currentColor"
+  return (
+    <div className="h-full flex flex-col bg-white dark:bg-blue-dark/30">
+      {/* Header */}
+      <div className="flex-shrink-0 px-5 py-3 b-thin-b flex items-center gap-3">
+        <span className="mono text-[10px] tracking-[0.22em] uppercase opacity-70">AI Tutor</span>
+        {selectedLesson && (
+          <span className="font-medium text-sm truncate">{selectedLesson.title}</span>
+        )}
+        {lastActionResult && (
+          <div className="ml-auto flex items-center gap-1">
+            <button
+              onClick={() => {
+                setShowEvidence((v) => !v);
+                setShowInspect(false);
+              }}
+              className={`font-mono text-[10px] tracking-[0.18em] uppercase px-2.5 h-7 rounded-md transition-all ${
+                showEvidence
+                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
+                  : 'b-thin hover:bg-blue-dark/5'
+              }`}
+            >
+              {showEvidence ? '▾' : '▸'} Evidence · {evidenceCitations.length}
+            </button>
+            <button
+              onClick={() => {
+                setShowInspect((v) => !v);
+                setShowEvidence(false);
+              }}
+              className={`font-mono text-[10px] tracking-[0.18em] uppercase px-2.5 h-7 rounded-md transition-all ${
+                showInspect
+                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
+                  : 'b-thin hover:bg-blue-dark/5'
+              }`}
+            >
+              {showInspect ? '▾' : '▸'} Inspect
+            </button>
+          </div>
+        )}
+      </div>
+ 
+      {/* Action bar */}
+      <StudyActionBar
+        onAction={handleAction}
+        activeAction={activeAction}
+        loading={loading}
+        hasIndexedDocs={hasIndexedDocs}
+      />
+ 
+      {/* Message thread */}
+      <div className="flex-1 overflow-y-auto p-4 space-y-4 ws-scroll">
+        {/* Empty states */}
+        {messages.length === 0 && !hasIndexedDocs && (
+          <div className="flex items-center justify-center h-full">
+            <div className="text-center max-w-xs">
+              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
+              <p className="font-medium mb-1">No documents indexed yet</p>
+              <p className="font-mono text-[11px] opacity-60 leading-relaxed mb-4">
+                Upload course material in the Workspace to enable study actions.
+              </p>
+              <Link href={`/courses/${courseId}`} className="btn-ghost text-sm inline-flex">
+                Go to Workspace →
+              </Link>
+            </div>
+          </div>
+        )}
+        {messages.length === 0 && hasIndexedDocs && (
+          <div className="flex items-center justify-center h-full">
+            <div className="text-center max-w-xs">
+              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
+              <p className="font-mono text-[11px] opacity-60 leading-relaxed">
+                Type a topic in the input below, then click a study action — or just ask a question.
+              </p>
+            </div>
+          </div>
+        )}
+ 
+        {messages.map((msg, i) => {
+          const isLast = i === messages.length - 1;
+ 
+          Iif (msg.role === 'action-result') {
+            return (
+              <div key={i} className="space-y-2">
+                <div className="inline-flex items-center gap-1.5">
+                  <span className="chip" style={{ border: '1px solid currentColor' }}>
+                    {msg.action.replace('_', ' ')}
+                  </span>
+                  <span className="font-mono text-[11px] opacity-60 truncate max-w-48">
+                    {msg.query}
+                  </span>
+                </div>
+                <div className="b-thin rounded-lg p-4">
+                  <StudyOutput
+                    result={msg.result}
+                    courseId={courseId}
+                    onOralFollowUp={(query) => handleAction('oral', query)}
+                  />
+                </div>
+ 
+                {/* Suggested next actions — only on last result */}
+                {isLast && (
+                  <div className="pt-1">
+                    <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
+                      Suggested next actions
+                    </div>
+                    <div className="flex flex-wrap gap-2">
+                      {NEXT_ACTIONS.filter((n) => n.action !== msg.action).map((n) => (
+                        <button
+                          key={n.action}
+                          onClick={() => handleAction(n.action, msg.query)}
+                          disabled={loading}
+                          className="btn-ghost text-sm disabled:opacity-40"
+                        >
+                          {n.glyph} {n.label}
+                        </button>
+                      ))}
+                    </div>
+                  </div>
+                )}
+              </div>
+            );
+          }
+ 
+          return (
+            <div
+              key={i}
+              className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
+            >
+              <div
+                className={`max-w-[85%] rounded-lg px-4 py-3 ${
+                  msg.role === 'user'
+                    ? 'bg-blue-dark text-white'
+                    : 'b-thin bg-white dark:bg-blue-dark/40'
+                }`}
+              >
+                <p className="text-sm whitespace-pre-wrap leading-relaxed">{msg.content}</p>
+                {msg.role === 'assistant' && msg.citations && msg.citations.length > 0 && (
+                  <div className="mt-3 space-y-2 b-thin-t pt-2">
+                    <p className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70">
+                      Sources
+                    </p>
+                    {msg.citations.map((citation, ci) => (
+                      <div
+                        key={ci}
+                        className="b-thin rounded-md px-3 py-2 bg-white dark:bg-blue-dark/20"
+                      >
+                        <p className="text-xs line-clamp-3 leading-relaxed opacity-90">
+                          &ldquo;{citation.snippet}&rdquo;
+                        </p>
+                        <p className="mt-1 font-mono text-[10px] opacity-60">
+                          score · {Math.round(citation.score * 100)}%
+                        </p>
+                      </div>
+                    ))}
+                  </div>
+                )}
+              </div>
+            </div>
+          );
+        })}
+ 
+        {loading && (
+          <div className="flex justify-start w-full" aria-live="polite">
+            <div className="b-thin rounded-lg px-4 py-3 w-full max-w-[85%] space-y-2">
+              <p className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-60 mb-1">
+                Retrieving · generating…
+              </p>
+              <div
+                className="h-1.5 rounded bar-stripes"
+                style={{ background: '#001CE0', opacity: 0.6 }}
+              />
+              <div className="h-2 rounded bg-blue-dark/10 animate-pulse w-4/5 mt-1" />
+              <div className="h-2 rounded bg-blue-dark/10 animate-pulse w-3/5" />
+            </div>
+          </div>
+        )}
+ 
+        <div ref={bottomRef} />
+      </div>
+ 
+      {/* Evidence / Inspect drawers */}
+      {showEvidence && evidenceCitations.length > 0 && (
+        <div className="flex-shrink-0 b-thin-t p-4 max-h-80 overflow-y-auto ws-scroll">
+          <EvidenceDrawer citations={evidenceCitations} />
+        </div>
+      )}
+      {showInspect && lastActionResult && (
+        <div className="flex-shrink-0 b-thin-t p-4 max-h-64 overflow-y-auto ws-scroll">
+          <InspectDrawer msg={lastActionResult} />
+        </div>
+      )}
+ 
+      {/* Input area */}
+      <div className="flex-shrink-0 b-thin-t p-4">
+        {/* Scope chips */}
+        <div className="flex items-center gap-2 mb-2">
+          <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
+            ⌖ scope · all course docs
+          </span>
+          <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
+            k=5 · QVAC
+          </span>
+          {lastActionResult && (
+            <span className="ml-auto font-mono text-[10px] opacity-50">
+              {lastActionResult.result.citations.length} sources ·{' '}
+              {lastActionResult.durationMs != null ? `${lastActionResult.durationMs}ms` : 'done'}
+            </span>
+          )}
+        </div>
+        <div className="flex gap-3">
+          <textarea
+            value={input}
+            onChange={(e) => setInput(e.target.value)}
+            onKeyDown={handleKeyDown}
+            placeholder={placeholder}
+            rows={2}
+            disabled={loading}
+            className="flex-1 resize-none rounded-md b-thin px-3 py-2 text-sm placeholder-blue-dark/40 dark:placeholder-white/40 bg-transparent outline-none focus:ring-1 focus:ring-blue-dark dark:focus:ring-white disabled:opacity-50"
+          />
+          <button
+            onClick={handleSend}
+            disabled={!input.trim() || loading}
+            className="flex-shrink-0 inline-flex items-center justify-center h-10 w-10 self-end btn-primary rounded-lg disabled:opacity-40 disabled:cursor-not-allowed"
           >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z"
-            />
-          </svg>
-          <h3 className="mt-4 text-sm font-medium text-gray-900">
-            No explanation generated
-          </h3>
-          <p className="mt-1 text-sm text-gray-500">
-            Once a document is selected and processed, the reconstructed explanation
-            will appear here with source-grounded references.
-          </p>
+            <svg
+              className="h-5 w-5"
+              fill="none"
+              viewBox="0 0 24 24"
+              strokeWidth={2}
+              stroke="currentColor"
+            >
+              <path
+                strokeLinecap="round"
+                strokeLinejoin="round"
+                d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"
+              />
+            </svg>
+          </button>
         </div>
+        <p className="mt-1.5 font-mono text-[10px] opacity-50">
+          Enter to send · Shift+Enter for new line
+        </p>
       </div>
     </div>
   );
@@ -184,7 +1792,7 @@ 

All files / src/compo + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html b/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html new file mode 100644 index 0000000..cdef2da --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html @@ -0,0 +1,931 @@ + + + + + + Code coverage report for src/components/study/StudyOutput.tsx + + + + + + + + + +
+
+

All files / src/components/study StudyOutput.tsx

+
+ +
+ 5.66% + Statements + 3/53 +
+ + +
+ 0% + Branches + 0/83 +
+ + +
+ 0% + Functions + 0/31 +
+ + +
+ 7.14% + Lines + 3/42 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283  +  +2x +2x +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import { useMemo, useState } from 'react';
+import ReactMarkdown from 'react-markdown';
+import type { ApiStudyResponse } from '@/lib/api/types';
+import { CitationCard } from './CitationCard';
+ 
+interface StudyOutputProps {
+  result: ApiStudyResponse;
+  courseId: string;
+  onOralFollowUp?: (query: string) => void;
+}
+ 
+export function StudyOutput({ result, courseId, onOralFollowUp }: StudyOutputProps) {
+  const [showSources, setShowSources] = useState(result.action === 'retrieve');
+ 
+  const hasOutput = result.answer && result.answer.trim().length > 0;
+  const hasCitations = result.citations.length > 0;
+ 
+  return (
+    <div className="space-y-4">
+      {!hasOutput && !hasCitations && (
+        <p className="font-mono text-[11px] opacity-50 italic">
+          No results found in course materials.
+        </p>
+      )}
+ 
+      {!hasOutput && hasCitations && result.action !== 'retrieve' && (
+        <div
+          className="b-thin rounded-md px-4 py-3 text-sm"
+          style={{ borderColor: '#a55a00', color: '#a55a00' }}
+        >
+          LLM generation unavailable (OPENAI_API_KEY not configured). Showing source passages below.
+        </div>
+      )}
+ 
+      {hasOutput && result.action === 'quiz' ? (
+        <QuizOutput text={result.answer} />
+      ) : hasOutput && result.action === 'oral' ? (
+        <OralOutput text={result.answer} onSubmit={onOralFollowUp} />
+      ) : hasOutput && result.action === 'open_questions' ? (
+        <QuestionsOutput text={result.answer} />
+      ) : hasOutput ? (
+        <ReactMarkdown className="md-prose">{result.answer}</ReactMarkdown>
+      ) : null}
+ 
+      {/* Sources toggle (non-retrieve actions) */}
+      {hasCitations && result.action !== 'retrieve' && (
+        <div>
+          <button
+            onClick={() => setShowSources((v) => !v)}
+            className="flex items-center gap-1.5 font-mono text-[11px] tracking-[0.14em] uppercase opacity-70 hover:opacity-100 transition-opacity"
+          >
+            <svg
+              className={`h-3 w-3 transition-transform ${showSources ? 'rotate-90' : ''}`}
+              fill="none"
+              viewBox="0 0 24 24"
+              strokeWidth={2.5}
+              stroke="currentColor"
+            >
+              <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
+            </svg>
+            {showSources ? 'Hide' : 'Show'} {result.citations.length} source
+            {result.citations.length !== 1 ? 's' : ''}
+          </button>
+          {showSources && (
+            <div className="mt-2 space-y-2">
+              {result.citations.map((citation, i) => (
+                <CitationCard key={i} citation={citation} courseId={courseId} index={i + 1} />
+              ))}
+            </div>
+          )}
+        </div>
+      )}
+ 
+      {/* Retrieve: always show citations directly */}
+      {result.action === 'retrieve' && hasCitations && (
+        <div className="space-y-2">
+          {result.citations.map((citation, i) => (
+            <CitationCard key={i} citation={citation} courseId={courseId} index={i + 1} />
+          ))}
+        </div>
+      )}
+    </div>
+  );
+}
+ 
+// ── Quiz ──────────────────────────────────────────────────────────────────────
+ 
+interface ParsedQuestion {
+  question: string;
+  options: string[];
+  correctLetter: string;
+}
+ 
+function parseQuizQuestion(raw: string): ParsedQuestion {
+  const lines = raw
+    .split('\n')
+    .map((l) => l.trim())
+    .filter(Boolean);
+  const qLine = lines.find((l) => /^Q\d*[:.]/i.test(l));
+  const question = qLine ? qLine.replace(/^Q\d*[:.]\s*/i, '') : raw.trim();
+  const options = lines.filter((l) => /^[A-D][).]\s/.test(l));
+  const answerLine = lines.find((l) => /^Answer:/i.test(l));
+  const correctLetter = answerLine
+    ? answerLine
+        .replace(/^Answer:\s*/i, '')
+        .trim()
+        .charAt(0)
+        .toUpperCase()
+    : '';
+  return { question, options, correctLetter };
+}
+ 
+function QuizQuestion({ raw, index }: { raw: string; index: number }) {
+  const [selected, setSelected] = useState('');
+  const [revealed, setRevealed] = useState(false);
+  const { question, options, correctLetter } = useMemo(() => parseQuizQuestion(raw), [raw]);
+ 
+  return (
+    <div className="b-thin rounded-lg p-4 bg-white dark:bg-blue-dark/20">
+      <p className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-50 mb-2">
+        Q{index + 1}
+      </p>
+      <p className="text-[13.5px] font-medium leading-snug mb-3">{question}</p>
+ 
+      {options.length > 0 ? (
+        <div className="space-y-2">
+          {options.map((opt) => {
+            const letter = opt.charAt(0).toUpperCase();
+            const isCorrect = letter === correctLetter;
+            const isSelected = selected === letter;
+            return (
+              <button
+                key={letter}
+                onClick={() => !revealed && setSelected(letter)}
+                disabled={revealed}
+                className={`w-full b-thin rounded-md px-3 py-2 text-left text-[13px] transition-colors ${
+                  revealed && isCorrect
+                    ? 'bg-[rgba(26,127,58,0.08)] dark:bg-[rgba(26,127,58,0.15)]'
+                    : revealed && isSelected && !isCorrect
+                      ? 'bg-[rgba(179,38,30,0.08)] dark:bg-[rgba(179,38,30,0.15)]'
+                      : isSelected
+                        ? 'bg-blue-dark text-white'
+                        : 'hover:bg-blue-dark/5 dark:hover:bg-white/5'
+                }`}
+                style={
+                  revealed && isCorrect
+                    ? { borderColor: '#1a7f3a' }
+                    : revealed && isSelected && !isCorrect
+                      ? { borderColor: '#b3261e' }
+                      : {}
+                }
+              >
+                {opt}
+              </button>
+            );
+          })}
+ 
+          <div className="flex items-center gap-3 pt-1">
+            {!revealed && selected && (
+              <button onClick={() => setRevealed(true)} className="btn-ghost text-[11px]">
+                Check answer
+              </button>
+            )}
+            {revealed && (
+              <p
+                className="font-mono text-[11px]"
+                style={{ color: selected === correctLetter ? '#1a7f3a' : '#b3261e' }}
+              >
+                {selected === correctLetter ? '✓ Correct' : `✗ Correct: ${correctLetter}`}
+              </p>
+            )}
+          </div>
+        </div>
+      ) : (
+        /* Fallback: options not parseable — show plain reveal */
+        <div>
+          <button
+            onClick={() => setRevealed((v) => !v)}
+            className="font-mono text-[11px] tracking-[0.14em] uppercase opacity-70 hover:opacity-100"
+          >
+            {revealed ? 'Hide answer' : 'Reveal answer'}
+          </button>
+          {revealed && correctLetter && (
+            <div
+              className="mt-2 b-thin rounded-md px-3 py-2"
+              style={{ borderColor: '#1a7f3a', background: 'rgba(26,127,58,0.06)' }}
+            >
+              <p className="font-mono text-[12px]" style={{ color: '#1a7f3a' }}>
+                Answer: {correctLetter}
+              </p>
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+ 
+function QuizOutput({ text }: { text: string }) {
+  const questions = text.split(/\n(?=Q:)/g).filter((s) => s.trim());
+  Iif (questions.length === 0) {
+    return (
+      <pre className="whitespace-pre-wrap font-sans text-[13.5px] leading-relaxed">{text}</pre>
+    );
+  }
+  return (
+    <div className="space-y-4">
+      {questions.map((q, i) => (
+        <QuizQuestion key={i} raw={q} index={i} />
+      ))}
+    </div>
+  );
+}
+ 
+// ── Oral ──────────────────────────────────────────────────────────────────────
+ 
+function OralOutput({ text, onSubmit }: { text: string; onSubmit?: (query: string) => void }) {
+  const [answers, setAnswers] = useState<Record<number, string>>({});
+  const questions = text.split(/\n(?=Q\d+:)/g).filter((s) => s.trim());
+ 
+  function handleSubmit(idx: number, questionText: string) {
+    const answer = (answers[idx] ?? '').trim();
+    Iif (!answer || !onSubmit) return;
+    onSubmit(`${questionText.trim()}\n\nMy answer: ${answer}`);
+  }
+ 
+  function answerBox(idx: number, questionText: string) {
+    return (
+      <div className="mt-3 space-y-2">
+        <textarea
+          value={answers[idx] ?? ''}
+          onChange={(e) => setAnswers((prev) => ({ ...prev, [idx]: e.target.value }))}
+          placeholder="Type your answer here…"
+          rows={3}
+          className="w-full resize-none rounded-md b-thin px-3 py-2 text-sm bg-transparent outline-none focus:ring-1 focus:ring-blue-dark"
+        />
+        {(answers[idx] ?? '').trim() && onSubmit && (
+          <button onClick={() => handleSubmit(idx, questionText)} className="btn-ghost text-[11px]">
+            Submit answer →
+          </button>
+        )}
+      </div>
+    );
+  }
+ 
+  Iif (questions.length === 0) {
+    return (
+      <div>
+        <ReactMarkdown className="md-prose">{text}</ReactMarkdown>
+        {answerBox(0, text)}
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-4">
+      {questions.map((q, i) => (
+        <div key={i} className="b-thin rounded-lg p-4 bg-white dark:bg-blue-dark/20">
+          <ReactMarkdown className="md-prose">{q.trim()}</ReactMarkdown>
+          {answerBox(i, q)}
+        </div>
+      ))}
+    </div>
+  );
+}
+ 
+// ── Open questions ────────────────────────────────────────────────────────────
+ 
+function QuestionsOutput({ text }: { text: string }) {
+  const lines = text.split('\n').filter((l) => l.trim());
+  return (
+    <div className="space-y-2">
+      {lines.map((line, i) => (
+        <p key={i} className="text-[13.5px] leading-relaxed">
+          {line}
+        </p>
+      ))}
+    </div>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/index.html b/apps/web/coverage/lcov-report/src/components/study/index.html index 5a28792..fe7c163 100644 --- a/apps/web/coverage/lcov-report/src/components/study/index.html +++ b/apps/web/coverage/lcov-report/src/components/study/index.html @@ -23,30 +23,30 @@

All files src/components/study

- 0% + 19.37% Statements - 0/26 + 50/258
- 0% + 15.64% Branches - 0/7 + 41/262
- 0% + 9% Functions - 0/6 + 9/100
- 0% + 22.02% Lines - 0/25 + 50/227
@@ -79,48 +79,123 @@

All files src/components/study

- OutputPane.tsx - -
+ CitationCard.tsx + +
+ 6.66% + 1/15 0% - 0/1 - 100% - 0/0 - 0% - 0/1 + 0/22 0% - 0/1 + 0/2 + 8.33% + 1/12 - SourcePane.tsx - -
+ ContentChunks.tsx + +
+ 9.09% + 3/33 0% - 0/1 + 0/22 0% - 0/1 + 0/13 + 9.37% + 3/32 + + + + LessonNav.tsx + +
+ + 100% + 10/10 + 93.33% + 14/15 + 100% + 4/4 + 100% + 10/10 + + + + OutputPane.tsx + +
+ + 24.48% + 24/98 + 23.59% + 21/89 + 8.33% + 3/36 + 28.23% + 24/85 + + + + SourcePane.tsx + +
+ + 40% + 2/5 0% - 0/1 + 0/7 0% - 0/1 + 0/2 + 40% + 2/5 SplitPane.tsx - -
+ +
+ 2.7% + 1/37 0% - 0/24 + 0/13 0% - 0/6 + 0/9 + 2.94% + 1/34 + + + + StudyActionBar.tsx + +
+ + 85.71% + 6/7 + 54.54% + 6/11 + 66.66% + 2/3 + 85.71% + 6/7 + + + + StudyOutput.tsx + +
+ + 5.66% + 3/53 0% - 0/4 + 0/83 0% - 0/23 + 0/31 + 7.14% + 3/42 @@ -131,7 +206,7 @@

All files src/components/study

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html new file mode 100644 index 0000000..b1a7f16 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html @@ -0,0 +1,175 @@ + + + + + + Code coverage report for src/components/ui/BrandMark.tsx + + + + + + + + + +
+
+

All files / src/components/ui BrandMark.tsx

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Link from 'next/link';
+ 
+export function BrandMark() {
+  return (
+    <Link href="/courses" className="inline-flex items-center gap-3">
+      <svg viewBox="0 0 36 24" className="w-9 h-6 ink" fill="currentColor" aria-hidden>
+        <rect x="0" y="2" width="2" height="14" />
+        <rect x="2" y="0" width="2" height="2" />
+        <rect x="4" y="2" width="2" height="2" />
+        <rect x="6" y="4" width="2" height="14" />
+        <rect x="4" y="14" width="2" height="2" />
+        <rect x="2" y="16" width="2" height="2" />
+        <rect x="10" y="6" width="2" height="12" />
+        <rect x="12" y="4" width="2" height="2" />
+        <rect x="14" y="6" width="2" height="6" />
+        <rect x="12" y="12" width="2" height="2" />
+        <rect x="20" y="2" width="2" height="20" />
+        <rect x="22" y="0" width="2" height="2" />
+        <rect x="22" y="22" width="2" height="2" />
+        <rect x="28" y="6" width="2" height="12" />
+        <rect x="30" y="4" width="2" height="2" />
+        <rect x="32" y="6" width="2" height="12" />
+        <rect x="30" y="18" width="2" height="2" />
+      </svg>
+      <span className="font-mono text-[11px] tracking-[0.18em] uppercase ink font-semibold hidden sm:inline">
+        BitPolito · Academy
+      </span>
+    </Link>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html new file mode 100644 index 0000000..81aad64 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html @@ -0,0 +1,268 @@ + + + + + + Code coverage report for src/components/ui/ErrorBoundary.tsx + + + + + + + + + +
+
+

All files / src/components/ui ErrorBoundary.tsx

+
+ +
+ 9.09% + Statements + 1/11 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 10% + Lines + 1/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import { Component, type ErrorInfo, type ReactNode } from 'react';
+ 
+interface Props {
+  children: ReactNode;
+  fallback?: ReactNode;
+}
+ 
+interface State {
+  hasError: boolean;
+}
+ 
+export class ErrorBoundary extends Component<Props, State> {
+  state: State = { hasError: false };
+ 
+  static getDerivedStateFromError(): State {
+    return { hasError: true };
+  }
+ 
+  componentDidCatch(error: Error, info: ErrorInfo) {
+    console.error('[ErrorBoundary] Caught rendering error:', error, info.componentStack);
+  }
+ 
+  handleReset = () => {
+    this.setState({ hasError: false });
+  };
+ 
+  render() {
+    Iif (this.state.hasError) {
+      Iif (this.props.fallback) return this.props.fallback;
+ 
+      return (
+        <div className="flex flex-col items-center justify-center h-full p-6 text-center">
+          <svg
+            className="h-10 w-10 text-gray-300 mb-3"
+            fill="none"
+            viewBox="0 0 24 24"
+            strokeWidth={1.5}
+            stroke="currentColor"
+          >
+            <path
+              strokeLinecap="round"
+              strokeLinejoin="round"
+              d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
+            />
+          </svg>
+          <p className="text-sm text-gray-600 mb-3">Something went wrong in this section.</p>
+          <button
+            onClick={this.handleReset}
+            className="text-sm font-medium text-orange-600 hover:text-orange-700 underline"
+          >
+            Try again
+          </button>
+        </div>
+      );
+    }
+ 
+    return this.props.children;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html new file mode 100644 index 0000000..961fb7c --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html @@ -0,0 +1,220 @@ + + + + + + Code coverage report for src/components/ui/ProgressBar.tsx + + + + + + + + + +
+
+

All files / src/components/ui ProgressBar.tsx

+
+ +
+ 100% + Statements + 4/4 +
+ + +
+ 92.3% + Branches + 12/13 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 4/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46  +  +  +  +  +  +  +  +  +  +13x +  +  +  +  +  +  +13x +13x +13x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+interface ProgressBarProps {
+  percent: number;
+  label?: string;
+  showPercent?: boolean;
+  size?: 'sm' | 'md';
+  className?: string;
+}
+ 
+export function ProgressBar({
+  percent,
+  label,
+  showPercent = true,
+  size = 'sm',
+  className = '',
+}: ProgressBarProps) {
+  const clamped = Math.max(0, Math.min(100, percent));
+  const barHeight = size === 'sm' ? 'h-1.5' : 'h-2.5';
+  const barColor = clamped === 100 ? 'bg-green-500' : 'bg-orange-500';
+ 
+  return (
+    <div className={className}>
+      {(label || showPercent) && (
+        <div className="flex justify-between items-center mb-1">
+          {label && <span className="text-xs text-gray-500">{label}</span>}
+          {showPercent && <span className="text-xs font-medium text-gray-700">{clamped}%</span>}
+        </div>
+      )}
+      <div
+        className={`w-full bg-gray-200 rounded-full ${barHeight}`}
+        role="progressbar"
+        aria-valuenow={clamped}
+        aria-valuemin={0}
+        aria-valuemax={100}
+        aria-label={label ?? `Progress: ${clamped}%`}
+      >
+        <div
+          className={`${barColor} ${barHeight} rounded-full transition-all duration-500`}
+          style={{ width: `${clamped}%` }}
+        />
+      </div>
+    </div>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html new file mode 100644 index 0000000..7dd6b8e --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html @@ -0,0 +1,352 @@ + + + + + + Code coverage report for src/components/ui/Toast.tsx + + + + + + + + + +
+
+

All files / src/components/ui Toast.tsx

+
+ +
+ 0% + Statements + 0/23 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/13 +
+ + +
+ 0% + Lines + 0/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import { createContext, useCallback, useContext, useRef, useState } from 'react';
+ 
+// ── Types ─────────────────────────────────────────────────────────────────────
+ 
+export type ToastType = 'ok' | 'err' | 'warn';
+ 
+interface ToastMsg {
+  id: number;
+  message: string;
+  type: ToastType;
+}
+ 
+interface ToastContextValue {
+  showToast: (message: string, type?: ToastType) => void;
+}
+ 
+// ── Context ───────────────────────────────────────────────────────────────────
+ 
+const ToastContext = createContext<ToastContextValue>({ showToast: () => {} });
+ 
+export function useToast() {
+  return useContext(ToastContext);
+}
+ 
+// ── Style map ─────────────────────────────────────────────────────────────────
+ 
+const TYPE_CONFIG: Record<ToastType, { bg: string; icon: string }> = {
+  ok: { bg: '#1a7f3a', icon: '✓' },
+  err: { bg: '#b3261e', icon: '✕' },
+  warn: { bg: '#a55a00', icon: '!' },
+};
+ 
+// ── Provider ──────────────────────────────────────────────────────────────────
+ 
+export function ToastProvider({ children }: { children: React.ReactNode }) {
+  const [toasts, setToasts] = useState<ToastMsg[]>([]);
+  const counter = useRef(0);
+ 
+  const showToast = useCallback((message: string, type: ToastType = 'ok') => {
+    const id = ++counter.current;
+    setToasts((prev) => [...prev, { id, message, type }]);
+    setTimeout(() => {
+      setToasts((prev) => prev.filter((t) => t.id !== id));
+    }, 4000);
+  }, []);
+ 
+  const dismiss = useCallback((id: number) => {
+    setToasts((prev) => prev.filter((t) => t.id !== id));
+  }, []);
+ 
+  return (
+    <ToastContext.Provider value={{ showToast }}>
+      {children}
+      {toasts.length > 0 && (
+        <div
+          className="fixed bottom-5 right-5 z-50 flex flex-col gap-2 pointer-events-none"
+          aria-live="assertive"
+          aria-label="Notifications"
+        >
+          {toasts.map((t) => {
+            const { bg, icon } = TYPE_CONFIG[t.type];
+            return (
+              <div
+                key={t.id}
+                role="alert"
+                className="pointer-events-auto flex items-center gap-3 px-4 py-2.5 rounded-lg shadow-lg text-white font-mono text-[12px] max-w-xs"
+                style={{ background: bg }}
+              >
+                <span className="text-sm font-bold flex-shrink-0" aria-hidden="true">
+                  {icon}
+                </span>
+                <span className="flex-1 leading-snug break-words">{t.message}</span>
+                <button
+                  onClick={() => dismiss(t.id)}
+                  className="flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity ml-1"
+                  aria-label="Dismiss notification"
+                >
+                  ×
+                </button>
+              </div>
+            );
+          })}
+        </div>
+      )}
+    </ToastContext.Provider>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html new file mode 100644 index 0000000..bc72b38 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html @@ -0,0 +1,508 @@ + + + + + + Code coverage report for src/components/ui/TopBar.tsx + + + + + + + + + +
+
+

All files / src/components/ui TopBar.tsx

+
+ +
+ 0% + Statements + 0/31 +
+ + +
+ 0% + Branches + 0/30 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/31 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
'use client';
+ 
+import Link from 'next/link';
+import { useEffect, useState } from 'react';
+import { usePathname } from 'next/navigation';
+import { useSession } from 'next-auth/react';
+import { BrandMark } from './BrandMark';
+ 
+export function TopBar() {
+  const pathname = usePathname();
+  const { data: session } = useSession();
+  const [dark, setDark] = useState(false);
+ 
+  // Sync with system preference and persisted value on mount
+  useEffect(() => {
+    const saved = localStorage.getItem('theme');
+    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+    const isDark = saved === 'dark' || (!saved && prefersDark);
+    setDark(isDark);
+    document.documentElement.classList.toggle('dark', isDark);
+  }, []);
+ 
+  function toggleDark() {
+    const next = !dark;
+    setDark(next);
+    document.documentElement.classList.toggle('dark', next);
+    localStorage.setItem('theme', next ? 'dark' : 'light');
+  }
+ 
+  const courseMatch = pathname.match(/^\/courses\/([^/]+)/);
+  const courseId = courseMatch?.[1];
+ 
+  const isStudy = !!courseId && pathname.includes('/study');
+  const isPreview = !!courseId && pathname.includes('/preview');
+  const isWorkspace = !!courseId && !isStudy && !isPreview;
+  const isCourses = !courseId;
+ 
+  const tabs = [
+    { id: 'courses', label: 'Courses', href: '/courses', active: isCourses },
+    ...(courseId
+      ? [
+          {
+            id: 'workspace',
+            label: 'Workspace',
+            href: `/courses/${courseId}`,
+            active: isWorkspace,
+          },
+          { id: 'study', label: 'Study', href: `/courses/${courseId}/study`, active: isStudy },
+        ]
+      : []),
+  ];
+ 
+  const isDebug = !!courseId && pathname.includes('/debug');
+ 
+  const name = session?.user?.name || session?.user?.email || '';
+  const initials =
+    name
+      .split(/[\s@]/)
+      .filter(Boolean)
+      .map((p: string) => p[0])
+      .join('')
+      .slice(0, 2)
+      .toUpperCase() || 'U';
+ 
+  return (
+    <header className="sticky top-0 z-40 bg-surface dark:bg-blue-dark b-thin-b">
+      <div className="max-w-8xl mx-auto px-6 h-14 flex items-center gap-6">
+        <BrandMark />
+ 
+        <nav className="flex items-center gap-1 ml-2">
+          {tabs.map((tab) => (
+            <Link
+              key={tab.id}
+              href={tab.href}
+              className={`px-3 h-8 rounded-md font-mono text-[11px] tracking-[0.14em] uppercase whitespace-nowrap transition-all inline-flex items-center ${
+                tab.active
+                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
+                  : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
+              }`}
+            >
+              {tab.label}
+            </Link>
+          ))}
+        </nav>
+ 
+        {process.env.NODE_ENV === 'development' && courseId && (
+          <Link
+            href={`/courses/${courseId}/debug`}
+            className={`px-3 h-8 rounded-md font-mono text-[11px] tracking-[0.14em] uppercase whitespace-nowrap transition-all inline-flex items-center ${
+              isDebug
+                ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
+                : 'opacity-50 hover:opacity-100 hover:bg-blue-dark/5 dark:hover:bg-white/10'
+            }`}
+          >
+            Debug
+          </Link>
+        )}
+ 
+        <div className="ml-auto flex items-center gap-2">
+          {/* Dark mode toggle */}
+          <button
+            onClick={toggleDark}
+            className="h-8 w-8 b-thin rounded-md flex items-center justify-center hover:bg-blue-dark/5 dark:hover:bg-white/10 transition-colors"
+            title={dark ? 'Switch to light mode' : 'Switch to dark mode'}
+            aria-label="Toggle dark mode"
+          >
+            {dark ? (
+              /* Sun icon */
+              <svg viewBox="0 0 24 24" className="w-3.5 h-3.5" fill="currentColor">
+                <circle cx="12" cy="12" r="4" />
+                <g stroke="currentColor" strokeWidth="2" strokeLinecap="round">
+                  <line x1="12" y1="2" x2="12" y2="5" />
+                  <line x1="12" y1="19" x2="12" y2="22" />
+                  <line x1="2" y1="12" x2="5" y2="12" />
+                  <line x1="19" y1="12" x2="22" y2="12" />
+                  <line x1="4.5" y1="4.5" x2="6.5" y2="6.5" />
+                  <line x1="17.5" y1="17.5" x2="19.5" y2="19.5" />
+                  <line x1="4.5" y1="19.5" x2="6.5" y2="17.5" />
+                  <line x1="17.5" y1="6.5" x2="19.5" y2="4.5" />
+                </g>
+              </svg>
+            ) : (
+              /* Moon icon */
+              <svg viewBox="0 0 24 24" className="w-3.5 h-3.5" fill="currentColor">
+                <path d="M21 12.8A9 9 0 0 1 11.2 3a7 7 0 1 0 9.8 9.8z" />
+              </svg>
+            )}
+          </button>
+ 
+          {/* User avatar */}
+          <div
+            className="h-8 w-8 b-thin rounded-md flex items-center justify-center font-mono text-[11px] font-semibold"
+            title={name}
+          >
+            {initials}
+          </div>
+        </div>
+      </div>
+    </header>
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/index.html b/apps/web/coverage/lcov-report/src/components/ui/index.html new file mode 100644 index 0000000..223a4b2 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/components/ui/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for src/components/ui + + + + + + + + + +
+
+

All files src/components/ui

+
+ +
+ 6.75% + Statements + 5/74 +
+ + +
+ 22.64% + Branches + 12/53 +
+ + +
+ 4% + Functions + 1/25 +
+ + +
+ 7.35% + Lines + 5/68 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
BadgeDisplay.tsx +
+
0%0/30%0/60%0/10%0/3
BrandMark.tsx +
+
0%0/2100%0/00%0/10%0/2
ErrorBoundary.tsx +
+
9.09%1/110%0/20%0/410%1/10
ProgressBar.tsx +
+
100%4/492.3%12/13100%1/1100%4/4
Toast.tsx +
+
0%0/230%0/20%0/130%0/18
TopBar.tsx +
+
0%0/310%0/300%0/50%0/31
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/index.html b/apps/web/coverage/lcov-report/src/index.html index 05dd18f..3ebccd2 100644 --- a/apps/web/coverage/lcov-report/src/index.html +++ b/apps/web/coverage/lcov-report/src/index.html @@ -101,7 +101,7 @@

All files src

+ + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html b/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html index ffeba07..b3fb810 100644 --- a/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html +++ b/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html @@ -25,7 +25,7 @@

All files / src/lib/s
0% Statements - 0/9 + 0/13
@@ -39,14 +39,14 @@

All files / src/lib/s
0% Functions - 0/4 + 0/5
0% Lines - 0/9 + 0/12
@@ -109,13 +109,11 @@

All files / src/lib/s 44 45 46 -47 -48 -49  -  +47        +        @@ -129,38 +127,40 @@

All files / src/lib/s       -            +            -  -  -          +      -  +          +        -     
import { apiFetch } from '@/lib/api';
  
+// MVP limit: pagination not implemented. Acceptable for expected scale (5–10 courses).
+// When real pagination is needed, replace getCourses calls with a paginated version.
+export const MVP_COURSES_LIMIT = 100;
+ 
 export interface Course {
   id: number;
   title: string;
@@ -177,36 +177,30 @@ 

All files / src/lib/s lessons: Lesson[]; }   -export async function getCourses( - skip = 0, - limit = 100, - accessToken?: string -): Promise<Course[]> { +export async function getCourses(skip = 0, limit = 100, accessToken?: string): Promise<Course[]> { return apiFetch<Course[]>(`/courses?skip=${skip}&limit=${limit}`, { accessToken, }); }   -export async function getCourse( - courseId: string, - accessToken?: string -): Promise<Course> { +export async function getCourse(courseId: string, accessToken?: string): Promise<Course> { return apiFetch<Course>(`/courses/${courseId}`, { accessToken }); }   -export async function getCourseLessons( - courseId: string, - accessToken?: string -): Promise<Lesson[]> { +export async function getCourseLessons(courseId: string, accessToken?: string): Promise<Lesson[]> { return apiFetch<Lesson[]>(`/courses/${courseId}/lessons`, { accessToken }); }   -export async function getLesson( - lessonId: string, - accessToken?: string -): Promise<Lesson> { +export async function getLesson(lessonId: string, accessToken?: string): Promise<Lesson> { return apiFetch<Lesson>(`/lessons/${lessonId}`, { accessToken }); } +  +export async function createCourse(title: string, description?: string): Promise<Course> { + return apiFetch<Course>('/courses', { + method: 'POST', + body: { title, description }, + }); +}  

@@ -214,7 +208,7 @@

All files / src/lib/s + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html b/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html index ec5e8e3..62f11c4 100644 --- a/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html +++ b/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html @@ -340,7 +340,7 @@

All files / src/lib/s + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/study.ts.html b/apps/web/coverage/lcov-report/src/lib/services/study.ts.html new file mode 100644 index 0000000..521a449 --- /dev/null +++ b/apps/web/coverage/lcov-report/src/lib/services/study.ts.html @@ -0,0 +1,139 @@ + + + + + + Code coverage report for src/lib/services/study.ts + + + + + + + + + +
+
+

All files / src/lib/services study.ts

+
+ +
+ 25% + Statements + 1/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 25% + Lines + 1/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +192x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { apiFetch } from '@/lib/api';
+import type { ApiStudyRequest, ApiStudyResponse, StudyAction } from '@/lib/api/types';
+ 
+export type { StudyAction, ApiStudyResponse as StudyResponse };
+ 
+export async function sendStudyAction(
+  courseId: string,
+  action: StudyAction,
+  query: string,
+  accessToken?: string
+): Promise<ApiStudyResponse> {
+  const body: ApiStudyRequest = { action, query };
+  return apiFetch<ApiStudyResponse>(`/courses/${courseId}/study`, {
+    method: 'POST',
+    body,
+    accessToken,
+  });
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/middleware.ts.html b/apps/web/coverage/lcov-report/src/middleware.ts.html index 373b3ee..006350e 100644 --- a/apps/web/coverage/lcov-report/src/middleware.ts.html +++ b/apps/web/coverage/lcov-report/src/middleware.ts.html @@ -85,7 +85,7 @@

All files / src middlew - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/prettify.css b/apps/web/coverage/lcov-report/prettify.css deleted file mode 100644 index b317a7c..0000000 --- a/apps/web/coverage/lcov-report/prettify.css +++ /dev/null @@ -1 +0,0 @@ -.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/apps/web/coverage/lcov-report/prettify.js b/apps/web/coverage/lcov-report/prettify.js deleted file mode 100644 index b322523..0000000 --- a/apps/web/coverage/lcov-report/prettify.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable */ -window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/apps/web/coverage/lcov-report/sort-arrow-sprite.png b/apps/web/coverage/lcov-report/sort-arrow-sprite.png deleted file mode 100644 index 6ed68316eb3f65dec9063332d2f69bf3093bbfab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc diff --git a/apps/web/coverage/lcov-report/sorter.js b/apps/web/coverage/lcov-report/sorter.js deleted file mode 100644 index 4ed70ae..0000000 --- a/apps/web/coverage/lcov-report/sorter.js +++ /dev/null @@ -1,210 +0,0 @@ -/* eslint-disable */ -var addSorting = (function() { - 'use strict'; - var cols, - currentSort = { - index: 0, - desc: false - }; - - // returns the summary table element - function getTable() { - return document.querySelector('.coverage-summary'); - } - // returns the thead element of the summary table - function getTableHeader() { - return getTable().querySelector('thead tr'); - } - // returns the tbody element of the summary table - function getTableBody() { - return getTable().querySelector('tbody'); - } - // returns the th element for nth column - function getNthColumn(n) { - return getTableHeader().querySelectorAll('th')[n]; - } - - function onFilterInput() { - const searchValue = document.getElementById('fileSearch').value; - const rows = document.getElementsByTagName('tbody')[0].children; - - // Try to create a RegExp from the searchValue. If it fails (invalid regex), - // it will be treated as a plain text search - let searchRegex; - try { - searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive - } catch (error) { - searchRegex = null; - } - - for (let i = 0; i < rows.length; i++) { - const row = rows[i]; - let isMatch = false; - - if (searchRegex) { - // If a valid regex was created, use it for matching - isMatch = searchRegex.test(row.textContent); - } else { - // Otherwise, fall back to the original plain text search - isMatch = row.textContent - .toLowerCase() - .includes(searchValue.toLowerCase()); - } - - row.style.display = isMatch ? '' : 'none'; - } - } - - // loads the search box - function addSearchBox() { - var template = document.getElementById('filterTemplate'); - var templateClone = template.content.cloneNode(true); - templateClone.getElementById('fileSearch').oninput = onFilterInput; - template.parentElement.appendChild(templateClone); - } - - // loads all columns - function loadColumns() { - var colNodes = getTableHeader().querySelectorAll('th'), - colNode, - cols = [], - col, - i; - - for (i = 0; i < colNodes.length; i += 1) { - colNode = colNodes[i]; - col = { - key: colNode.getAttribute('data-col'), - sortable: !colNode.getAttribute('data-nosort'), - type: colNode.getAttribute('data-type') || 'string' - }; - cols.push(col); - if (col.sortable) { - col.defaultDescSort = col.type === 'number'; - colNode.innerHTML = - colNode.innerHTML + ''; - } - } - return cols; - } - // attaches a data attribute to every tr element with an object - // of data values keyed by column name - function loadRowData(tableRow) { - var tableCols = tableRow.querySelectorAll('td'), - colNode, - col, - data = {}, - i, - val; - for (i = 0; i < tableCols.length; i += 1) { - colNode = tableCols[i]; - col = cols[i]; - val = colNode.getAttribute('data-value'); - if (col.type === 'number') { - val = Number(val); - } - data[col.key] = val; - } - return data; - } - // loads all row data - function loadData() { - var rows = getTableBody().querySelectorAll('tr'), - i; - - for (i = 0; i < rows.length; i += 1) { - rows[i].data = loadRowData(rows[i]); - } - } - // sorts the table using the data for the ith column - function sortByIndex(index, desc) { - var key = cols[index].key, - sorter = function(a, b) { - a = a.data[key]; - b = b.data[key]; - return a < b ? -1 : a > b ? 1 : 0; - }, - finalSorter = sorter, - tableBody = document.querySelector('.coverage-summary tbody'), - rowNodes = tableBody.querySelectorAll('tr'), - rows = [], - i; - - if (desc) { - finalSorter = function(a, b) { - return -1 * sorter(a, b); - }; - } - - for (i = 0; i < rowNodes.length; i += 1) { - rows.push(rowNodes[i]); - tableBody.removeChild(rowNodes[i]); - } - - rows.sort(finalSorter); - - for (i = 0; i < rows.length; i += 1) { - tableBody.appendChild(rows[i]); - } - } - // removes sort indicators for current column being sorted - function removeSortIndicators() { - var col = getNthColumn(currentSort.index), - cls = col.className; - - cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); - col.className = cls; - } - // adds sort indicators for current column being sorted - function addSortIndicators() { - getNthColumn(currentSort.index).className += currentSort.desc - ? ' sorted-desc' - : ' sorted'; - } - // adds event listeners for all sorter widgets - function enableUI() { - var i, - el, - ithSorter = function ithSorter(i) { - var col = cols[i]; - - return function() { - var desc = col.defaultDescSort; - - if (currentSort.index === i) { - desc = !currentSort.desc; - } - sortByIndex(i, desc); - removeSortIndicators(); - currentSort.index = i; - currentSort.desc = desc; - addSortIndicators(); - }; - }; - for (i = 0; i < cols.length; i += 1) { - if (cols[i].sortable) { - // add the click event handler on the th so users - // dont have to click on those tiny arrows - el = getNthColumn(i).querySelector('.sorter').parentElement; - if (el.addEventListener) { - el.addEventListener('click', ithSorter(i)); - } else { - el.attachEvent('onclick', ithSorter(i)); - } - } - } - } - // adds sorting functionality to the UI - return function() { - if (!getTable()) { - return; - } - cols = loadColumns(); - loadData(); - addSearchBox(); - addSortIndicators(); - enableUI(); - }; -})(); - -window.addEventListener('load', addSorting); diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/index.html b/apps/web/coverage/lcov-report/src/app/(auth)/index.html deleted file mode 100644 index 4fd53f2..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/(auth) - - - - - - - - - -
-
-

All files src/app/(auth)

-
- -
- 0% - Statements - 0/2 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/2 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
layout.tsx -
-
0%0/2100%0/00%0/10%0/2
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/layout.tsx.html b/apps/web/coverage/lcov-report/src/app/(auth)/layout.tsx.html deleted file mode 100644 index 7a71cb8..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/layout.tsx.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - Code coverage report for src/app/(auth)/layout.tsx - - - - - - - - - -
-
-

All files / src/app/(auth) layout.tsx

-
- -
- 0% - Statements - 0/2 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/2 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { ReactNode } from 'react';
-import { BrandMark } from '@/components/ui/BrandMark';
- 
-interface AuthLayoutProps {
-  children: ReactNode;
-}
- 
-export default function AuthLayout({ children }: AuthLayoutProps) {
-  return (
-    <div className="min-h-screen bg-surface dark:bg-[#0a0a0a] dotgrid flex flex-col">
-      {/* Header — mirrors TopBar structure and styling */}
-      <header className="sticky top-0 z-10 bg-surface dark:bg-blue-dark b-thin-b">
-        <div className="max-w-8xl mx-auto px-6 h-14 flex items-center">
-          <BrandMark />
-        </div>
-      </header>
- 
-      {/* Form area */}
-      <main className="flex-1 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
-        <div className="max-w-md w-full space-y-6 page-fade">
-          <p className="text-center font-mono text-[11px] tracking-wide uppercase text-[#001CE0]/50 dark:text-white/40">
-            Learn Bitcoin with interactive courses
-          </p>
-          {children}
-        </div>
-      </main>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/login/index.html b/apps/web/coverage/lcov-report/src/app/(auth)/login/index.html deleted file mode 100644 index a840460..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/login/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/(auth)/login - - - - - - - - - -
-
-

All files src/app/(auth)/login

-
- -
- 97.87% - Statements - 46/47 -
- - -
- 96.96% - Branches - 32/33 -
- - -
- 100% - Functions - 6/6 -
- - -
- 97.82% - Lines - 45/46 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
page.tsx -
-
97.87%46/4796.96%32/33100%6/697.82%45/46
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/login/page.tsx.html b/apps/web/coverage/lcov-report/src/app/(auth)/login/page.tsx.html deleted file mode 100644 index bb0d306..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/login/page.tsx.html +++ /dev/null @@ -1,778 +0,0 @@ - - - - - - Code coverage report for src/app/(auth)/login/page.tsx - - - - - - - - - -
-
-

All files / src/app/(auth)/login page.tsx

-
- -
- 97.87% - Statements - 46/47 -
- - -
- 96.96% - Branches - 32/33 -
- - -
- 100% - Functions - 6/6 -
- - -
- 97.82% - Lines - 45/46 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232  -  -2x -2x -2x -2x -  -  -  -  -  -  -  -  -2x -  -2x -2x -  -  -2x -  -  -327x -327x -327x -327x -327x -  -327x -325x -325x -325x -  -  -325x -  -325x -15x -15x -3x -12x -2x -  -15x -6x -  -15x -15x -  -  -325x -15x -15x -15x -9x -9x -9x -  -  -  -  -  -8x -  -2x -  -  -2x -6x -6x -6x -  -  -  -  -8x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -179x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -106x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -2x -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { signIn } from 'next-auth/react';
-import Link from 'next/link';
-import { useRouter, useSearchParams } from 'next/navigation';
-import { FormEvent, Suspense, useState } from 'react';
- 
-interface FormErrors {
-  email?: string;
-  password?: string;
-  general?: string;
-}
- 
-const inputBase =
-  'appearance-none block w-full px-3 py-2 border rounded-md bg-white dark:bg-[#0a0a0a] text-[#001CE0] dark:text-white placeholder-[rgba(0,28,224,0.25)] dark:placeholder-white/25 focus:outline-none focus:ring-1 focus:ring-blue-dark focus:border-blue-dark sm:text-sm transition-colors';
- 
-const inputBorder = 'border-[rgba(0,28,224,0.18)] dark:border-[rgba(255,255,255,0.22)]';
-const inputBorderErr = 'border-err dark:border-red-400';
- 
-const labelClass =
-  'block font-mono text-[11px] tracking-wide uppercase text-[#001CE0]/70 dark:text-white/60';
- 
-function LoginForm() {
-  const router = useRouter();
-  const searchParams = useSearchParams();
-  const callbackUrl = searchParams.get('callbackUrl') || '/courses';
-  const errorParam = searchParams.get('error');
-  const messageParam = searchParams.get('message');
- 
-  const [email, setEmail] = useState('');
-  const [password, setPassword] = useState('');
-  const [isLoading, setIsLoading] = useState(false);
-  const [errors, setErrors] = useState<FormErrors>({});
- 
-  const sessionError =
-    errorParam === 'SessionExpired' ? 'Your session has expired. Please log in again.' : null;
- 
-  const validateForm = (): boolean => {
-    const newErrors: FormErrors = {};
-    if (!email) {
-      newErrors.email = 'Email is required';
-    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
-      newErrors.email = 'Please enter a valid email address';
-    }
-    if (!password) {
-      newErrors.password = 'Password is required';
-    }
-    setErrors(newErrors);
-    return Object.keys(newErrors).length === 0;
-  };
- 
-  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
-    e.preventDefault();
-    setErrors({});
-    if (!validateForm()) return;
-    setIsLoading(true);
-    try {
-      const result = await signIn('credentials', {
-        email,
-        password,
-        redirect: false,
-        callbackUrl,
-      });
-      if (result?.error) {
-        const friendlyError =
-          result.error === 'CredentialsSignin'
-            ? 'Invalid email or password. Please try again.'
-            : 'An unexpected error occurred. Please try again.';
-        setErrors({ general: friendlyError });
-      } else if (result?.ok) {
-        router.push(callbackUrl);
-        router.refresh();
-      }
-    } catch {
-      setErrors({ general: 'An unexpected error occurred. Please try again.' });
-    } finally {
-      setIsLoading(false);
-    }
-  };
- 
-  return (
-    <div className="mt-8 bg-white dark:bg-[#0f0f0f] py-8 px-4 b-thin sm:rounded-lg sm:px-10">
-      <h2 className="text-center text-xl font-bold ink dark:text-white mb-6 font-mono tracking-tight">
-        Sign in
-      </h2>
- 
-      {messageParam && (
-        <div className="mb-4 p-3 rounded bg-blue-50 dark:bg-[rgba(0,28,224,0.07)] border border-blue-200 dark:border-blue-600/30">
-          <p className="text-sm text-blue-800 dark:text-blue-300">{messageParam}</p>
-        </div>
-      )}
- 
-      {sessionError && (
-        <div className="mb-4 p-3 rounded bg-amber-50 dark:bg-[rgba(255,180,0,0.07)] border border-amber-200 dark:border-amber-600/30">
-          <p className="text-sm text-amber-800 dark:text-amber-400">{sessionError}</p>
-        </div>
-      )}
- 
-      {errors.general && (
-        <div className="mb-4 p-3 rounded bg-red-50 dark:bg-[rgba(255,0,0,0.06)] border border-red-200 dark:border-red-700/40">
-          <p className="text-sm text-red-600 dark:text-red-400">{errors.general}</p>
-        </div>
-      )}
- 
-      <form className="space-y-5" onSubmit={handleSubmit} noValidate>
-        <div>
-          <label htmlFor="email" className={labelClass}>
-            Email address
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="email"
-              name="email"
-              type="email"
-              autoComplete="email"
-              required
-              value={email}
-              onChange={(e) => setEmail(e.target.value)}
-              className={`${inputBase} ${errors.email ? inputBorderErr : inputBorder}`}
-              placeholder="you@example.com"
-              aria-invalid={errors.email ? 'true' : 'false'}
-              aria-describedby={errors.email ? 'email-error' : undefined}
-            />
-          </div>
-          {errors.email && (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="email-error"
-              role="alert"
-            >
-              {errors.email}
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <label htmlFor="password" className={labelClass}>
-            Password
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="password"
-              name="password"
-              type="password"
-              autoComplete="current-password"
-              required
-              value={password}
-              onChange={(e) => setPassword(e.target.value)}
-              className={`${inputBase} ${errors.password ? inputBorderErr : inputBorder}`}
-              placeholder="••••••••"
-              aria-invalid={errors.password ? 'true' : 'false'}
-              aria-describedby={errors.password ? 'password-error' : undefined}
-            />
-          </div>
-          {errors.password && (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="password-error"
-              role="alert"
-            >
-              {errors.password}
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <button type="submit" disabled={isLoading} className="btn-primary w-full justify-center">
-            {isLoading ? (
-              <>
-                <svg
-                  className="animate-spin h-4 w-4"
-                  xmlns="http://www.w3.org/2000/svg"
-                  fill="none"
-                  viewBox="0 0 24 24"
-                >
-                  <circle
-                    className="opacity-25"
-                    cx="12"
-                    cy="12"
-                    r="10"
-                    stroke="currentColor"
-                    strokeWidth="4"
-                  />
-                  <path
-                    className="opacity-75"
-                    fill="currentColor"
-                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
-                  />
-                </svg>
-                Signing in...
-              </>
-            ) : (
-              'Sign in'
-            )}
-          </button>
-        </div>
-      </form>
- 
-      <div className="mt-6">
-        <div className="relative">
-          <div className="absolute inset-0 flex items-center">
-            <div className="w-full border-t border-[rgba(0,28,224,0.12)] dark:border-[rgba(255,255,255,0.12)]" />
-          </div>
-          <div className="relative flex justify-center text-sm">
-            <span className="px-2 bg-white dark:bg-[#0f0f0f] font-mono text-[11px] tracking-wide text-[#001CE0]/40 dark:text-white/30">
-              Don&apos;t have an account?
-            </span>
-          </div>
-        </div>
- 
-        <div className="mt-4">
-          <Link href="/signup" className="btn-ghost w-full justify-center">
-            Create an account
-          </Link>
-        </div>
-      </div>
-    </div>
-  );
-}
- 
-export default function LoginPage() {
-  return (
-    <Suspense
-      fallback={
-        <div className="mt-8 bg-white dark:bg-[#0f0f0f] py-8 px-4 b-thin sm:rounded-lg sm:px-10 min-h-[320px]" />
-      }
-    >
-      <LoginForm />
-    </Suspense>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/signup/index.html b/apps/web/coverage/lcov-report/src/app/(auth)/signup/index.html deleted file mode 100644 index 806e004..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/signup/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/(auth)/signup - - - - - - - - - -
-
-

All files src/app/(auth)/signup

-
- -
- 68.49% - Statements - 50/73 -
- - -
- 66.66% - Branches - 48/72 -
- - -
- 87.5% - Functions - 7/8 -
- - -
- 68.05% - Lines - 49/72 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
page.tsx -
-
68.49%50/7366.66%48/7287.5%7/868.05%49/72
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/(auth)/signup/page.tsx.html b/apps/web/coverage/lcov-report/src/app/(auth)/signup/page.tsx.html deleted file mode 100644 index 0abe16b..0000000 --- a/apps/web/coverage/lcov-report/src/app/(auth)/signup/page.tsx.html +++ /dev/null @@ -1,1051 +0,0 @@ - - - - - - Code coverage report for src/app/(auth)/signup/page.tsx - - - - - - - - - -
-
-

All files / src/app/(auth)/signup page.tsx

-
- -
- 68.49% - Statements - 50/73 -
- - -
- 66.66% - Branches - 48/72 -
- - -
- 87.5% - Functions - 7/8 -
- - -
- 68.05% - Lines - 49/72 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323  -  -2x -2x -2x -2x -  -2x -  -  -  -  -  -  -  -  -  -  -2x -  -2x -2x -  -  -2x -  -2x -767x -  -767x -765x -765x -765x -765x -765x -  -765x -18x -  -18x -1x -17x -1x -  -  -18x -  -18x -1x -17x -1x -16x -1x -15x -1x -14x -14x -  -  -18x -1x -17x -1x -  -  -18x -  -  -  -18x -18x -  -  -765x -18x -18x -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -17x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -281x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -217x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -209x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import Link from 'next/link';
-import { useRouter } from 'next/navigation';
-import { signIn } from 'next-auth/react';
-import { FormEvent, useState } from 'react';
- 
-const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
- 
-interface FormErrors {
-  email?: string;
-  password?: string;
-  confirmPassword?: string;
-  displayName?: string;
-  general?: string;
-}
- 
-const inputBase =
-  'appearance-none block w-full px-3 py-2 border rounded-md bg-white dark:bg-[#0a0a0a] text-[#001CE0] dark:text-white placeholder-[rgba(0,28,224,0.25)] dark:placeholder-white/25 focus:outline-none focus:ring-1 focus:ring-blue-dark focus:border-blue-dark sm:text-sm transition-colors';
- 
-const inputBorder = 'border-[rgba(0,28,224,0.18)] dark:border-[rgba(255,255,255,0.22)]';
-const inputBorderErr = 'border-err dark:border-red-400';
- 
-const labelClass =
-  'block font-mono text-[11px] tracking-wide uppercase text-[#001CE0]/70 dark:text-white/60';
- 
-export default function SignupPage() {
-  const router = useRouter();
- 
-  const [email, setEmail] = useState('');
-  const [password, setPassword] = useState('');
-  const [confirmPassword, setConfirmPassword] = useState('');
-  const [displayName, setDisplayName] = useState('');
-  const [isLoading, setIsLoading] = useState(false);
-  const [errors, setErrors] = useState<FormErrors>({});
- 
-  const validateForm = (): boolean => {
-    const newErrors: FormErrors = {};
- 
-    if (!email) {
-      newErrors.email = 'Email is required';
-    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
-      newErrors.email = 'Please enter a valid email address';
-    }
- 
-    Iif (!password) {
-      newErrors.password = 'Password is required';
-    } else if (password.length < 12) {
-      newErrors.password = 'Password must be at least 12 characters long';
-    } else if (!/[A-Z]/.test(password)) {
-      newErrors.password = 'Password must contain at least one uppercase letter';
-    } else if (!/[a-z]/.test(password)) {
-      newErrors.password = 'Password must contain at least one lowercase letter';
-    } else if (!/\d/.test(password)) {
-      newErrors.password = 'Password must contain at least one digit';
-    } else if (!/[!@#$%^&*()\-_=+[\]{};':"\\|,.<>/?`~]/.test(password)) {
-      newErrors.password = 'Password must contain at least one special character';
-    }
- 
-    if (!confirmPassword) {
-      newErrors.confirmPassword = 'Please confirm your password';
-    } else if (password !== confirmPassword) {
-      newErrors.confirmPassword = 'Passwords do not match';
-    }
- 
-    Iif (displayName && displayName.length < 2) {
-      newErrors.displayName = 'Display name must be at least 2 characters long';
-    }
- 
-    setErrors(newErrors);
-    return Object.keys(newErrors).length === 0;
-  };
- 
-  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
-    e.preventDefault();
-    setErrors({});
-    if (!validateForm()) return;
-    setIsLoading(true);
-    try {
-      const registerResponse = await fetch(`${API_URL}/api/auth/register`, {
-        method: 'POST',
-        headers: { 'Content-Type': 'application/json' },
-        body: JSON.stringify({ email, password, display_name: displayName || null }),
-      });
- 
-      Iif (!registerResponse.ok) {
-        const error = await registerResponse.json();
- 
-        // FastAPI validation errors (422) return detail as an array of objects
-        const detail = Array.isArray(error.detail)
-          ? error.detail.map((e: { msg: string }) => e.msg).join('. ')
-          : (error.detail as string | undefined);
- 
-        if (registerResponse.status === 409) {
-          setErrors({ email: 'A user with this email already exists' });
-        } else if (registerResponse.status === 400 || registerResponse.status === 422) {
-          setErrors({ general: detail || 'Invalid input data' });
-        } else {
-          setErrors({ general: detail || 'Registration failed' });
-        }
-        return;
-      }
- 
-      const result = await signIn('credentials', {
-        email,
-        password,
-        redirect: false,
-        callbackUrl: '/courses',
-      });
- 
-      if (result?.error) {
-        router.push('/login?message=Registration successful. Please log in.');
-      } else Iif (result?.ok) {
-        router.push('/courses');
-        router.refresh();
-      }
-    } catch {
-      setErrors({ general: 'An unexpected error occurred. Please try again.' });
-    } finally {
-      setIsLoading(false);
-    }
-  };
- 
-  return (
-    <div className="mt-8 bg-white dark:bg-[#0f0f0f] py-8 px-4 b-thin sm:rounded-lg sm:px-10">
-      <h2 className="text-center text-xl font-bold ink dark:text-white mb-6 font-mono tracking-tight">
-        Create account
-      </h2>
- 
-      {errors.general && (
-        <div
-          className="mb-4 p-3 rounded bg-red-50 dark:bg-[rgba(255,0,0,0.06)] border border-red-200 dark:border-red-700/40"
-          role="alert"
-        >
-          <p className="text-sm text-red-600 dark:text-red-400">{errors.general}</p>
-        </div>
-      )}
- 
-      <form className="space-y-5" onSubmit={handleSubmit} noValidate>
-        <div>
-          <label htmlFor="displayName" className={labelClass}>
-            Display name{' '}
-            <span className="normal-case text-[#001CE0]/35 dark:text-white/30 tracking-normal">
-              (optional)
-            </span>
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="displayName"
-              name="displayName"
-              type="text"
-              autoComplete="name"
-              value={displayName}
-              onChange={(e) => setDisplayName(e.target.value)}
-              className={`${inputBase} ${errors.displayName ? inputBorderErr : inputBorder}`}
-              placeholder="Satoshi Nakamoto"
-              aria-invalid={errors.displayName ? 'true' : 'false'}
-              aria-describedby={errors.displayName ? 'displayName-error' : undefined}
-            />
-          </div>
-          {errors.displayName && (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="displayName-error"
-              role="alert"
-            >
-              {errors.displayName}
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <label htmlFor="email" className={labelClass}>
-            Email address
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="email"
-              name="email"
-              type="email"
-              autoComplete="email"
-              required
-              value={email}
-              onChange={(e) => setEmail(e.target.value)}
-              className={`${inputBase} ${errors.email ? inputBorderErr : inputBorder}`}
-              placeholder="you@example.com"
-              aria-invalid={errors.email ? 'true' : 'false'}
-              aria-describedby={errors.email ? 'email-error' : undefined}
-            />
-          </div>
-          {errors.email && (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="email-error"
-              role="alert"
-            >
-              {errors.email}
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <label htmlFor="password" className={labelClass}>
-            Password
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="password"
-              name="password"
-              type="password"
-              autoComplete="new-password"
-              required
-              value={password}
-              onChange={(e) => setPassword(e.target.value)}
-              className={`${inputBase} ${errors.password ? inputBorderErr : inputBorder}`}
-              placeholder="••••••••"
-              aria-invalid={errors.password ? 'true' : 'false'}
-              aria-describedby={errors.password ? 'password-error' : 'password-hint'}
-            />
-          </div>
-          {errors.password ? (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="password-error"
-              role="alert"
-            >
-              {errors.password}
-            </p>
-          ) : (
-            <p
-              className="mt-1 font-mono text-[11px] text-[#001CE0]/40 dark:text-white/30"
-              id="password-hint"
-            >
-              Min 12 chars · uppercase · lowercase · digit · special char
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <label htmlFor="confirmPassword" className={labelClass}>
-            Confirm password
-          </label>
-          <div className="mt-1.5">
-            <input
-              id="confirmPassword"
-              name="confirmPassword"
-              type="password"
-              autoComplete="new-password"
-              required
-              value={confirmPassword}
-              onChange={(e) => setConfirmPassword(e.target.value)}
-              className={`${inputBase} ${errors.confirmPassword ? inputBorderErr : inputBorder}`}
-              placeholder="••••••••"
-              aria-invalid={errors.confirmPassword ? 'true' : 'false'}
-              aria-describedby={errors.confirmPassword ? 'confirmPassword-error' : undefined}
-            />
-          </div>
-          {errors.confirmPassword && (
-            <p
-              className="mt-1 font-mono text-[11px] text-err dark:text-red-400"
-              id="confirmPassword-error"
-              role="alert"
-            >
-              {errors.confirmPassword}
-            </p>
-          )}
-        </div>
- 
-        <div>
-          <button type="submit" disabled={isLoading} className="btn-primary w-full justify-center">
-            {isLoading ? (
-              <>
-                <svg
-                  className="animate-spin h-4 w-4"
-                  xmlns="http://www.w3.org/2000/svg"
-                  fill="none"
-                  viewBox="0 0 24 24"
-                >
-                  <circle
-                    className="opacity-25"
-                    cx="12"
-                    cy="12"
-                    r="10"
-                    stroke="currentColor"
-                    strokeWidth="4"
-                  />
-                  <path
-                    className="opacity-75"
-                    fill="currentColor"
-                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
-                  />
-                </svg>
-                Creating account...
-              </>
-            ) : (
-              'Create account'
-            )}
-          </button>
-        </div>
-      </form>
- 
-      <div className="mt-6">
-        <div className="relative">
-          <div className="absolute inset-0 flex items-center">
-            <div className="w-full border-t border-[rgba(0,28,224,0.12)] dark:border-[rgba(255,255,255,0.12)]" />
-          </div>
-          <div className="relative flex justify-center text-sm">
-            <span className="px-2 bg-white dark:bg-[#0f0f0f] font-mono text-[11px] tracking-wide text-[#001CE0]/40 dark:text-white/30">
-              Already have an account?
-            </span>
-          </div>
-        </div>
- 
-        <div className="mt-4">
-          <Link href="/login" className="btn-ghost w-full justify-center">
-            Sign in instead
-          </Link>
-        </div>
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/index.html b/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/index.html deleted file mode 100644 index 572a702..0000000 --- a/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/api/auth/[...nextauth] - - - - - - - - - -
-
-

All files src/app/api/auth/[...nextauth]

-
- -
- 0% - Statements - 0/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
route.ts -
-
0%0/5100%0/0100%0/00%0/4
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/route.ts.html b/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/route.ts.html deleted file mode 100644 index 254daf9..0000000 --- a/apps/web/coverage/lcov-report/src/app/api/auth/[...nextauth]/route.ts.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - Code coverage report for src/app/api/auth/[...nextauth]/route.ts - - - - - - - - - -
-
-

All files / src/app/api/auth/[...nextauth] route.ts

-
- -
- 0% - Statements - 0/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10  -  -  -  -  -  -  -  -  - 
/**
- * NextAuth.js API route handler
- */
-import NextAuth from 'next-auth';
-import { authOptions } from '@/lib/auth/config';
- 
-const handler = NextAuth(authOptions);
- 
-export { handler as GET, handler as POST };
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/index.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/index.html deleted file mode 100644 index 52ae14c..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/debug - - - - - - - - - -
-
-

All files src/app/courses/[courseId]/debug

-
- -
- 0% - Statements - 0/46 -
- - -
- 0% - Branches - 0/24 -
- - -
- 0% - Functions - 0/11 -
- - -
- 0% - Lines - 0/44 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
page.tsx -
-
0%0/460%0/240%0/110%0/44
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html deleted file mode 100644 index bc86fe6..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/debug/page.tsx.html +++ /dev/null @@ -1,802 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/debug/page.tsx - - - - - - - - - -
-
-

All files / src/app/courses/[courseId]/debug page.tsx

-
- -
- 0% - Statements - 0/46 -
- - -
- 0% - Branches - 0/24 -
- - -
- 0% - Functions - 0/11 -
- - -
- 0% - Lines - 0/44 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useEffect, useState } from 'react';
-import { useParams, useRouter } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import {
-  getPipelineHealth,
-  testRetrieval,
-  getEvidencePack,
-  type PipelineHealth,
-} from '@/lib/services/debug';
-import type { EvidencePack } from '@/lib/api/types';
- 
-export default function DebugPage() {
-  const params = useParams();
-  const router = useRouter();
-  const courseId = params.courseId as string;
-  const { data: session } = useSession();
-  const accessToken = session?.user?.accessToken;
- 
-  const [health, setHealth] = useState<PipelineHealth | null>(null);
-  const [healthError, setHealthError] = useState<string | null>(null);
- 
-  const [query, setQuery] = useState('');
-  const [action, setAction] = useState('explain');
-  const [retrievalResult, setRetrievalResult] = useState<Record<string, unknown> | null>(null);
-  const [evidencePack, setEvidencePack] = useState<EvidencePack | null>(null);
-  const [querying, setQuerying] = useState(false);
- 
-  const loadHealth = useCallback(async () => {
-    try {
-      const h = await getPipelineHealth(accessToken);
-      setHealth(h);
-    } catch (err) {
-      setHealthError(err instanceof Error ? err.message : 'Failed to load health');
-    }
-  }, [accessToken]);
- 
-  useEffect(() => {
-    loadHealth();
-  }, [loadHealth]);
- 
-  async function handleTestRetrieval() {
-    Iif (!query.trim()) return;
-    setQuerying(true);
-    try {
-      const result = await testRetrieval(courseId, query, 5, accessToken);
-      setRetrievalResult(result as unknown as Record<string, unknown>);
-    } catch (err) {
-      setRetrievalResult({ error: err instanceof Error ? err.message : 'Failed' });
-    } finally {
-      setQuerying(false);
-    }
-  }
- 
-  async function handleGetEvidence() {
-    Iif (!query.trim()) return;
-    setQuerying(true);
-    try {
-      const pack = await getEvidencePack(courseId, query, action, accessToken);
-      setEvidencePack(pack);
-    } catch (err) {
-      setEvidencePack(null);
-    } finally {
-      setQuerying(false);
-    }
-  }
- 
-  return (
-    <main className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-8">
-      <div className="flex items-center gap-3">
-        <button
-          onClick={() => router.push(`/courses/${courseId}`)}
-          className="text-sm text-gray-500 hover:text-gray-700 flex items-center gap-1"
-        >
-          <svg
-            className="h-4 w-4"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
-            />
-          </svg>
-          Back
-        </button>
-        <h1 className="text-xl font-bold text-gray-900">Debug Inspector</h1>
-        <span className="px-2 py-0.5 text-xs bg-yellow-100 text-yellow-800 rounded font-medium">
-          DEV ONLY
-        </span>
-      </div>
- 
-      {/* Pipeline health */}
-      <section className="bg-white rounded-lg shadow">
-        <div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
-          <h2 className="text-base font-semibold text-gray-900">Pipeline Health</h2>
-          <button
-            onClick={loadHealth}
-            className="text-xs text-gray-500 hover:text-gray-700 underline"
-          >
-            Refresh
-          </button>
-        </div>
-        <div className="p-6">
-          {healthError && <p className="text-sm text-red-600">{healthError}</p>}
-          {health && (
-            <div className="space-y-3 text-sm">
-              <div className="flex items-center gap-2">
-                <span
-                  className={`h-2 w-2 rounded-full ${health.chroma_status === 'ok' ? 'bg-green-500' : 'bg-red-500'}`}
-                />
-                <span className="font-medium">ChromaDB:</span>
-                <span className="text-gray-600">
-                  {health.chroma_status} · {health.chroma_db_path}
-                </span>
-              </div>
-              <div>
-                <span className="font-medium">Collections:</span>
-                <div className="mt-1 flex flex-wrap gap-2">
-                  {Object.entries(health.collection_sizes).map(([name, count]) => (
-                    <span key={name} className="px-2 py-0.5 bg-gray-100 rounded text-xs">
-                      {name}: {count} chunks
-                    </span>
-                  ))}
-                  {Object.keys(health.collection_sizes).length === 0 && (
-                    <span className="text-gray-400 text-xs">No collections</span>
-                  )}
-                </div>
-              </div>
-              <div className="flex gap-4 text-xs text-gray-500">
-                <span>Uploads: {health.uploads_dir_size_mb} MB</span>
-                <span>Python: {health.python_version.split(' ')[0]}</span>
-              </div>
-            </div>
-          )}
-          {!health && !healthError && (
-            <p className="text-sm text-gray-400 animate-pulse">Loading…</p>
-          )}
-        </div>
-      </section>
- 
-      {/* Retrieval test */}
-      <section className="bg-white rounded-lg shadow">
-        <div className="px-6 py-4 border-b border-gray-100">
-          <h2 className="text-base font-semibold text-gray-900">Retrieval Test</h2>
-        </div>
-        <div className="p-6 space-y-4">
-          <div className="flex gap-3">
-            <input
-              type="text"
-              value={query}
-              onChange={(e) => setQuery(e.target.value)}
-              placeholder="Enter a query…"
-              className="flex-1 rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-orange-500 focus:outline-none focus:ring-1 focus:ring-orange-500"
-            />
-            <select
-              value={action}
-              onChange={(e) => setAction(e.target.value)}
-              className="rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-orange-500 focus:outline-none"
-            >
-              {['explain', 'summarize', 'retrieve', 'open_questions', 'quiz', 'oral'].map((a) => (
-                <option key={a} value={a}>
-                  {a}
-                </option>
-              ))}
-            </select>
-          </div>
-          <div className="flex gap-2">
-            <button
-              onClick={handleTestRetrieval}
-              disabled={querying || !query.trim()}
-              className="px-4 py-2 text-sm font-medium text-white bg-gray-700 rounded-md hover:bg-gray-800 disabled:opacity-40"
-            >
-              Raw Retrieval
-            </button>
-            <button
-              onClick={handleGetEvidence}
-              disabled={querying || !query.trim()}
-              className="px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700 disabled:opacity-40"
-            >
-              Evidence Pack
-            </button>
-          </div>
- 
-          {retrievalResult && (
-            <div>
-              <p className="text-xs font-medium text-gray-500 mb-2">
-                Raw candidates ({(retrievalResult as any).total ?? 0}):
-              </p>
-              <pre className="bg-gray-50 rounded-md p-4 text-xs text-gray-700 overflow-auto max-h-80">
-                {JSON.stringify(retrievalResult, null, 2)}
-              </pre>
-            </div>
-          )}
- 
-          {evidencePack && (
-            <div>
-              <p className="text-xs font-medium text-gray-500 mb-2">
-                Evidence pack · {evidencePack.chunks.length} chunks (from{' '}
-                {evidencePack.total_candidates} candidates):
-              </p>
-              <div className="space-y-2">
-                {evidencePack.chunks.map((chunk, i) => (
-                  <div
-                    key={chunk.chunk_id}
-                    className="rounded-md border border-gray-200 p-3 text-xs"
-                  >
-                    <div className="flex justify-between mb-1">
-                      <span className="font-medium text-gray-700">
-                        [{i + 1}] {chunk.anchor.doc_name}
-                      </span>
-                      <span className="text-orange-600 font-medium">
-                        {Math.round(chunk.score * 100)}%
-                      </span>
-                    </div>
-                    {chunk.anchor.section && (
-                      <p className="text-gray-500 mb-1">{chunk.anchor.section}</p>
-                    )}
-                    <p className="text-gray-700">
-                      {chunk.text.slice(0, 300)}
-                      {chunk.text.length > 300 ? '…' : ''}
-                    </p>
-                    <p className="mt-1 text-gray-400">
-                      {chunk.chunk_id} · {chunk.anchor.chunk_type}
-                    </p>
-                  </div>
-                ))}
-              </div>
-            </div>
-          )}
-        </div>
-      </section>
-    </main>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html deleted file mode 100644 index ece2f84..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/documents/[documentId]/preview - - - - - - - - - -
-
-

All files src/app/courses/[courseId]/documents/[documentId]/preview

-
- -
- 0% - Statements - 0/72 -
- - -
- 0% - Branches - 0/57 -
- - -
- 0% - Functions - 0/26 -
- - -
- 0% - Lines - 0/65 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
page.tsx -
-
0%0/720%0/570%0/260%0/65
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx.html deleted file mode 100644 index 245f082..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx.html +++ /dev/null @@ -1,1330 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx - - - - - - - - - -
-
-

All files / src/app/courses/[courseId]/documents/[documentId]/preview page.tsx

-
- -
- 0% - Statements - 0/72 -
- - -
- 0% - Branches - 0/57 -
- - -
- 0% - Functions - 0/26 -
- - -
- 0% - Lines - 0/65 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import Link from 'next/link';
-import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
-import { useParams, useRouter, useSearchParams } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import { getDocumentPreviewView } from '@/lib/api/documents';
-import type { DocumentPreviewView, ApiPreviewChunk } from '@/lib/api/types';
- 
-// ── Helpers ───────────────────────────────────────────────────────────────────
- 
-function uniqueSections(chunks: ApiPreviewChunk[]): { name: string; firstIndex: number }[] {
-  const seen = new Map<string, number>();
-  chunks.forEach((c, i) => {
-    const key = c.section ?? '';
-    Iif (!seen.has(key)) seen.set(key, i);
-  });
-  return Array.from(seen.entries()).map(([name, firstIndex]) => ({ name, firstIndex }));
-}
- 
-// ── Spinner ───────────────────────────────────────────────────────────────────
- 
-function Spinner() {
-  return (
-    <div className="h-[calc(100vh-48px)] flex items-center justify-center">
-      <div className="w-5 h-5 rounded-full border-2 border-blue-dark border-t-transparent animate-spin" />
-    </div>
-  );
-}
- 
-// ── Outline pane (3 col) ──────────────────────────────────────────────────────
- 
-function OutlinePane({
-  sections,
-  activeChunkIndex,
-  onSelect,
-}: {
-  sections: { name: string; firstIndex: number }[];
-  activeChunkIndex: number;
-  onSelect: (i: number) => void;
-}) {
-  const activeSection = [...sections].reverse().find((s) => s.firstIndex <= activeChunkIndex);
- 
-  return (
-    <div className="h-full flex flex-col b-thin-r">
-      <div className="flex-shrink-0 px-4 py-3 b-thin-b">
-        <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-          Outline
-        </span>
-      </div>
-      {sections.length === 0 ? (
-        <div className="flex-1 flex items-center justify-center">
-          <p className="font-mono text-[11px] opacity-50 text-center px-4">No sections found</p>
-        </div>
-      ) : (
-        <nav className="flex-1 overflow-y-auto ws-scroll p-2">
-          <ul className="space-y-0.5">
-            {sections.map((s, i) => {
-              const isActive = activeSection?.name === s.name;
-              return (
-                <li key={i}>
-                  <button
-                    onClick={() => onSelect(s.firstIndex)}
-                    className={`w-full text-left px-3 py-2 rounded text-[11px] font-mono transition-colors truncate ${
-                      isActive
-                        ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                        : 'hover:bg-blue-dark/5 dark:hover:bg-white/10 opacity-80'
-                    }`}
-                  >
-                    {s.name || `Section ${i + 1}`}
-                  </button>
-                </li>
-              );
-            })}
-          </ul>
-        </nav>
-      )}
-    </div>
-  );
-}
- 
-// ── Center viewer (6 col) ─────────────────────────────────────────────────────
- 
-function ViewerPane({
-  chunks,
-  activeIndex,
-  fallbackText,
-}: {
-  chunks: ApiPreviewChunk[];
-  activeIndex: number;
-  fallbackText: string | null;
-}) {
-  const containerRef = useRef<HTMLDivElement | null>(null);
-  const activeRef = useRef<HTMLDivElement | null>(null);
- 
-  useEffect(() => {
-    activeRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
-  }, [activeIndex]);
- 
-  Iif (chunks.length === 0) {
-    return (
-      <div className="h-full overflow-y-auto ws-scroll p-6">
-        {fallbackText ? (
-          <pre className="font-mono text-[12.5px] leading-relaxed whitespace-pre-wrap opacity-90">
-            {fallbackText}
-          </pre>
-        ) : (
-          <div className="flex items-center justify-center h-full">
-            <div className="text-center">
-              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
-              <p className="font-mono text-[11px] opacity-60">No content available yet.</p>
-            </div>
-          </div>
-        )}
-      </div>
-    );
-  }
- 
-  return (
-    <div ref={containerRef} className="h-full overflow-y-auto ws-scroll px-6 py-4 space-y-3">
-      {chunks.map((chunk, i) => {
-        const isActive = activeIndex === i;
-        return (
-          <div
-            key={i}
-            ref={
-              isActive
-                ? (el) => {
-                    activeRef.current = el;
-                  }
-                : undefined
-            }
-            className={`rounded-lg px-4 py-3 transition-all ${
-              isActive
-                ? 'bg-blue-dark/10 b-thin ring-1 ring-blue-dark/40 dark:ring-white/30'
-                : 'b-thin'
-            }`}
-          >
-            <div className="flex items-center gap-3 mb-2">
-              <span
-                className={`font-mono text-[10px] tracking-[0.18em] uppercase ${
-                  isActive ? 'text-blue-dark dark:text-white font-semibold' : 'opacity-50'
-                }`}
-              >
-                {chunk.label ?? `Chunk ${i + 1}`}
-              </span>
-              {chunk.section && (
-                <span className="font-mono text-[10px] opacity-40 truncate">· {chunk.section}</span>
-              )}
-            </div>
-            <p className="text-[13.5px] leading-relaxed whitespace-pre-wrap opacity-90">
-              {chunk.text}
-            </p>
-          </div>
-        );
-      })}
-    </div>
-  );
-}
- 
-// ── Chunk browser (3 col) ─────────────────────────────────────────────────────
- 
-function ChunkBrowser({
-  chunks,
-  activeIndex,
-  onSelect,
-  courseId,
-}: {
-  chunks: ApiPreviewChunk[];
-  activeIndex: number;
-  onSelect: (i: number) => void;
-  courseId: string;
-}) {
-  const activeChunk = chunks[activeIndex];
-  const context = activeChunk?.section || activeChunk?.label || `part ${activeIndex + 1}`;
-  const explainUrl = `/courses/${courseId}/study?q=${encodeURIComponent(`Explain: ${context}`)}&action=explain`;
-  const quizUrl = `/courses/${courseId}/study?q=${encodeURIComponent(`Quiz on: ${context}`)}&action=quiz`;
- 
-  return (
-    <div className="h-full flex flex-col b-thin-l">
-      <div className="flex-shrink-0 px-4 py-3 b-thin-b flex items-center justify-between">
-        <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">Chunks</span>
-        <span className="font-mono text-[10px] opacity-50">{chunks.length}</span>
-      </div>
-      {chunks.length === 0 ? (
-        <div className="flex-1 flex items-center justify-center">
-          <p className="font-mono text-[11px] opacity-50 text-center px-4">No chunks indexed</p>
-        </div>
-      ) : (
-        <div className="flex-1 overflow-y-auto ws-scroll p-2 space-y-1">
-          {chunks.map((chunk, i) => {
-            const isActive = activeIndex === i;
-            return (
-              <button
-                key={i}
-                onClick={() => onSelect(i)}
-                className={`w-full text-left rounded-md px-3 py-2 transition-colors ${
-                  isActive
-                    ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                    : 'b-thin hover:bg-blue-dark/5 dark:hover:bg-white/10'
-                }`}
-              >
-                <div
-                  className={`font-mono text-[10px] mb-0.5 truncate ${isActive ? 'opacity-80' : 'opacity-60'}`}
-                >
-                  {chunk.label ?? `Chunk ${i + 1}`}
-                </div>
-                <p className={`text-[11px] line-clamp-2 ${isActive ? 'opacity-90' : 'opacity-70'}`}>
-                  {chunk.text.slice(0, 90)}
-                </p>
-              </button>
-            );
-          })}
-        </div>
-      )}
- 
-      {/* Quick actions — navigate to study with this section pre-loaded */}
-      {chunks.length > 0 && (
-        <div className="flex-shrink-0 b-thin-t p-2 space-y-1">
-          <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-50 px-2 py-1">
-            Quick actions
-          </div>
-          <Link
-            href={explainUrl}
-            className="flex items-center gap-2 w-full text-left rounded-md px-3 py-2 font-mono text-[11px] b-thin hover:bg-blue-dark/5 dark:hover:bg-white/10 transition-colors"
-          >
-            <span className="opacity-60">∑</span>
-            <span className="flex-1 truncate opacity-80">{context}</span>
-            <span className="opacity-50 flex-shrink-0">explain →</span>
-          </Link>
-          <Link
-            href={quizUrl}
-            className="flex items-center gap-2 w-full text-left rounded-md px-3 py-2 font-mono text-[11px] b-thin hover:bg-blue-dark/5 dark:hover:bg-white/10 transition-colors"
-          >
-            <span className="opacity-60">▢</span>
-            <span className="flex-1 truncate opacity-80">{context}</span>
-            <span className="opacity-50 flex-shrink-0">quiz →</span>
-          </Link>
-        </div>
-      )}
-    </div>
-  );
-}
- 
-// ── Main content (needs useSearchParams → Suspense) ───────────────────────────
- 
-function PreviewContent() {
-  const params = useParams();
-  const router = useRouter();
-  const searchParams = useSearchParams();
-  const { data: session } = useSession();
- 
-  const courseId = params.courseId as string;
-  const documentId = params.documentId as string;
-  const accessToken = session?.user?.accessToken;
- 
-  const pageParam = Math.max(1, parseInt(searchParams.get('page') ?? '1', 10));
- 
-  const [preview, setPreview] = useState<DocumentPreviewView | null>(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
-  const [activeIndex, setActiveIndex] = useState(pageParam - 1);
- 
-  const load = useCallback(async () => {
-    Iif (!accessToken) return;
-    try {
-      setError(null);
-      setLoading(true);
-      const data = await getDocumentPreviewView(documentId, accessToken);
-      setPreview(data);
-    } catch (err) {
-      setError(err instanceof Error ? err.message : 'Failed to load preview');
-    } finally {
-      setLoading(false);
-    }
-  }, [documentId, accessToken]);
- 
-  useEffect(() => {
-    load();
-  }, [load]);
- 
-  // Sync ?page= to activeIndex once chunks are loaded
-  useEffect(() => {
-    Iif (preview?.sampleChunks) {
-      const clamped = Math.min(pageParam - 1, preview.sampleChunks.length - 1);
-      setActiveIndex(Math.max(0, clamped));
-    }
-  }, [preview, pageParam]);
- 
-  Iif (loading) return <Spinner />;
- 
-  Iif (error || !preview) {
-    return (
-      <div className="h-[calc(100vh-48px)] flex items-center justify-center p-8">
-        <div className="b-thin rounded-lg p-6 text-center max-w-sm w-full">
-          <p className="font-mono text-[11px] opacity-70 mb-4">
-            {error ?? 'Preview not available'}
-          </p>
-          <div className="flex items-center justify-center gap-4">
-            <button onClick={load} className="btn-ghost text-sm">
-              Retry
-            </button>
-            <button
-              onClick={() => router.push(`/courses/${courseId}`)}
-              className="font-mono text-[11px] opacity-60 hover:opacity-100 transition-opacity"
-            >
-              ← Back
-            </button>
-          </div>
-        </div>
-      </div>
-    );
-  }
- 
-  const chunks = preview.sampleChunks ?? [];
-  const sections = uniqueSections(chunks);
- 
-  return (
-    <div className="h-[calc(100vh-48px)] flex flex-col">
-      {/* Header */}
-      <div className="flex-shrink-0 b-thin-b px-5 py-3 flex items-center gap-4 bg-white dark:bg-blue-dark/30">
-        <button
-          onClick={() => router.push(`/courses/${courseId}`)}
-          className="flex items-center gap-1.5 font-mono text-[11px] opacity-60 hover:opacity-100 transition-opacity flex-shrink-0"
-        >
-          <svg
-            className="h-3.5 w-3.5"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2.5}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
-            />
-          </svg>
-          Back
-        </button>
- 
-        <div className="flex-1 min-w-0">
-          <span className="font-medium text-sm truncate">{preview.filename}</span>
-          {(preview.pageCount != null || chunks.length > 0) && (
-            <span className="ml-3 font-mono text-[10px] opacity-50">
-              {preview.pageCount != null ? `${preview.pageCount} pages` : ''}
-              {preview.pageCount != null && chunks.length > 0 ? ' · ' : ''}
-              {chunks.length > 0 ? `${chunks.length} chunks` : ''}
-            </span>
-          )}
-        </div>
- 
-        {chunks.length > 0 && (
-          <div className="flex items-center gap-1 font-mono text-[11px] opacity-60 flex-shrink-0">
-            <button
-              onClick={() => setActiveIndex((i) => Math.max(0, i - 1))}
-              disabled={activeIndex === 0}
-              className="px-2 py-1 rounded b-thin disabled:opacity-30 hover:bg-blue-dark/5 transition-colors"
-              aria-label="Previous chunk"
-            >
-              ‹
-            </button>
-            <span className="px-2 tabular-nums">
-              {activeIndex + 1} / {chunks.length}
-            </span>
-            <button
-              onClick={() => setActiveIndex((i) => Math.min(chunks.length - 1, i + 1))}
-              disabled={activeIndex === chunks.length - 1}
-              className="px-2 py-1 rounded b-thin disabled:opacity-30 hover:bg-blue-dark/5 transition-colors"
-              aria-label="Next chunk"
-            >
-              ›
-            </button>
-          </div>
-        )}
-      </div>
- 
-      {/* 3-pane body */}
-      <div className="flex-1 min-h-0 grid grid-cols-12 bg-white dark:bg-blue-dark/20">
-        <div className="col-span-3">
-          <OutlinePane
-            sections={sections}
-            activeChunkIndex={activeIndex}
-            onSelect={setActiveIndex}
-          />
-        </div>
-        <div className="col-span-6">
-          <ViewerPane
-            chunks={chunks}
-            activeIndex={activeIndex}
-            fallbackText={preview.extractedTextPreview}
-          />
-        </div>
-        <div className="col-span-3">
-          <ChunkBrowser
-            chunks={chunks}
-            activeIndex={activeIndex}
-            onSelect={setActiveIndex}
-            courseId={courseId}
-          />
-        </div>
-      </div>
-    </div>
-  );
-}
- 
-// ── Page export ───────────────────────────────────────────────────────────────
- 
-export default function DocumentPreviewPage() {
-  return (
-    <Suspense fallback={<Spinner />}>
-      <PreviewContent />
-    </Suspense>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/index.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/index.html deleted file mode 100644 index 37e22e3..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId] - - - - - - - - - -
-
-

All files src/app/courses/[courseId]

-
- -
- 0% - Statements - 0/95 -
- - -
- 0% - Branches - 0/68 -
- - -
- 0% - Functions - 0/31 -
- - -
- 0% - Lines - 0/81 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
layout.tsx -
-
0%0/1100%0/00%0/10%0/1
page.tsx -
-
0%0/940%0/680%0/300%0/80
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/layout.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/layout.tsx.html deleted file mode 100644 index b293414..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/layout.tsx.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/layout.tsx - - - - - - - - - -
-
-

All files / src/app/courses/[courseId] layout.tsx

-
- -
- 0% - Statements - 0/1 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4  -  -  - 
export default function CourseLayout({ children }: { children: React.ReactNode }) {
-  return <>{children}</>;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/page.tsx.html deleted file mode 100644 index aacbf64..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/page.tsx.html +++ /dev/null @@ -1,1420 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/page.tsx - - - - - - - - - -
-
-

All files / src/app/courses/[courseId] page.tsx

-
- -
- 0% - Statements - 0/94 -
- - -
- 0% - Branches - 0/68 -
- - -
- 0% - Functions - 0/30 -
- - -
- 0% - Lines - 0/80 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect, useState, useCallback } from 'react';
-import { useParams, useRouter } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import { getCourse, type Course } from '@/lib/services/courses';
-import { DocumentUpload } from '@/components/documents/DocumentUpload';
-import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
-import { getDocumentListRows } from '@/lib/api/documents';
-import type { DocumentListRow } from '@/lib/api/types';
-import { DocumentProcessingPanel } from '@/components/documents/DocumentProcessingPanel';
- 
-type DocFilter = 'all' | 'ready' | 'processing' | 'error';
- 
-const FILTER_OPTIONS: { id: DocFilter; label: string }[] = [
-  { id: 'all', label: 'All' },
-  { id: 'ready', label: 'Indexed' },
-  { id: 'processing', label: 'Processing' },
-  { id: 'error', label: 'Failed' },
-];
- 
-function formatSize(bytes: number) {
-  Iif (bytes < 1024) return `${bytes} B`;
-  Iif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
-  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
-}
- 
-const STATE_DOT: Record<string, string> = {
-  ready: '#1a7f3a',
-  processing: '#a55a00',
-  uploading: '#a55a00',
-  error: '#b3261e',
-};
- 
-const LIFECYCLE_STAGES = ['uploading', 'processing', 'ready'] as const;
- 
-function Lifecycle({ status }: { status: string }) {
-  const failed = status === 'error';
-  const idx = failed ? -1 : (LIFECYCLE_STAGES as readonly string[]).indexOf(status);
-  return (
-    <div className="flex items-center gap-1.5">
-      {LIFECYCLE_STAGES.map((s, i) => {
-        const done = !failed && i < idx;
-        const here = !failed && i === idx;
-        return (
-          <div key={s} className="flex items-center gap-1.5 flex-1">
-            <div
-              className={`flex-1 h-7 b-thin rounded-sm flex items-center justify-center font-mono text-[10px] tracking-[0.16em] uppercase ${
-                done
-                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                  : here
-                    ? 'bg-blue-dark/10'
-                    : 'opacity-40'
-              }`}
-            >
-              {s}
-            </div>
-            {i < LIFECYCLE_STAGES.length - 1 && (
-              <span className="opacity-40 mono text-[10px]">›</span>
-            )}
-          </div>
-        );
-      })}
-      {failed && (
-        <span className="ml-2 chip" style={{ color: '#b3261e', border: '1px solid #b3261e' }}>
-          FAILED
-        </span>
-      )}
-    </div>
-  );
-}
- 
-export default function CourseWorkspacePage() {
-  const params = useParams();
-  const router = useRouter();
-  const courseId = params.courseId as string;
-  const { data: session } = useSession();
-  const accessToken = session?.user?.accessToken;
- 
-  const [course, setCourse] = useState<Course | null>(null);
-  const [docs, setDocs] = useState<DocumentListRow[]>([]);
-  const [loading, setLoading] = useState(true);
-  const [docsLoading, setDocsLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
-  const [selectedId, setSelectedId] = useState<string | null>(null);
-  const [filter, setFilter] = useState<DocFilter>('all');
-  const [refreshKey, setRefreshKey] = useState(0);
- 
-  const refreshDocuments = useCallback(() => setRefreshKey((k) => k + 1), []);
- 
-  useEffect(() => {
-    async function load() {
-      try {
-        const courseData = await getCourse(courseId, accessToken);
-        setCourse(courseData);
-      } catch (err) {
-        setError(err instanceof Error ? err.message : 'Failed to load course');
-      } finally {
-        setLoading(false);
-      }
-    }
-    Iif (courseId) load();
-  }, [courseId, accessToken]);
- 
-  useEffect(() => {
-    async function loadDocs() {
-      try {
-        setDocsLoading(true);
-        const rows = await getDocumentListRows(courseId, accessToken);
-        setDocs(rows);
-        Iif (rows.length > 0 && !selectedId) setSelectedId(rows[0].id);
-      } catch {
-        setDocs([]);
-      } finally {
-        setDocsLoading(false);
-      }
-    }
-    Iif (courseId) loadDocs();
-  }, [courseId, accessToken, refreshKey]);
- 
-  Iif (loading) {
-    return (
-      <main className="page-fade max-w-8xl mx-auto px-6 py-6">
-        <div className="animate-pulse space-y-5">
-          <div className="b-hard rounded-lg h-24 bg-blue-dark/5" />
-          <div className="grid grid-cols-12 gap-5">
-            <div className="col-span-7 space-y-3">
-              <div className="h-32 b-hard rounded-lg bg-blue-dark/5" />
-              <div className="h-64 b-hard rounded-lg bg-blue-dark/5" />
-            </div>
-            <div className="col-span-5 h-96 b-hard rounded-lg bg-blue-dark/5" />
-          </div>
-        </div>
-      </main>
-    );
-  }
- 
-  Iif (error || !course) {
-    return (
-      <main className="max-w-8xl mx-auto px-6 py-6">
-        <div
-          className="b-hard rounded-lg p-6 text-center"
-          style={{ borderColor: '#b3261e', color: '#b3261e' }}
-        >
-          <p className="text-sm">{error || 'Course not found'}</p>
-          <button
-            onClick={() => window.location.reload()}
-            className="mt-3 text-sm font-medium underline"
-          >
-            Retry
-          </button>
-        </div>
-      </main>
-    );
-  }
- 
-  const indexed = docs.filter((d) => d.status === 'ready').length;
-  const processing = docs.filter(
-    (d) => d.status === 'processing' || d.status === 'uploading'
-  ).length;
-  const failed = docs.filter((d) => d.status === 'error').length;
- 
-  const filtered = docs.filter((d) => {
-    Iif (filter === 'all') return true;
-    Iif (filter === 'ready') return d.status === 'ready';
-    Iif (filter === 'processing') return d.status === 'processing' || d.status === 'uploading';
-    Iif (filter === 'error') return d.status === 'error';
-    return true;
-  });
- 
-  const selected = docs.find((d) => d.id === selectedId) ?? null;
- 
-  return (
-    <main className="page-fade max-w-8xl mx-auto px-6 py-6">
-      {/* Course header */}
-      <div className="b-hard rounded-lg bg-white dark:bg-blue-dark/30 px-6 py-5 mb-5 flex items-start gap-6">
-        <div className="flex-1 min-w-0">
-          <div className="flex items-center gap-2 font-mono text-[11px] tracking-[0.12em] uppercase opacity-70 mb-3">
-            <span>Academy</span>
-            <span className="opacity-40">/</span>
-            <span>Courses</span>
-            <span className="opacity-40">/</span>
-            <span className="font-semibold opacity-100 truncate">{course.title}</span>
-          </div>
-          <h1 className="text-3xl font-medium leading-tight">{course.title}</h1>
-          {course.description && (
-            <p className="font-mono text-[11px] opacity-70 mt-1">{course.description}</p>
-          )}
-        </div>
- 
-        <div className="flex items-center gap-6 b-thin-l pl-6">
-          <Stat2 n={docs.length} k="documents" />
-          <Stat2 n={indexed} k="indexed" />
-          <Stat2 n={processing} k="processing" warn={processing > 0} />
-          <Stat2 n={failed} k="failed" err={failed > 0} />
-        </div>
- 
-        <div className="flex items-center gap-2 flex-shrink-0">
-          <button
-            className="btn-ghost"
-            onClick={() =>
-              selected && router.push(`/courses/${courseId}/documents/${selected.id}/preview`)
-            }
-            disabled={!selected}
-          >
-            Open viewer
-          </button>
-          <button className="btn-primary" onClick={() => router.push(`/courses/${courseId}/study`)}>
-            Study →
-          </button>
-        </div>
-      </div>
- 
-      {/* Two columns */}
-      <div className="grid grid-cols-12 gap-5">
-        {/* LEFT — upload + document list */}
-        <div className="col-span-12 lg:col-span-7 space-y-5">
-          {/* Upload zone */}
-          <div className="b-hard rounded-lg p-5 bg-white dark:bg-blue-dark/30">
-            <div className="flex items-end justify-between b-thin-b pb-1.5 mb-3">
-              <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-                Upload · drop or click
-              </span>
-              <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-60">
-                PDF · PPTX · MD · TXT · ≤ 50 MB
-              </span>
-            </div>
-            <ErrorBoundary>
-              <DocumentUpload
-                courseId={courseId}
-                accessToken={accessToken}
-                onUploadComplete={refreshDocuments}
-              />
-            </ErrorBoundary>
-          </div>
- 
-          {/* Filter bar */}
-          <div className="flex items-center gap-2">
-            {FILTER_OPTIONS.map((f) => (
-              <button
-                key={f.id}
-                onClick={() => setFilter(f.id)}
-                className={`font-mono text-[11px] tracking-[0.18em] uppercase px-3 h-8 rounded-md transition-all ${
-                  filter === f.id
-                    ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                    : 'b-thin hover:bg-blue-dark/5'
-                }`}
-              >
-                {f.label}
-              </button>
-            ))}
-            <span className="ml-auto font-mono text-[11px] opacity-60">
-              {filtered.length} shown
-            </span>
-          </div>
- 
-          {/* Document list */}
-          <div className="b-hard rounded-lg bg-white dark:bg-blue-dark/30 overflow-hidden">
-            <div className="grid grid-cols-[1fr_100px_120px_30px] gap-3 px-4 py-2.5 b-thin-b font-mono text-[10px] tracking-[0.18em] uppercase opacity-70">
-              <div>Document</div>
-              <div>Size</div>
-              <div>Status</div>
-              <div />
-            </div>
- 
-            {docsLoading ? (
-              <div className="p-6 space-y-3">
-                {[1, 2, 3].map((i) => (
-                  <div key={i} className="animate-pulse h-12 bg-blue-dark/5 rounded" />
-                ))}
-              </div>
-            ) : filtered.length === 0 ? (
-              <div className="p-10 text-center font-mono text-[11px] opacity-60">
-                {filter === 'all' ? 'No documents uploaded yet.' : 'No documents in this view.'}
-              </div>
-            ) : (
-              filtered.map((doc) => (
-                <DocRow
-                  key={doc.id}
-                  doc={doc}
-                  selected={doc.id === selectedId}
-                  onSelect={() => setSelectedId(doc.id)}
-                  onOpen={() => router.push(`/courses/${courseId}/documents/${doc.id}/preview`)}
-                />
-              ))
-            )}
-          </div>
-        </div>
- 
-        {/* RIGHT — document detail panel */}
-        <div className="col-span-12 lg:col-span-5">
-          <div className="b-hard rounded-lg bg-white dark:bg-blue-dark/30 sticky top-20">
-            {selected ? (
-              <>
-                <div className="px-5 py-4 b-thin-b">
-                  <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-1">
-                    Document detail
-                  </div>
-                  <h3 className="font-medium leading-tight truncate">{selected.filename}</h3>
-                  <div className="font-mono text-[11px] opacity-70 mt-1">
-                    {selected.documentType || 'lecture'} · {formatSize(selected.size)}
-                  </div>
-                </div>
-                <div className="p-5 space-y-5">
-                  <div>
-                    <div className="flex items-end justify-between b-thin-b pb-1.5 mb-3">
-                      <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-                        Lifecycle
-                      </span>
-                    </div>
-                    <Lifecycle status={selected.status} />
-                    {selected.processingStage && (
-                      <div className="font-mono text-[11px] opacity-70 mt-2">
-                        stage · {selected.processingStage}
-                      </div>
-                    )}
-                  </div>
- 
-                  {selected.status === 'error' && selected.errorMessage && (
-                    <div
-                      className="b-hard-1 rounded-md p-3"
-                      style={{ borderColor: '#b3261e', color: '#b3261e' }}
-                    >
-                      <div className="font-mono text-[10px] tracking-[0.2em] uppercase mb-1">
-                        Error
-                      </div>
-                      <div className="font-mono text-[12px] leading-relaxed">
-                        {selected.errorMessage}
-                      </div>
-                    </div>
-                  )}
- 
-                  <ErrorBoundary>
-                    <DocumentProcessingPanel
-                      documentId={selected.id}
-                      accessToken={accessToken}
-                      onViewPreview={() =>
-                        router.push(`/courses/${courseId}/documents/${selected.id}/preview`)
-                      }
-                    />
-                  </ErrorBoundary>
- 
-                  <div className="flex items-center gap-2 b-thin-t pt-4">
-                    <button
-                      className="btn-ghost"
-                      onClick={() =>
-                        router.push(`/courses/${courseId}/documents/${selected.id}/preview`)
-                      }
-                    >
-                      Open in viewer →
-                    </button>
-                    <button
-                      className="btn-primary"
-                      onClick={() => router.push(`/courses/${courseId}/study`)}
-                    >
-                      Use in study
-                    </button>
-                    <span className="ml-auto font-mono text-[10px] opacity-60 truncate">
-                      id · {selected.id.slice(0, 8)}
-                    </span>
-                  </div>
-                </div>
-              </>
-            ) : (
-              <div className="p-10 text-center">
-                <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-1">
-                  Document detail
-                </div>
-                <div className="font-mono text-[11px] opacity-50 mt-3">
-                  Select a document to inspect it.
-                </div>
-              </div>
-            )}
-          </div>
-        </div>
-      </div>
-    </main>
-  );
-}
- 
-function Stat2({ n, k, warn, err }: { n: number; k: string; warn?: boolean; err?: boolean }) {
-  const cls = err ? 'text-err' : warn ? 'text-warn' : '';
-  return (
-    <div className="text-center">
-      <div className={`text-xl tnum font-medium ${cls}`}>{n}</div>
-      <div className="font-mono text-[9px] tracking-[0.18em] uppercase opacity-70">{k}</div>
-    </div>
-  );
-}
- 
-function DocRow({
-  doc,
-  selected,
-  onSelect,
-  onOpen,
-}: {
-  doc: DocumentListRow;
-  selected: boolean;
-  onSelect: () => void;
-  onOpen: () => void;
-}) {
-  const dot = STATE_DOT[doc.status] || '#7a7f9a';
-  const animated = doc.status === 'processing' || doc.status === 'uploading';
- 
-  return (
-    <div
-      onClick={onSelect}
-      className={`grid grid-cols-[1fr_100px_120px_30px] gap-3 px-4 py-3 b-thin-b items-center cursor-pointer transition-colors ${
-        selected ? 'bg-blue-dark/8 dark:bg-white/10' : 'hover:bg-blue-dark/5 dark:hover:bg-white/5'
-      }`}
-    >
-      <div className="flex items-center gap-3 min-w-0">
-        <div className="w-8 h-10 b-thin stripes flex-shrink-0 rounded-sm" />
-        <div className="min-w-0">
-          <div className="text-sm font-medium truncate">{doc.filename}</div>
-          <div className="font-mono text-[10px] opacity-60 mt-0.5 uppercase">
-            {doc.documentType || 'lecture'}
-          </div>
-        </div>
-      </div>
- 
-      <div className="font-mono text-[11px] opacity-80 tnum">{formatSize(doc.size)}</div>
- 
-      <div>
-        <span className="chip" style={{ color: dot, borderColor: dot, border: '1px solid' }}>
-          <span
-            className={`inline-block w-1.5 h-1.5 rounded-full ${animated ? 'dotpulse' : ''}`}
-            style={{ background: dot }}
-          />
-          {doc.status}
-        </span>
-      </div>
- 
-      <button
-        onClick={(e) => {
-          e.stopPropagation();
-          onOpen();
-        }}
-        className="font-mono text-sm opacity-60 hover:opacity-100"
-      >
-        →
-      </button>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/index.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/index.html deleted file mode 100644 index 20adce5..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/study - - - - - - - - - -
-
-

All files src/app/courses/[courseId]/study

-
- -
- 19.71% - Statements - 14/71 -
- - -
- 0% - Branches - 0/18 -
- - -
- 8.33% - Functions - 1/12 -
- - -
- 21.87% - Lines - 14/64 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
page.tsx -
-
19.71%14/710%0/188.33%1/1221.87%14/64
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/page.tsx.html deleted file mode 100644 index 8f54d84..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/[courseId]/study/page.tsx.html +++ /dev/null @@ -1,667 +0,0 @@ - - - - - - Code coverage report for src/app/courses/[courseId]/study/page.tsx - - - - - - - - - -
-
-

All files / src/app/courses/[courseId]/study page.tsx

-
- -
- 19.71% - Statements - 14/71 -
- - -
- 0% - Branches - 0/18 -
- - -
- 8.33% - Functions - 1/12 -
- - -
- 21.87% - Lines - 14/64 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195  -  -1x -1x -1x -  -1x -1x -  -  -  -  -  -1x -1x -1x -1x -1x -1x -  -1x -69x -69x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useEffect, useMemo, useState } from 'react';
-import { useParams, useSearchParams } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import type { ApiStudyResponse, StudyAction } from '@/lib/api/types';
-import { getCourse, getCourseLessons, type Course, type Lesson } from '@/lib/services/courses';
-import { getDocumentListRows } from '@/lib/api/documents';
-import {
-  getCourseProgress,
-  markLessonComplete,
-  type Badge,
-  type CourseProgress,
-} from '@/lib/services/progress';
-import { SplitPane } from '@/components/study/SplitPane';
-import { SourcePane } from '@/components/study/SourcePane';
-import { OutputPane } from '@/components/study/OutputPane';
-import { BadgeDisplay } from '@/components/ui/BadgeDisplay';
-import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
- 
-export default function StudyPage() {
-  const params = useParams();
-  const searchParams = useSearchParams();
-  const courseId = params.courseId as string;
-  const { data: session } = useSession();
-  const accessToken = session?.user?.accessToken;
- 
-  const initialQuery = searchParams.get('q') ?? '';
-  const initialAction = (searchParams.get('action') as StudyAction) || null;
- 
-  const [course, setCourse] = useState<Course | null>(null);
-  const [lessons, setLessons] = useState<Lesson[]>([]);
-  const [selectedLesson, setSelectedLesson] = useState<Lesson | null>(null);
-  const [completedLessons, setCompletedLessons] = useState<Set<string>>(new Set());
-  const [courseProgress, setCourseProgress] = useState<CourseProgress | null>(null);
-  const [newBadges, setNewBadges] = useState<Badge[]>([]);
-  const [loading, setLoading] = useState(true);
-  const [hasIndexedDocs, setHasIndexedDocs] = useState(false);
-  const [lastActionResult, setLastActionResult] = useState<{
-    result: ApiStudyResponse;
-    lesson: Lesson | null;
-  } | null>(null);
- 
-  const activeCitationDocIds = useMemo(() => {
-    Iif (!lastActionResult) return new Set<string>();
-    return new Set(
-      lastActionResult.result.citations.map((c) => c.doc_id).filter(Boolean) as string[]
-    );
-  }, [lastActionResult]);
- 
-  const lastStudiedLessonId = lastActionResult?.lesson ? String(lastActionResult.lesson.id) : null;
- 
-  const handleActionResult = useCallback((result: ApiStudyResponse, lesson: Lesson | null) => {
-    setLastActionResult({ result, lesson });
-  }, []);
- 
-  useEffect(() => {
-    async function load() {
-      try {
-        const [courseData, lessonsData, docs] = await Promise.all([
-          getCourse(courseId, accessToken),
-          getCourseLessons(courseId, accessToken),
-          getDocumentListRows(courseId, accessToken),
-        ]);
-        setCourse(courseData);
-        setLessons(lessonsData);
-        Iif (lessonsData.length > 0) setSelectedLesson(lessonsData[0]);
-        setHasIndexedDocs(docs.some((d) => d.status === 'ready'));
-      } catch {
-        // empty state shown in SourcePane
-      } finally {
-        setLoading(false);
-      }
-    }
- 
-    async function loadProgress() {
-      Iif (!accessToken) return;
-      try {
-        const p = await getCourseProgress(courseId, accessToken);
-        setCourseProgress(p);
-        Iif (p.completedLessonIds.length > 0) {
-          setCompletedLessons(new Set(p.completedLessonIds));
-        }
-      } catch {
-        /* non-critical */
-      }
-    }
- 
-    Iif (courseId) {
-      load();
-      loadProgress();
-    }
-  }, [courseId, accessToken]);
- 
-  const handleMarkComplete = useCallback(
-    async (lesson: Lesson) => {
-      Iif (!accessToken) return;
-      const lessonId = String(lesson.id);
-      try {
-        const result = await markLessonComplete(lessonId, courseId, accessToken);
-        setCompletedLessons((prev) => new Set([...prev, lessonId]));
-        setCourseProgress(result.courseProgress);
-        Iif (result.newBadges.length > 0) {
-          setNewBadges(result.newBadges);
-          setTimeout(() => setNewBadges([]), 5000);
-        }
-      } catch {
-        /* non-critical */
-      }
-    },
-    [courseId, accessToken]
-  );
- 
-  Iif (loading) {
-    return (
-      <div className="h-[calc(100vh-3.5rem)] flex items-center justify-center">
-        <div className="space-y-2 text-center">
-          <div className="w-8 h-8 border-2 border-blue-dark/30 border-t-blue-dark rounded-full animate-spin mx-auto" />
-          <p className="font-mono text-[11px] tracking-[0.18em] uppercase opacity-60">Loading…</p>
-        </div>
-      </div>
-    );
-  }
- 
-  return (
-    <div className="flex flex-col h-[calc(100vh-3.5rem)]">
-      {/* Progress strip */}
-      {courseProgress && (
-        <div className="flex-shrink-0 px-6 py-2 bg-white b-thin-b flex items-center gap-4">
-          <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70 whitespace-nowrap">
-            Progress
-          </span>
-          <div className="flex-1 h-1.5 b-thin overflow-hidden rounded-none">
-            <div
-              className="h-full bg-blue-dark transition-all"
-              style={{ width: `${courseProgress.percent}%` }}
-            />
-          </div>
-          <span className="font-mono text-[11px] opacity-60 whitespace-nowrap">
-            {courseProgress.completedCount}/{courseProgress.lessonCount}
-          </span>
-        </div>
-      )}
- 
-      {/* Badge notification */}
-      {newBadges.length > 0 && (
-        <div className="flex-shrink-0 flex items-center gap-3 px-6 py-2 b-thin-b bg-white">
-          <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70">
-            {newBadges.length === 1 ? 'Badge earned' : 'Badges earned'}
-          </span>
-          <div className="flex gap-2">
-            {newBadges.map((badge) => (
-              <BadgeDisplay key={badge.id} badge={badge} size="sm" />
-            ))}
-          </div>
-        </div>
-      )}
- 
-      {/* Split pane */}
-      <div className="flex-1 overflow-hidden">
-        <SplitPane
-          left={
-            <SourcePane
-              courseId={courseId}
-              accessToken={accessToken}
-              courseTitle={course?.title}
-              lessons={lessons}
-              selectedLesson={selectedLesson}
-              completedLessons={completedLessons}
-              onSelectLesson={setSelectedLesson}
-              onMarkComplete={handleMarkComplete}
-              activeCitationDocIds={activeCitationDocIds}
-              lastStudiedLessonId={lastStudiedLessonId}
-            />
-          }
-          right={
-            <ErrorBoundary>
-              <OutputPane
-                courseId={courseId}
-                accessToken={accessToken}
-                selectedLesson={selectedLesson}
-                hasIndexedDocs={hasIndexedDocs}
-                initialQuery={initialQuery}
-                initialAction={initialAction}
-                onActionResult={handleActionResult}
-              />
-            </ErrorBoundary>
-          }
-          defaultLeftPercent={40}
-        />
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/error.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/error.tsx.html deleted file mode 100644 index 3ac6fda..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/error.tsx.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - Code coverage report for src/app/courses/error.tsx - - - - - - - - - -
-
-

All files / src/app/courses error.tsx

-
- -
- 0% - Statements - 0/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect } from 'react';
- 
-export default function CoursesError({
-  error,
-  reset,
-}: {
-  error: Error & { digest?: string };
-  reset: () => void;
-}) {
-  useEffect(() => {
-    console.error('[CoursesError]', error);
-  }, [error]);
- 
-  return (
-    <main className="max-w-8xl mx-auto px-6 py-16 text-center">
-      <p className="text-sm opacity-70 mb-4">Something went wrong loading this page.</p>
-      <button
-        onClick={reset}
-        className="font-mono text-[11px] tracking-[0.14em] uppercase px-4 py-2 b-thin rounded-md hover:bg-blue-dark/5 dark:hover:bg-white/10 transition-colors"
-      >
-        Try again
-      </button>
-    </main>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/index.html b/apps/web/coverage/lcov-report/src/app/courses/index.html deleted file mode 100644 index 74f7dd1..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Code coverage report for src/app/courses - - - - - - - - - -
-
-

All files src/app/courses

-
- -
- 0% - Statements - 0/74 -
- - -
- 0% - Branches - 0/26 -
- - -
- 0% - Functions - 0/23 -
- - -
- 0% - Lines - 0/71 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
error.tsx -
-
0%0/4100%0/00%0/20%0/4
layout.tsx -
-
0%0/3100%0/00%0/10%0/3
page.tsx -
-
0%0/670%0/260%0/200%0/64
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/layout.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/layout.tsx.html deleted file mode 100644 index a51bcc0..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/layout.tsx.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for src/app/courses/layout.tsx - - - - - - - - - -
-
-

All files / src/app/courses layout.tsx

-
- -
- 0% - Statements - 0/3 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/3 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { TopBar } from '@/components/ui/TopBar';
-import { ToastProvider } from '@/components/ui/Toast';
- 
-export default function CoursesLayout({ children }: { children: React.ReactNode }) {
-  return (
-    <ToastProvider>
-      <div className="min-h-screen bg-surface">
-        <TopBar />
-        {children}
-      </div>
-    </ToastProvider>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html b/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html deleted file mode 100644 index c22d56a..0000000 --- a/apps/web/coverage/lcov-report/src/app/courses/page.tsx.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - Code coverage report for src/app/courses/page.tsx - - - - - - - - - -
-
-

All files / src/app/courses page.tsx

-
- -
- 0% - Statements - 0/67 -
- - -
- 0% - Branches - 0/26 -
- - -
- 0% - Functions - 0/20 -
- - -
- 0% - Lines - 0/64 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect, useState } from 'react';
-import { useSession } from 'next-auth/react';
-import { useRouter } from 'next/navigation';
-import { getCourses, createCourse, MVP_COURSES_LIMIT, type Course } from '@/lib/services/courses';
-import { getDocumentListRows } from '@/lib/api/documents';
-import { CourseCard } from '@/components/courses/CourseCard';
-import { CreateCourseModal } from '@/components/courses/CreateCourseModal';
- 
-type Filter = 'all';
- 
-interface DocStats {
-  total: number;
-  ready: number;
-  processing: number;
-  error: number;
-}
- 
-export default function CoursesPage() {
-  const { data: session, status } = useSession();
-  const router = useRouter();
-  const [courses, setCourses] = useState<Course[]>([]);
-  const [docStats, setDocStats] = useState<Record<string | number, DocStats>>({});
-  const [globalStats, setGlobalStats] = useState({ docs: 0, indexed: 0, processing: 0 });
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
-  const [_filter] = useState<Filter>('all');
-  const [showCreate, setShowCreate] = useState(false);
- 
-  useEffect(() => {
-    function onKey(e: KeyboardEvent) {
-      Iif ((e.metaKey || e.ctrlKey) && e.key === 'n') {
-        e.preventDefault();
-        setShowCreate(true);
-      }
-    }
-    window.addEventListener('keydown', onKey);
-    return () => window.removeEventListener('keydown', onKey);
-  }, []);
- 
-  useEffect(() => {
-    Iif (status === 'unauthenticated') {
-      router.push('/login');
-      return;
-    }
-    Iif (status !== 'authenticated') return;
- 
-    const token = session?.user?.accessToken;
- 
-    async function fetchAll() {
-      try {
-        const data = await getCourses(0, MVP_COURSES_LIMIT, token);
-        setCourses(data);
- 
-        const docsResults = await Promise.allSettled(
-          data.map((c) => getDocumentListRows(String(c.id), token))
-        );
- 
-        const statsMap: Record<string | number, DocStats> = {};
-        let totalDocs = 0,
-          totalIndexed = 0,
-          totalProcessing = 0;
-        docsResults.forEach((r, i) => {
-          Iif (r.status === 'fulfilled') {
-            const docs = r.value;
-            const stats: DocStats = {
-              total: docs.length,
-              ready: docs.filter((d) => d.status === 'ready').length,
-              processing: docs.filter((d) => d.status === 'processing' || d.status === 'uploading')
-                .length,
-              error: docs.filter((d) => d.status === 'error').length,
-            };
-            statsMap[data[i].id] = stats;
-            totalDocs += stats.total;
-            totalIndexed += stats.ready;
-            totalProcessing += stats.processing;
-          }
-        });
-        setDocStats(statsMap);
-        setGlobalStats({ docs: totalDocs, indexed: totalIndexed, processing: totalProcessing });
-      } catch (err) {
-        setError(err instanceof Error ? err.message : 'Failed to load courses');
-      } finally {
-        setLoading(false);
-      }
-    }
-    fetchAll();
-  }, [status, session, router]);
- 
-  async function handleCreate(title: string, description?: string) {
-    const created = await createCourse(title, description);
-    setCourses((prev) => [...prev, created]);
-    router.push(`/courses/${created.id}`);
-  }
- 
-  Iif (status === 'loading' || (status === 'authenticated' && loading)) {
-    return (
-      <main className="page-fade max-w-8xl mx-auto px-6 py-8">
-        <div className="animate-pulse space-y-8">
-          <div className="h-10 w-1/2 bg-blue-dark/10 rounded" />
-          <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
-            {[1, 2, 3].map((i) => (
-              <div key={i} className="b-hard rounded-lg p-5 space-y-4" style={{ minHeight: 238 }}>
-                <div className="h-3 w-1/3 bg-blue-dark/10 rounded" />
-                <div className="h-20 bg-blue-dark/5 rounded" />
-                <div className="h-5 w-3/4 bg-blue-dark/10 rounded" />
-              </div>
-            ))}
-          </div>
-        </div>
-      </main>
-    );
-  }
- 
-  return (
-    <main className="page-fade max-w-8xl mx-auto px-6 py-8">
-      {/* Hero */}
-      <div className="grid grid-cols-12 gap-6 mb-10">
-        <div className="col-span-12 lg:col-span-8">
-          <div className="flex items-center gap-2 font-mono text-[11px] tracking-[0.12em] uppercase opacity-70 mb-6">
-            <span>Academy</span>
-            <span className="opacity-40">/</span>
-            <span className="font-semibold opacity-100">Courses</span>
-          </div>
-          <h1 className="text-5xl lg:text-6xl font-medium tracking-tight leading-[1.05] mb-5">
-            Study, grounded in your
-            <br className="hidden lg:block" /> own course material.
-          </h1>
-          <p className="text-lg leading-relaxed max-w-[58ch] opacity-80">
-            Each course is an isolated workspace. Drop in slides, notes and past exams — Academy
-            indexes everything and keeps every answer anchored to its source.
-          </p>
-          <div className="flex items-center gap-3 mt-6">
-            <span className="font-mono text-[11px] opacity-60">
-              {courses.length} {courses.length === 1 ? 'course' : 'courses'} · {globalStats.docs}{' '}
-              documents · {globalStats.indexed} indexed
-            </span>
-          </div>
-        </div>
- 
-        {/* Stats widget */}
-        <div className="col-span-12 lg:col-span-4">
-          <div className="b-hard rounded-lg p-5 bg-white dark:bg-blue-dark/40 tick-corners">
-            <div className="flex items-end justify-between b-thin-b pb-1.5 mb-3">
-              <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-                Local index · QVAC
-              </span>
-              <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-60">
-                v0.1 MVP
-              </span>
-            </div>
-            <div className="grid grid-cols-2 gap-3">
-              <StatBox n={String(courses.length)} k="courses" />
-              <StatBox n={String(globalStats.docs)} k="documents" />
-              <StatBox n={String(globalStats.indexed)} k="indexed" />
-              <StatBox
-                n={String(globalStats.processing)}
-                k="processing"
-                warn={globalStats.processing > 0}
-              />
-            </div>
-            <div className="mt-4 pt-4 b-thin-t flex items-center justify-between">
-              <span className="font-mono text-[11px] opacity-70">
-                Local-first · all data on device
-              </span>
-              <span className="font-mono text-[10px] tracking-[0.2em] uppercase">v0.1</span>
-            </div>
-          </div>
-        </div>
-      </div>
- 
-      {/* Filter rail */}
-      <div className="flex items-center gap-2 mb-4">
-        <button className="font-mono text-[11px] tracking-[0.18em] uppercase px-3 h-8 rounded-md bg-blue-dark text-white dark:bg-white dark:text-blue-dark">
-          All <span className="opacity-60 ml-1">{courses.length}</span>
-        </button>
-        <div className="ml-auto font-mono text-[11px] opacity-60">sorted · last updated</div>
-      </div>
- 
-      {error ? (
-        <div
-          className="b-hard rounded-lg p-6 text-center"
-          style={{ borderColor: '#b3261e', color: '#b3261e' }}
-        >
-          <p className="text-sm">{error}</p>
-          <button
-            onClick={() => window.location.reload()}
-            className="mt-3 text-sm font-medium underline"
-          >
-            Retry
-          </button>
-        </div>
-      ) : courses.length === 0 ? (
-        <div className="b-hard rounded-lg p-10 text-center bg-white dark:bg-blue-dark">
-          <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
-          <div className="font-medium text-lg">No courses yet</div>
-          <div className="opacity-70 text-sm mt-1 mb-5">
-            Create your first course to get started.
-          </div>
-          <button className="btn-primary" onClick={() => setShowCreate(true)}>
-            Create workspace →
-          </button>
-        </div>
-      ) : (
-        <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
-          {courses.map((course) => (
-            <CourseCard key={course.id} course={course} stats={docStats[course.id] ?? null} />
-          ))}
-          {/* Create course card */}
-          <button
-            onClick={() => setShowCreate(true)}
-            className="b-hard rounded-lg p-6 stripes hover-card flex flex-col items-center justify-center min-h-[238px] text-center w-full"
-          >
-            <div className="font-mono text-3xl leading-none mb-2">+</div>
-            <div className="font-medium">Create new course</div>
-            <div className="font-mono text-[11px] opacity-70 mt-1">⌘N</div>
-          </button>
-        </div>
-      )}
- 
-      {showCreate && (
-        <CreateCourseModal onClose={() => setShowCreate(false)} onCreate={handleCreate} />
-      )}
-    </main>
-  );
-}
- 
-function StatBox({ n, k, warn }: { n: string; k: string; warn?: boolean }) {
-  return (
-    <div className="b-thin rounded-md p-3">
-      <div
-        className={`text-2xl font-medium tnum ${warn ? '' : ''}`}
-        style={warn ? { color: '#a55a00' } : {}}
-      >
-        {n}
-      </div>
-      <div className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70 mt-1">{k}</div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/dashboard/error.tsx.html b/apps/web/coverage/lcov-report/src/app/dashboard/error.tsx.html deleted file mode 100644 index 9c0f4d9..0000000 --- a/apps/web/coverage/lcov-report/src/app/dashboard/error.tsx.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - Code coverage report for src/app/dashboard/error.tsx - - - - - - - - - -
-
-

All files / src/app/dashboard error.tsx

-
- -
- 0% - Statements - 0/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect } from 'react';
- 
-export default function DashboardError({
-  error,
-  reset,
-}: {
-  error: Error & { digest?: string };
-  reset: () => void;
-}) {
-  useEffect(() => {
-    console.error('[DashboardError]', error);
-  }, [error]);
- 
-  return (
-    <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
-      <div className="text-center max-w-sm">
-        <p className="text-sm text-gray-600 mb-4">Something went wrong loading the dashboard.</p>
-        <button
-          onClick={reset}
-          className="px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700"
-        >
-          Try again
-        </button>
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/dashboard/index.html b/apps/web/coverage/lcov-report/src/app/dashboard/index.html deleted file mode 100644 index be14600..0000000 --- a/apps/web/coverage/lcov-report/src/app/dashboard/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Code coverage report for src/app/dashboard - - - - - - - - - -
-
-

All files src/app/dashboard

-
- -
- 0% - Statements - 0/28 -
- - -
- 0% - Branches - 0/11 -
- - -
- 0% - Functions - 0/6 -
- - -
- 0% - Lines - 0/27 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
error.tsx -
-
0%0/4100%0/00%0/20%0/4
page.tsx -
-
0%0/240%0/110%0/40%0/23
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/dashboard/page.tsx.html b/apps/web/coverage/lcov-report/src/app/dashboard/page.tsx.html deleted file mode 100644 index d8af6e1..0000000 --- a/apps/web/coverage/lcov-report/src/app/dashboard/page.tsx.html +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - Code coverage report for src/app/dashboard/page.tsx - - - - - - - - - -
-
-

All files / src/app/dashboard page.tsx

-
- -
- 0% - Statements - 0/24 -
- - -
- 0% - Branches - 0/11 -
- - -
- 0% - Functions - 0/4 -
- - -
- 0% - Lines - 0/23 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect, useState } from 'react';
-import { signOut, useSession } from 'next-auth/react';
-import { useRouter } from 'next/navigation';
-import Link from 'next/link';
-import { getCourses, MVP_COURSES_LIMIT, type Course } from '@/lib/services/courses';
- 
-export default function DashboardPage() {
-  const { data: session, status } = useSession();
-  const router = useRouter();
-  const [courses, setCourses] = useState<Course[]>([]);
-  const [coursesLoading, setCoursesLoading] = useState(true);
- 
-  useEffect(() => {
-    Iif (status !== 'authenticated') return;
- 
-    async function fetchCourses() {
-      try {
-        const data = await getCourses(0, MVP_COURSES_LIMIT, session?.user?.accessToken);
-        setCourses(data);
-      } catch {
-        // Non-critical — dashboard still usable without course count
-      } finally {
-        setCoursesLoading(false);
-      }
-    }
- 
-    fetchCourses();
-  }, [status, session]);
- 
-  Iif (status === 'loading') {
-    return (
-      <div className="min-h-screen flex items-center justify-center bg-gray-50">
-        <div className="text-center">
-          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-orange-600 mx-auto"></div>
-          <p className="mt-4 text-gray-600">Loading...</p>
-        </div>
-      </div>
-    );
-  }
- 
-  Iif (status === 'unauthenticated') {
-    router.push('/login');
-    return null;
-  }
- 
-  const handleSignOut = async () => {
-    await signOut({ callbackUrl: '/login' });
-  };
- 
-  return (
-    <div className="min-h-screen bg-gray-50">
-      <header className="bg-white shadow-sm">
-        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
-          <h1 className="text-2xl font-bold text-gray-900">BitPolito Academy</h1>
-          <div className="flex items-center space-x-4">
-            <span className="text-sm text-gray-600">{session?.user?.email}</span>
-            <button
-              onClick={handleSignOut}
-              className="px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500"
-            >
-              Sign out
-            </button>
-          </div>
-        </div>
-      </header>
- 
-      <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
-        <div className="bg-white rounded-lg shadow p-6">
-          <h2 className="text-xl font-semibold text-gray-900 mb-4">
-            Welcome, {session?.user?.displayName || session?.user?.email}!
-          </h2>
- 
-          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
-            {/* User Info Card */}
-            <div className="bg-gray-50 rounded-lg p-4">
-              <h3 className="text-lg font-medium text-gray-900 mb-2">Your Profile</h3>
-              <dl className="space-y-2">
-                <div>
-                  <dt className="text-sm text-gray-500">Email</dt>
-                  <dd className="text-sm font-medium text-gray-900">{session?.user?.email}</dd>
-                </div>
-                <div>
-                  <dt className="text-sm text-gray-500">Display Name</dt>
-                  <dd className="text-sm font-medium text-gray-900">
-                    {session?.user?.displayName || 'Not set'}
-                  </dd>
-                </div>
-                <div>
-                  <dt className="text-sm text-gray-500">Role</dt>
-                  <dd className="text-sm font-medium text-gray-900 capitalize">
-                    {session?.user?.role || 'Student'}
-                  </dd>
-                </div>
-              </dl>
-            </div>
- 
-            {/* Quick Stats Card */}
-            <div className="bg-gray-50 rounded-lg p-4">
-              <h3 className="text-lg font-medium text-gray-900 mb-2">Your Progress</h3>
-              <div className="space-y-3">
-                <div className="flex justify-between items-center">
-                  <span className="text-sm text-gray-600">Available Courses</span>
-                  <span className="text-lg font-semibold text-orange-600">
-                    {coursesLoading ? (
-                      <span className="inline-block h-5 w-6 bg-gray-200 rounded animate-pulse" />
-                    ) : (
-                      courses.length
-                    )}
-                  </span>
-                </div>
-                <div className="flex justify-between items-center">
-                  <span className="text-sm text-gray-600">Courses Completed</span>
-                  <span className="text-lg font-semibold text-green-600">–</span>
-                </div>
-                <div className="flex justify-between items-center">
-                  <span className="text-sm text-gray-600">Certificates Earned</span>
-                  <span className="text-lg font-semibold text-blue-600">–</span>
-                </div>
-              </div>
-            </div>
- 
-            {/* Quick Actions Card */}
-            <div className="bg-gray-50 rounded-lg p-4">
-              <h3 className="text-lg font-medium text-gray-900 mb-2">Quick Actions</h3>
-              <div className="space-y-2">
-                <Link
-                  href="/courses"
-                  className="block w-full px-4 py-2 text-center text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700"
-                >
-                  Browse Courses
-                </Link>
-              </div>
-            </div>
-          </div>
-        </div>
-      </main>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/error.tsx.html b/apps/web/coverage/lcov-report/src/app/error.tsx.html deleted file mode 100644 index b43e2d6..0000000 --- a/apps/web/coverage/lcov-report/src/app/error.tsx.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - Code coverage report for src/app/error.tsx - - - - - - - - - -
-
-

All files / src/app error.tsx

-
- -
- 0% - Statements - 0/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect } from 'react';
- 
-export default function GlobalError({
-  error,
-  reset,
-}: {
-  error: Error & { digest?: string };
-  reset: () => void;
-}) {
-  useEffect(() => {
-    console.error('[GlobalError]', error);
-  }, [error]);
- 
-  return (
-    <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
-      <div className="text-center max-w-sm">
-        <p className="text-sm text-gray-600 mb-4">Something went wrong. Please try again.</p>
-        <button
-          onClick={reset}
-          className="px-4 py-2 text-sm font-medium text-white bg-orange-600 rounded-md hover:bg-orange-700"
-        >
-          Try again
-        </button>
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/index.html b/apps/web/coverage/lcov-report/src/app/index.html deleted file mode 100644 index b473467..0000000 --- a/apps/web/coverage/lcov-report/src/app/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Code coverage report for src/app - - - - - - - - - -
-
-

All files src/app

-
- -
- 0% - Statements - 0/25 -
- - -
- 0% - Branches - 0/6 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/23 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
error.tsx -
-
0%0/4100%0/00%0/20%0/4
layout.tsx -
-
0%0/8100%0/00%0/10%0/6
page.tsx -
-
0%0/130%0/60%0/20%0/13
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/layout.tsx.html b/apps/web/coverage/lcov-report/src/app/layout.tsx.html deleted file mode 100644 index dfd68ea..0000000 --- a/apps/web/coverage/lcov-report/src/app/layout.tsx.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - Code coverage report for src/app/layout.tsx - - - - - - - - - -
-
-

All files / src/app layout.tsx

-
- -
- 0% - Statements - 0/8 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import type { Metadata, Viewport } from 'next';
-import './globals.css';
-import { AuthProvider } from '@/components/providers/AuthProvider';
-import { SessionErrorGuard } from '@/components/providers/SessionErrorGuard';
- 
-export const metadata: Metadata = {
-  title: 'BitPolito Academy',
-  description: 'Learn Bitcoin with interactive courses',
-};
- 
-export const viewport: Viewport = {
-  width: 'device-width',
-  initialScale: 1,
-};
- 
-export default function RootLayout({ children }: { children: React.ReactNode }) {
-  return (
-    <html lang="en">
-      <body>
-        <AuthProvider>
-          <SessionErrorGuard />
-          {children}
-        </AuthProvider>
-      </body>
-    </html>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/app/page.tsx.html b/apps/web/coverage/lcov-report/src/app/page.tsx.html deleted file mode 100644 index ee65f69..0000000 --- a/apps/web/coverage/lcov-report/src/app/page.tsx.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - Code coverage report for src/app/page.tsx - - - - - - - - - -
-
-

All files / src/app page.tsx

-
- -
- 0% - Statements - 0/13 -
- - -
- 0% - Branches - 0/6 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/13 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-import { useSession } from 'next-auth/react';
- 
-export default function Home() {
-  const router = useRouter();
-  const { status } = useSession();
- 
-  useEffect(() => {
-    if (status === 'unauthenticated') {
-      router.push('/login');
-    } else Iif (status === 'authenticated') {
-      router.push('/dashboard');
-    }
-  }, [status, router]);
- 
-  Iif (status === 'loading' || status === 'unauthenticated') {
-    return (
-      <main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gradient-to-b from-dark to-light">
-        <div className="text-center">
-          <p className="text-gray-500">Redirecting...</p>
-        </div>
-      </main>
-    );
-  }
- 
-  return null;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/CourseCard.tsx.html b/apps/web/coverage/lcov-report/src/components/courses/CourseCard.tsx.html deleted file mode 100644 index 22b9626..0000000 --- a/apps/web/coverage/lcov-report/src/components/courses/CourseCard.tsx.html +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - Code coverage report for src/components/courses/CourseCard.tsx - - - - - - - - - -
-
-

All files / src/components/courses CourseCard.tsx

-
- -
- 0% - Statements - 0/5 -
- - -
- 0% - Branches - 0/19 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import Link from 'next/link';
-import type { Course } from '@/lib/services/courses';
- 
-interface DocStats {
-  total: number;
-  ready: number;
-  processing: number;
-  error: number;
-}
- 
-interface CourseCardProps {
-  course: Course;
-  stats?: DocStats | null;
-}
- 
-function Mini({ n, k, warn }: { n: number; k: string; warn?: boolean }) {
-  return (
-    <div className="text-center">
-      <div className="text-xl tnum font-medium" style={warn ? { color: '#a55a00' } : {}}>
-        {n}
-      </div>
-      <div className="font-mono text-[9px] tracking-[0.18em] uppercase opacity-70 mt-0.5">{k}</div>
-    </div>
-  );
-}
- 
-export function CourseCard({ course, stats = null }: CourseCardProps) {
-  const failed = stats?.error ?? 0;
-  const processing = stats?.processing ?? 0;
-  const statusDot =
-    failed > 0
-      ? { color: '#b3261e', label: `${failed} failed` }
-      : processing > 0
-        ? { color: '#a55a00', label: `${processing} processing` }
-        : stats
-          ? { color: '#1a7f3a', label: 'all indexed' }
-          : { color: '#001CE0', label: 'ready' };
- 
-  return (
-    <Link
-      href={`/courses/${course.id}`}
-      className="b-hard rounded-lg p-5 bg-white dark:bg-blue-dark/30 hover-card cursor-pointer block"
-    >
-      {/* Top row */}
-      <div className="flex items-center justify-between mb-3">
-        <span className="font-mono text-[10px] tracking-[0.2em] uppercase opacity-70">
-          {course.description ? course.description.slice(0, 20) : `#${course.id}`}
-        </span>
-      </div>
- 
-      {/* Striped cover */}
-      <div
-        className="stripes b-thin rounded-md mb-4 relative overflow-hidden"
-        style={{ aspectRatio: '16/7' }}
-      >
-        <div className="absolute inset-0 flex items-center justify-center">
-          <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-50">
-            {course.title.slice(0, 16)}
-          </span>
-        </div>
-        <div className="absolute top-1.5 left-1.5 w-2 h-2 border-l border-t border-current opacity-40" />
-        <div className="absolute top-1.5 right-1.5 w-2 h-2 border-r border-t border-current opacity-40" />
-        <div className="absolute bottom-1.5 left-1.5 w-2 h-2 border-l border-b border-current opacity-40" />
-        <div className="absolute bottom-1.5 right-1.5 w-2 h-2 border-r border-b border-current opacity-40" />
-      </div>
- 
-      <h3 className="text-lg font-medium leading-snug mb-1">{course.title}</h3>
-      {course.description && (
-        <div className="font-mono text-[11px] opacity-70 mb-3 line-clamp-1">
-          {course.description}
-        </div>
-      )}
- 
-      {/* Doc stats grid */}
-      {stats && (
-        <div className="grid grid-cols-3 gap-2 mb-4">
-          <Mini n={stats.total} k="docs" />
-          <Mini n={stats.ready} k="indexed" />
-          <Mini n={processing + failed} k="open" warn={failed > 0 || processing > 0} />
-        </div>
-      )}
- 
-      {/* Footer */}
-      <div className="flex items-center b-thin-t pt-3 mt-auto">
-        <span className="font-mono text-[11px] flex items-center gap-2">
-          <span
-            className="inline-block w-1.5 h-1.5 rounded-full flex-shrink-0"
-            style={{ background: statusDot.color }}
-          />
-          {statusDot.label}
-        </span>
-      </div>
-    </Link>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/CreateCourseModal.tsx.html b/apps/web/coverage/lcov-report/src/components/courses/CreateCourseModal.tsx.html deleted file mode 100644 index 39dd91d..0000000 --- a/apps/web/coverage/lcov-report/src/components/courses/CreateCourseModal.tsx.html +++ /dev/null @@ -1,472 +0,0 @@ - - - - - - Code coverage report for src/components/courses/CreateCourseModal.tsx - - - - - - - - - -
-
-

All files / src/components/courses CreateCourseModal.tsx

-
- -
- 0% - Statements - 0/29 -
- - -
- 0% - Branches - 0/14 -
- - -
- 0% - Functions - 0/9 -
- - -
- 0% - Lines - 0/26 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useState, useEffect, useRef } from 'react';
- 
-interface CreateCourseModalProps {
-  onClose: () => void;
-  onCreate: (title: string, description?: string) => Promise<void>;
-}
- 
-export function CreateCourseModal({ onClose, onCreate }: CreateCourseModalProps) {
-  const [title, setTitle] = useState('');
-  const [description, setDescription] = useState('');
-  const [saving, setSaving] = useState(false);
-  const [err, setErr] = useState<string | null>(null);
-  const inputRef = useRef<HTMLInputElement>(null);
- 
-  useEffect(() => {
-    inputRef.current?.focus();
-    function onKey(e: KeyboardEvent) {
-      Iif (e.key === 'Escape') onClose();
-    }
-    window.addEventListener('keydown', onKey);
-    return () => window.removeEventListener('keydown', onKey);
-  }, [onClose]);
- 
-  async function handleCreate() {
-    Iif (!title.trim()) {
-      setErr('Course title is required.');
-      return;
-    }
-    setSaving(true);
-    setErr(null);
-    try {
-      await onCreate(title.trim(), description.trim() || undefined);
-    } catch (e) {
-      setErr(e instanceof Error ? e.message : 'Failed to create course.');
-      setSaving(false);
-    }
-  }
- 
-  return (
-    <div
-      className="fixed inset-0 z-50 flex items-center justify-center p-6"
-      style={{ background: 'rgba(0,28,224,0.18)' }}
-      onClick={onClose}
-    >
-      <div
-        className="b-hard rounded-lg bg-white dark:bg-blue-dark w-full max-w-xl p-7"
-        onClick={(e) => e.stopPropagation()}
-      >
-        {/* Header */}
-        <div className="flex items-center justify-between mb-5">
-          <div className="flex items-center gap-2 font-mono text-[11px] tracking-[0.12em] uppercase opacity-70">
-            <span>Courses</span>
-            <span className="opacity-40">/</span>
-            <span className="font-semibold opacity-100">New</span>
-          </div>
-          <button
-            onClick={onClose}
-            className="font-mono text-lg opacity-50 hover:opacity-100 leading-none"
-          >
-            ×
-          </button>
-        </div>
- 
-        <h2 className="text-2xl font-medium mb-1">Create a course workspace</h2>
-        <p className="opacity-75 text-sm mb-6">
-          A course is a sealed bucket — its documents, embeddings and outputs never bleed into other
-          courses.
-        </p>
- 
-        <div className="grid grid-cols-1 gap-4 mb-5">
-          <label className="block">
-            <div className="flex items-baseline justify-between mb-1.5">
-              <span className="font-mono text-[10px] tracking-[0.2em] uppercase opacity-80">
-                Course title
-              </span>
-            </div>
-            <input
-              ref={inputRef}
-              value={title}
-              onChange={(e) => {
-                setTitle(e.target.value);
-                setErr(null);
-              }}
-              onKeyDown={(e) => {
-                Iif (e.key === 'Enter' && !saving) handleCreate();
-              }}
-              className="w-full h-10 px-3 b-hard rounded-md bg-transparent outline-none focus:bg-blue-dark/5 dark:focus:bg-white/10"
-              placeholder="e.g. Information Theory & Coding"
-            />
-          </label>
-          <label className="block">
-            <div className="flex items-baseline justify-between mb-1.5">
-              <span className="font-mono text-[10px] tracking-[0.2em] uppercase opacity-80">
-                Description
-              </span>
-              <span className="font-mono text-[10px] opacity-50">optional</span>
-            </div>
-            <input
-              value={description}
-              onChange={(e) => setDescription(e.target.value)}
-              className="w-full h-10 px-3 b-hard rounded-md bg-transparent outline-none focus:bg-blue-dark/5 dark:focus:bg-white/10"
-              placeholder="Short description or notes"
-            />
-          </label>
-        </div>
- 
-        {err && (
-          <div
-            className="mb-4 font-mono text-[11px] px-3 py-2 rounded-md"
-            style={{ background: '#b3261e18', color: '#b3261e' }}
-          >
-            {err}
-          </div>
-        )}
- 
-        <div className="flex items-center justify-end gap-2 b-thin-t pt-4">
-          <button className="btn-ghost" onClick={onClose} disabled={saving}>
-            Cancel
-          </button>
-          <button className="btn-primary" onClick={handleCreate} disabled={saving || !title.trim()}>
-            {saving ? 'Creating…' : 'Create workspace →'}
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/DocumentList.tsx.html b/apps/web/coverage/lcov-report/src/components/courses/DocumentList.tsx.html deleted file mode 100644 index 42f8761..0000000 --- a/apps/web/coverage/lcov-report/src/components/courses/DocumentList.tsx.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - Code coverage report for src/components/courses/DocumentList.tsx - - - - - - - - - -
-
-

All files / src/components/courses DocumentList.tsx

-
- -
- 0% - Statements - 0/57 -
- - -
- 0% - Branches - 0/30 -
- - -
- 0% - Functions - 0/16 -
- - -
- 0% - Lines - 0/50 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { getDocumentListRows } from '@/lib/api/documents';
-import type { DocumentListRow } from '@/lib/api/types';
-import { ProcessingIndicator } from './ProcessingIndicator';
-import { DocumentProcessingPanel } from '@/components/documents/DocumentProcessingPanel';
- 
-interface DocumentListProps {
-  courseId: string;
-  accessToken?: string;
-  refreshKey?: number;
-  onViewPreview?: (documentId: string) => void;
-}
- 
-const AUTO_POLL_INTERVAL_MS = 8000;
- 
-function formatFileSize(bytes: number): string {
-  Iif (bytes < 1024) return `${bytes} B`;
-  Iif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
-  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
-}
- 
-function formatTime(iso: string): string {
-  try {
-    return new Date(iso).toLocaleString(undefined, {
-      month: 'short',
-      day: 'numeric',
-      hour: '2-digit',
-      minute: '2-digit',
-    });
-  } catch {
-    return iso;
-  }
-}
- 
-export function DocumentList({
-  courseId,
-  accessToken,
-  refreshKey = 0,
-  onViewPreview,
-}: DocumentListProps) {
-  const [documents, setDocuments] = useState<DocumentListRow[]>([]);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
-  const [refreshing, setRefreshing] = useState(false);
-  const [expandedId, setExpandedId] = useState<string | null>(null);
-  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
- 
-  const fetchDocuments = useCallback(
-    async (silent = false) => {
-      try {
-        Iif (!silent) setError(null);
-        const rows = await getDocumentListRows(courseId, accessToken);
-        setDocuments(rows);
-      } catch (err) {
-        const message = err instanceof Error ? err.message : 'Failed to load documents';
-        if (message.includes('Request failed (404)') || message.includes('Request failed (50')) {
-          setDocuments([]);
-          setError(null);
-        } else Iif (!silent) {
-          setError(message);
-        }
-      } finally {
-        setLoading(false);
-        setRefreshing(false);
-      }
-    },
-    [courseId, accessToken],
-  );
- 
-  useEffect(() => {
-    fetchDocuments();
-  }, [fetchDocuments, refreshKey]);
- 
-  useEffect(() => {
-    const hasInProgress = documents.some((d) => !d.isTerminal);
-    Iif (hasInProgress) {
-      pollRef.current = setInterval(() => fetchDocuments(true), AUTO_POLL_INTERVAL_MS);
-    }
-    return () => {
-      Iif (pollRef.current) clearInterval(pollRef.current);
-    };
-  }, [documents, fetchDocuments]);
- 
-  function handleRefresh() {
-    setRefreshing(true);
-    fetchDocuments();
-  }
- 
-  function toggleExpand(id: string) {
-    setExpandedId((prev) => (prev === id ? null : id));
-  }
- 
-  Iif (loading) {
-    return (
-      <div className="space-y-3">
-        {[1, 2, 3].map((i) => (
-          <div key={i} className="animate-pulse flex items-center gap-3 p-3 rounded-lg bg-gray-50">
-            <div className="h-8 w-8 rounded bg-gray-200" />
-            <div className="flex-1 space-y-1.5">
-              <div className="h-3 w-2/3 rounded bg-gray-200" />
-              <div className="h-2.5 w-1/4 rounded bg-gray-200" />
-            </div>
-          </div>
-        ))}
-      </div>
-    );
-  }
- 
-  Iif (error) {
-    return (
-      <div className="rounded-lg border border-red-200 bg-red-50 p-4">
-        <p className="text-sm text-red-700">{error}</p>
-        <button
-          onClick={() => fetchDocuments()}
-          className="mt-2 text-sm font-medium text-red-700 hover:text-red-800 underline"
-        >
-          Retry
-        </button>
-      </div>
-    );
-  }
- 
-  Iif (documents.length === 0) {
-    return (
-      <div className="text-center py-8 px-4">
-        <svg
-          className="mx-auto h-10 w-10 text-gray-300"
-          fill="none"
-          viewBox="0 0 24 24"
-          strokeWidth={1.5}
-          stroke="currentColor"
-        >
-          <path
-            strokeLinecap="round"
-            strokeLinejoin="round"
-            d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m6.75 12H9.75m3 0h.008v.008H12.75v-.008zM12 18.75h.008v.008H12v-.008zm-3 0h.008v.008H9v-.008zm6-6h.008v.008h-.008v-.008zm-3 0h.008v.008H12v-.008zm-3 0h.008v.008H9v-.008z"
-          />
-        </svg>
-        <p className="mt-2 text-sm text-gray-500">No documents uploaded yet</p>
-        <p className="text-xs text-gray-400">Upload slides, notes, or reference material to get started</p>
-      </div>
-    );
-  }
- 
-  return (
-    <div>
-      <div className="flex items-center justify-between mb-3">
-        <p className="text-xs text-gray-500">
-          {documents.length} document{documents.length !== 1 ? 's' : ''}
-        </p>
-        <button
-          onClick={handleRefresh}
-          disabled={refreshing}
-          className="inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 disabled:opacity-50"
-          title="Refresh list"
-        >
-          <svg
-            className={`h-3.5 w-3.5 ${refreshing ? 'animate-spin' : ''}`}
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182"
-            />
-          </svg>
-          {refreshing ? 'Refreshing...' : 'Refresh'}
-        </button>
-      </div>
- 
-      <ul className="divide-y divide-gray-100">
-        {documents.map((doc) => {
-          const isExpanded = expandedId === doc.id;
-          return (
-            <li key={doc.id} className="py-2 px-1">
-              <button
-                type="button"
-                onClick={() => toggleExpand(doc.id)}
-                className="w-full text-left rounded-md hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-orange-400"
-                aria-expanded={isExpanded}
-              >
-                <div className="flex items-start gap-3">
-                  {/* Expand chevron */}
-                  <svg
-                    className={`mt-1 h-4 w-4 flex-shrink-0 text-gray-400 transition-transform ${isExpanded ? 'rotate-90' : ''}`}
-                    fill="none"
-                    viewBox="0 0 24 24"
-                    strokeWidth={2}
-                    stroke="currentColor"
-                  >
-                    <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
-                  </svg>
- 
-                  {/* File type badge */}
-                  <span className="flex-shrink-0 mt-0.5 inline-flex items-center justify-center h-8 w-8 rounded-md bg-gray-100 text-[10px] font-bold text-gray-500 uppercase">
-                    {doc.fileType}
-                  </span>
- 
-                  <div className="flex-1 min-w-0">
-                    <div className="flex items-center gap-2">
-                      <p className="text-sm font-medium text-gray-900 truncate">{doc.filename}</p>
-                      <ProcessingIndicator status={doc.status} stage={doc.processingStage} />
-                    </div>
- 
-                    <div className="mt-0.5 flex items-center gap-3 text-xs text-gray-500">
-                      <span>{formatFileSize(doc.size)}</span>
-                      <span title="Uploaded">{formatTime(doc.createdAt)}</span>
-                      <span title="Last updated">updated {formatTime(doc.updatedAt)}</span>
-                    </div>
- 
-                    {doc.status === 'error' && doc.errorMessage && (
-                      <p className="mt-1 text-xs text-red-600 truncate" title={doc.errorMessage}>
-                        {doc.errorMessage}
-                      </p>
-                    )}
-                  </div>
-                </div>
-              </button>
- 
-              {/* Expandable detail panel (U-05) */}
-              {isExpanded && (
-                <div className="mt-2 ml-7">
-                  <DocumentProcessingPanel
-                    documentId={doc.id}
-                    accessToken={accessToken}
-                    onViewPreview={onViewPreview}
-                  />
-                </div>
-              )}
-            </li>
-          );
-        })}
-      </ul>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html b/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html deleted file mode 100644 index 1522396..0000000 --- a/apps/web/coverage/lcov-report/src/components/courses/ProcessingIndicator.tsx.html +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - Code coverage report for src/components/courses/ProcessingIndicator.tsx - - - - - - - - - -
-
-

All files / src/components/courses ProcessingIndicator.tsx

-
- -
- 0% - Statements - 0/5 -
- - -
- 0% - Branches - 0/7 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import type { DocumentStatus, ProcessingStage } from '@/lib/api/types';
- 
-interface ProcessingIndicatorProps {
-  status: DocumentStatus;
-  stage?: ProcessingStage;
-  className?: string;
-}
- 
-const STATUS_CONFIG: Record<
-  DocumentStatus,
-  { label: string; bg: string; text: string; dot: string }
-> = {
-  uploading: {
-    label: 'Uploading',
-    bg: 'bg-blue-50',
-    text: 'text-blue-700',
-    dot: 'bg-blue-500 animate-pulse',
-  },
-  processing: {
-    label: 'Processing',
-    bg: 'bg-yellow-50',
-    text: 'text-yellow-700',
-    dot: 'bg-yellow-500 animate-pulse',
-  },
-  ready: {
-    label: 'Ready',
-    bg: 'bg-green-50',
-    text: 'text-green-700',
-    dot: 'bg-green-500',
-  },
-  error: {
-    label: 'Error',
-    bg: 'bg-red-50',
-    text: 'text-red-700',
-    dot: 'bg-red-500',
-  },
-};
- 
-const STAGE_LABELS: Record<ProcessingStage, string> = {
-  queued: 'Queued',
-  uploading: 'Uploading',
-  parsing: 'Parsing',
-  normalizing: 'Normalizing',
-  chunking: 'Chunking',
-  indexing: 'Indexing',
-  done: 'Done',
-  error: 'Error',
-};
- 
-export function ProcessingIndicator({ status, stage, className = '' }: ProcessingIndicatorProps) {
-  const config = STATUS_CONFIG[status];
-  const stageLabel = stage && stage !== 'done' && stage !== 'error' ? STAGE_LABELS[stage] : null;
- 
-  return (
-    <span
-      className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${config.bg} ${config.text} ${className}`}
-    >
-      <span className={`h-1.5 w-1.5 rounded-full ${config.dot}`} />
-      {config.label}
-      {stageLabel && <span className="opacity-75">· {stageLabel}</span>}
-    </span>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/courses/index.html b/apps/web/coverage/lcov-report/src/components/courses/index.html deleted file mode 100644 index e26805e..0000000 --- a/apps/web/coverage/lcov-report/src/components/courses/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Code coverage report for src/components/courses - - - - - - - - - -
-
-

All files src/components/courses

-
- -
- 0% - Statements - 0/39 -
- - -
- 0% - Branches - 0/40 -
- - -
- 0% - Functions - 0/12 -
- - -
- 0% - Lines - 0/36 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
CourseCard.tsx -
-
0%0/50%0/190%0/20%0/5
CreateCourseModal.tsx -
-
0%0/290%0/140%0/90%0/26
ProcessingIndicator.tsx -
-
0%0/50%0/70%0/10%0/5
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/documents/DocumentProcessingPanel.tsx.html b/apps/web/coverage/lcov-report/src/components/documents/DocumentProcessingPanel.tsx.html deleted file mode 100644 index 74acb65..0000000 --- a/apps/web/coverage/lcov-report/src/components/documents/DocumentProcessingPanel.tsx.html +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - Code coverage report for src/components/documents/DocumentProcessingPanel.tsx - - - - - - - - - -
-
-

All files / src/components/documents DocumentProcessingPanel.tsx

-
- -
- 0% - Statements - 0/20 -
- - -
- 0% - Branches - 0/10 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/19 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useEffect, useState } from 'react';
-import { getDocumentDetailView } from '@/lib/api/documents';
-import type { DocumentDetailView } from '@/lib/api/types';
- 
-interface DocumentProcessingPanelProps {
-  documentId: string;
-  accessToken?: string;
-  onViewPreview?: (documentId: string) => void;
-}
- 
-function DetailRow({ label, value }: { label: string; value: React.ReactNode }) {
-  return (
-    <div className="flex items-start justify-between py-1.5">
-      <dt className="text-xs font-medium text-gray-500 w-36 flex-shrink-0">{label}</dt>
-      <dd className="text-xs text-gray-900 text-right">
-        {value ?? <span className="text-gray-400">N/A</span>}
-      </dd>
-    </div>
-  );
-}
- 
-export function DocumentProcessingPanel({
-  documentId,
-  accessToken,
-  onViewPreview,
-}: DocumentProcessingPanelProps) {
-  const [detail, setDetail] = useState<DocumentDetailView | null>(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
- 
-  const load = useCallback(async () => {
-    try {
-      setError(null);
-      const data = await getDocumentDetailView(documentId, accessToken);
-      setDetail(data);
-    } catch (err) {
-      setError(err instanceof Error ? err.message : 'Failed to load document details');
-    } finally {
-      setLoading(false);
-    }
-  }, [documentId, accessToken]);
- 
-  useEffect(() => {
-    load();
-  }, [load]);
- 
-  Iif (loading) {
-    return (
-      <div className="animate-pulse space-y-2 py-3 px-4">
-        <div className="h-3 w-2/3 rounded bg-gray-200" />
-        <div className="h-3 w-1/2 rounded bg-gray-200" />
-        <div className="h-3 w-3/4 rounded bg-gray-200" />
-      </div>
-    );
-  }
- 
-  Iif (error) {
-    return (
-      <div className="py-3 px-4">
-        <p className="text-xs text-red-600">{error}</p>
-        <button onClick={load} className="mt-1 text-xs text-red-700 hover:text-red-800 underline">
-          Retry
-        </button>
-      </div>
-    );
-  }
- 
-  Iif (!detail) return null;
- 
-  return (
-    <div className="py-3 px-4 bg-gray-50 rounded-md space-y-1">
-      <dl className="divide-y divide-gray-100">
-        <DetailRow label="Raw status" value={detail.status} />
-        <DetailRow label="Processing stage" value={detail.processingStage} />
-        <DetailRow label="Parser used" value={detail.parserUsed} />
-        <DetailRow label="Page / slide count" value={detail.pageCount} />
-        <DetailRow label="Chunk count" value={detail.chunkCount} />
-        <DetailRow label="Indexing status" value={detail.indexingStatus} />
-        {detail.errorMessage && (
-          <DetailRow
-            label="Processing errors"
-            value={<span className="text-red-600">{detail.errorMessage}</span>}
-          />
-        )}
-        {detail.normalizedMetadata && (
-          <div className="py-1.5">
-            <dt className="text-xs font-medium text-gray-500 mb-1">Normalized metadata</dt>
-            <dd className="text-[11px] font-mono bg-white rounded border border-gray-200 p-2 max-h-32 overflow-auto whitespace-pre-wrap">
-              {JSON.stringify(detail.normalizedMetadata, null, 2)}
-            </dd>
-          </div>
-        )}
-      </dl>
- 
-      {onViewPreview && detail.status === 'ready' && (
-        <button
-          onClick={() => onViewPreview(detail.id)}
-          className="mt-2 inline-flex items-center gap-1 text-xs font-medium text-orange-600 hover:text-orange-700"
-        >
-          View extracted content
-          <svg
-            className="h-3.5 w-3.5"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3"
-            />
-          </svg>
-        </button>
-      )}
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/documents/DocumentRow.tsx.html b/apps/web/coverage/lcov-report/src/components/documents/DocumentRow.tsx.html deleted file mode 100644 index c6cb871..0000000 --- a/apps/web/coverage/lcov-report/src/components/documents/DocumentRow.tsx.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - Code coverage report for src/components/documents/DocumentRow.tsx - - - - - - - - - -
-
-

All files / src/components/documents DocumentRow.tsx

-
- -
- 0% - Statements - 0/24 -
- - -
- 0% - Branches - 0/5 -
- - -
- 0% - Functions - 0/4 -
- - -
- 0% - Lines - 0/21 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useState } from 'react';
-import { useRouter, useParams } from 'next/navigation';
-import type { DocumentListRow, MaterialType } from '@/lib/api/types';
-import { deleteDocument } from '@/lib/api/documents';
-import { ProcessingIndicator } from '@/components/courses/ProcessingIndicator';
- 
-interface DocumentRowProps {
-  document: DocumentListRow;
-  accessToken?: string;
-  onDeleted?: () => void;
-}
- 
-function formatFileSize(bytes: number): string {
-  Iif (bytes < 1024) return `${bytes} B`;
-  Iif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
-  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
-}
- 
-const TYPE_BADGE: Record<MaterialType, { label: string; className: string }> = {
-  lecture: { label: 'Lecture', className: 'bg-blue-100 text-blue-700' },
-  past_exam: { label: 'Past Exam', className: 'bg-purple-100 text-purple-700' },
-  supplement: { label: 'Supplement', className: 'bg-gray-100 text-gray-600' },
-};
- 
-export function DocumentRow({ document: doc, accessToken, onDeleted }: DocumentRowProps) {
-  const [deleting, setDeleting] = useState(false);
-  const router = useRouter();
-  const params = useParams();
-  const courseId = params.courseId as string;
- 
-  const typeBadge = TYPE_BADGE[doc.documentType] ?? TYPE_BADGE.lecture;
- 
-  async function handleDelete() {
-    Iif (!confirm(`Delete "${doc.filename}"?`)) return;
-    setDeleting(true);
-    try {
-      await deleteDocument(doc.id, accessToken);
-      onDeleted?.();
-    } catch {
-      setDeleting(false);
-    }
-  }
- 
-  function handlePreview() {
-    router.push(`/courses/${courseId}/documents/${doc.id}/preview`);
-  }
- 
-  return (
-    <div className="flex items-center gap-3 py-3 px-1 group">
-      <div className="flex-shrink-0">
-        <svg
-          className="h-7 w-7 text-gray-400"
-          fill="none"
-          viewBox="0 0 24 24"
-          strokeWidth={1.5}
-          stroke="currentColor"
-        >
-          <path
-            strokeLinecap="round"
-            strokeLinejoin="round"
-            d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
-          />
-        </svg>
-      </div>
- 
-      <div className="flex-1 min-w-0">
-        <div className="flex items-center gap-2 flex-wrap">
-          <p className="text-sm font-medium text-gray-900 truncate">{doc.filename}</p>
-          <span
-            className={`inline-flex px-1.5 py-0.5 rounded text-[10px] font-semibold ${typeBadge.className}`}
-          >
-            {typeBadge.label}
-          </span>
-        </div>
-        <p className="text-xs text-gray-500">{formatFileSize(doc.size)}</p>
-      </div>
- 
-      <ProcessingIndicator status={doc.status} />
- 
-      {/* Preview link */}
-      <button
-        onClick={handlePreview}
-        className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600"
-        title="View preview"
-      >
-        <svg
-          className="h-4 w-4"
-          fill="none"
-          viewBox="0 0 24 24"
-          strokeWidth={1.5}
-          stroke="currentColor"
-        >
-          <path
-            strokeLinecap="round"
-            strokeLinejoin="round"
-            d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
-          />
-          <path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
-        </svg>
-      </button>
- 
-      {/* Delete */}
-      <button
-        onClick={handleDelete}
-        disabled={deleting}
-        className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-red-50 text-gray-400 hover:text-red-500 disabled:opacity-50"
-        title="Delete document"
-      >
-        <svg
-          className="h-4 w-4"
-          fill="none"
-          viewBox="0 0 24 24"
-          strokeWidth={1.5}
-          stroke="currentColor"
-        >
-          <path
-            strokeLinecap="round"
-            strokeLinejoin="round"
-            d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
-          />
-        </svg>
-      </button>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/documents/DocumentUpload.tsx.html b/apps/web/coverage/lcov-report/src/components/documents/DocumentUpload.tsx.html deleted file mode 100644 index a0a94fe..0000000 --- a/apps/web/coverage/lcov-report/src/components/documents/DocumentUpload.tsx.html +++ /dev/null @@ -1,1768 +0,0 @@ - - - - - - Code coverage report for src/components/documents/DocumentUpload.tsx - - - - - - - - - -
-
-

All files / src/components/documents DocumentUpload.tsx

-
- -
- 0% - Statements - 0/154 -
- - -
- 0% - Branches - 0/92 -
- - -
- 0% - Functions - 0/43 -
- - -
- 0% - Lines - 0/129 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -491 -492 -493 -494 -495 -496 -497 -498 -499 -500 -501 -502 -503 -504 -505 -506 -507 -508 -509 -510 -511 -512 -513 -514 -515 -516 -517 -518 -519 -520 -521 -522 -523 -524 -525 -526 -527 -528 -529 -530 -531 -532 -533 -534 -535 -536 -537 -538 -539 -540 -541 -542 -543 -544 -545 -546 -547 -548 -549 -550 -551 -552 -553 -554 -555 -556 -557 -558 -559 -560 -561 -562  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useRef, useState } from 'react';
-import {
-  uploadDocumentWithProgress,
-  fetchDocumentStatus,
-  retryDocument,
-} from '@/lib/api/documents';
-import { ApiError } from '@/lib/api';
-import type { MaterialType, ProcessingStage } from '@/lib/api/types';
-import { useToast } from '@/components/ui/Toast';
- 
-// ── Constants ──────────────────────────────────────────────────────────────
- 
-const MAX_CONCURRENT = 2;
-const ALLOWED_EXTS = ['.pdf', '.pptx'];
-const ALLOWED_MIME_TYPES = new Set([
-  'application/pdf',
-  'application/vnd.openxmlformats-officedocument.presentationml.presentation',
-]);
-const MAX_SIZE_BYTES = 50 * 1024 * 1024;
- 
-const MATERIAL_TYPE_LABELS: Record<MaterialType, string> = {
-  lecture: 'Lecture',
-  past_exam: 'Past Exam',
-  supplement: 'Supplement',
-};
- 
-const STAGE_LABELS: Partial<Record<ProcessingStage, string>> = {
-  parsing: 'Parsing',
-  normalizing: 'Normalizing',
-  chunking: 'Chunking',
-  indexing: 'Indexing',
-  done: 'Indexed',
-  error: 'Error',
-};
- 
-// ── Types ──────────────────────────────────────────────────────────────────
- 
-type UploadStatus = 'queued' | 'uploading' | 'processing' | 'indexed' | 'failed';
-type ErrorKind = 'validation' | 'upload' | 'processing' | 'timeout';
- 
-interface UploadJob {
-  id: string;
-  file: File;
-  documentType: MaterialType;
-  status: UploadStatus;
-  uploadPct: number;
-  processingStage?: ProcessingStage;
-  docId?: string;
-  errorKind?: ErrorKind;
-  errorMessage?: string;
-  retryCount: number;
-}
- 
-interface DocumentUploadProps {
-  courseId: string;
-  accessToken?: string;
-  onUploadComplete?: () => void;
-}
- 
-// ── Validation ─────────────────────────────────────────────────────────────
- 
-function isSupportedType(file: File): boolean {
-  const ext = '.' + (file.name.split('.').pop() ?? '').toLowerCase();
-  Iif (!ALLOWED_EXTS.includes(ext)) return false;
-  // If the browser reports a MIME type it must match; empty string means undetected (trust extension)
-  return !file.type || ALLOWED_MIME_TYPES.has(file.type);
-}
- 
-function validateFile(file: File): string | null {
-  Iif (file.size > MAX_SIZE_BYTES) return 'File too large (max 50 MB)';
-  return null;
-}
- 
-// ── Standalone async runners (no stale closures) ───────────────────────────
- 
-async function runUpload(
-  jobId: string,
-  file: File,
-  documentType: string,
-  courseId: string,
-  accessToken: string | undefined,
-  setJobs: React.Dispatch<React.SetStateAction<UploadJob[]>>,
-  onRelease: () => void,
-  onComplete?: () => void
-): Promise<void> {
-  const patch = (p: Partial<UploadJob>) =>
-    setJobs((prev) => prev.map((j) => (j.id === jobId ? { ...j, ...p } : j)));
- 
-  try {
-    const doc = await uploadDocumentWithProgress(courseId, file, accessToken, documentType, (pct) =>
-      patch({ uploadPct: pct })
-    );
-    patch({ docId: doc.id, status: 'processing', uploadPct: 100 });
- 
-    for (let i = 0; i < 120; i++) {
-      try {
-        const s = await fetchDocumentStatus(doc.id, accessToken);
-        patch({ processingStage: s.processing_stage });
-        Iif (s.status === 'ready') {
-          patch({ status: 'indexed' });
-          onComplete?.();
-          return;
-        }
-        Iif (s.status === 'error') {
-          patch({
-            status: 'failed',
-            errorKind: 'processing',
-            errorMessage: s.error_message ?? 'Processing failed',
-          });
-          onComplete?.();
-          return;
-        }
-      } catch (err) {
-        Iif (!(err instanceof ApiError && err.status >= 500)) throw err;
-      }
-      await new Promise((r) => setTimeout(r, 5000));
-    }
-    patch({ status: 'failed', errorKind: 'timeout', errorMessage: 'Processing timed out' });
-    onComplete?.();
-  } catch (err) {
-    patch({
-      status: 'failed',
-      errorKind: 'upload',
-      errorMessage: err instanceof Error ? err.message : 'Upload failed',
-    });
-  } finally {
-    onRelease();
-  }
-}
- 
-async function runRetry(
-  jobId: string,
-  docId: string,
-  accessToken: string | undefined,
-  setJobs: React.Dispatch<React.SetStateAction<UploadJob[]>>,
-  onComplete?: () => void
-): Promise<void> {
-  setJobs((prev) =>
-    prev.map((j) =>
-      j.id === jobId
-        ? {
-            ...j,
-            status: 'processing',
-            processingStage: undefined,
-            errorKind: undefined,
-            errorMessage: undefined,
-            retryCount: j.retryCount + 1,
-          }
-        : j
-    )
-  );
- 
-  const patch = (p: Partial<UploadJob>) =>
-    setJobs((prev) => prev.map((j) => (j.id === jobId ? { ...j, ...p } : j)));
- 
-  try {
-    await retryDocument(docId, accessToken);
- 
-    for (let i = 0; i < 120; i++) {
-      try {
-        const s = await fetchDocumentStatus(docId, accessToken);
-        patch({ processingStage: s.processing_stage });
-        Iif (s.status === 'ready') {
-          patch({ status: 'indexed' });
-          onComplete?.();
-          return;
-        }
-        Iif (s.status === 'error') {
-          patch({
-            status: 'failed',
-            errorKind: 'processing',
-            errorMessage: s.error_message ?? 'Processing failed',
-          });
-          onComplete?.();
-          return;
-        }
-      } catch (err) {
-        Iif (!(err instanceof ApiError && err.status >= 500)) throw err;
-      }
-      await new Promise((r) => setTimeout(r, 5000));
-    }
-    patch({ status: 'failed', errorKind: 'timeout', errorMessage: 'Processing timed out' });
-    onComplete?.();
-  } catch (err) {
-    patch({
-      status: 'failed',
-      errorKind: 'processing',
-      errorMessage: err instanceof Error ? err.message : 'Retry failed',
-    });
-    onComplete?.();
-  }
-}
- 
-// ── JobRow ─────────────────────────────────────────────────────────────────
- 
-function JobRow({
-  job,
-  onRetry,
-  onDismiss,
-}: {
-  job: UploadJob;
-  onRetry?: () => void;
-  onDismiss: () => void;
-}) {
-  const isTerminal = job.status === 'indexed' || job.status === 'failed';
-  const isFailed = job.status === 'failed';
-  const isActive = job.status === 'uploading' || job.status === 'processing';
- 
-  const stageLabel =
-    job.status === 'uploading'
-      ? `${job.uploadPct}%`
-      : job.status === 'processing'
-        ? job.processingStage
-          ? (STAGE_LABELS[job.processingStage] ?? job.processingStage)
-          : 'Processing'
-        : job.status === 'indexed'
-          ? 'Indexed'
-          : job.status === 'queued'
-            ? 'Queued'
-            : '';
- 
-  return (
-    <div
-      className={`flex flex-col gap-1.5 b-thin rounded-md px-3 py-2 text-sm ${
-        isFailed
-          ? 'border-red-500/40 bg-red-500/[0.03] dark:border-red-400/30 dark:bg-red-400/[0.04]'
-          : ''
-      }`}
-    >
-      <div className="flex items-center gap-2">
-        {/* Status icon */}
-        {job.status === 'indexed' && (
-          <svg
-            className="h-4 w-4 flex-shrink-0 text-green-600 dark:text-green-400"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-          >
-            <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
-          </svg>
-        )}
-        {isFailed && (
-          <svg
-            className="h-4 w-4 flex-shrink-0 text-red-500 dark:text-red-400"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={2}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
-            />
-          </svg>
-        )}
-        {isActive && (
-          <svg
-            className="h-4 w-4 flex-shrink-0 animate-spin opacity-50"
-            fill="none"
-            viewBox="0 0 24 24"
-          >
-            <circle
-              className="opacity-25"
-              cx="12"
-              cy="12"
-              r="10"
-              stroke="currentColor"
-              strokeWidth="4"
-            />
-            <path
-              className="opacity-75"
-              fill="currentColor"
-              d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
-            />
-          </svg>
-        )}
-        {job.status === 'queued' && (
-          <svg
-            className="h-4 w-4 flex-shrink-0 opacity-35"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={1.5}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
-            />
-          </svg>
-        )}
- 
-        {/* Filename */}
-        <span className="truncate flex-1 font-mono text-[11px]">{job.file.name}</span>
- 
-        {/* Stage label */}
-        <span
-          className={`font-mono text-[10px] flex-shrink-0 ${
-            isFailed
-              ? 'text-red-500 dark:text-red-400'
-              : job.status === 'indexed'
-                ? 'text-green-600 dark:text-green-400'
-                : 'opacity-50'
-          }`}
-        >
-          {stageLabel}
-        </span>
- 
-        {/* Retry */}
-        {isFailed && job.errorKind !== 'validation' && onRetry && (
-          <button
-            onClick={onRetry}
-            className="flex-shrink-0 font-mono text-[10px] underline text-red-500 dark:text-red-400 hover:opacity-70 transition-opacity"
-          >
-            Retry
-          </button>
-        )}
- 
-        {/* Dismiss */}
-        {isTerminal && (
-          <button
-            onClick={onDismiss}
-            aria-label="Dismiss"
-            className="flex-shrink-0 opacity-35 hover:opacity-70 transition-opacity"
-          >
-            <svg
-              className="h-3.5 w-3.5"
-              fill="none"
-              viewBox="0 0 24 24"
-              strokeWidth={2}
-              stroke="currentColor"
-            >
-              <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
-            </svg>
-          </button>
-        )}
-      </div>
- 
-      {/* Upload progress bar */}
-      {job.status === 'uploading' && (
-        <div className="h-1 w-full rounded-full bg-blue-dark/10 dark:bg-white/10 overflow-hidden">
-          <div
-            className="h-full rounded-full bg-blue-dark dark:bg-white transition-all duration-150"
-            style={{ width: `${job.uploadPct}%` }}
-          />
-        </div>
-      )}
- 
-      {/* Error message */}
-      {isFailed && job.errorMessage && (
-        <p className="font-mono text-[10px] text-red-500 dark:text-red-400 opacity-75 truncate pl-6">
-          {job.errorMessage}
-        </p>
-      )}
-    </div>
-  );
-}
- 
-// ── DocumentUpload ─────────────────────────────────────────────────────────
- 
-export function DocumentUpload({ courseId, accessToken, onUploadComplete }: DocumentUploadProps) {
-  const { showToast } = useToast();
-  const [jobs, setJobs] = useState<UploadJob[]>([]);
-  const [isDragOver, setIsDragOver] = useState(false);
-  const [selectedType, setSelectedType] = useState<MaterialType>('lecture');
-  const inputRef = useRef<HTMLInputElement>(null);
-  const activeRef = useRef(0);
-  const pendingRef = useRef<Array<() => void>>([]);
- 
-  const release = useCallback(() => {
-    activeRef.current--;
-    const next = pendingRef.current.shift();
-    Iif (next) {
-      activeRef.current++;
-      next();
-    }
-  }, []);
- 
-  const enqueue = useCallback((fn: () => void) => {
-    if (activeRef.current < MAX_CONCURRENT) {
-      activeRef.current++;
-      fn();
-    } else {
-      pendingRef.current.push(fn);
-    }
-  }, []);
- 
-  const handleFiles = useCallback(
-    (files: FileList | File[]) => {
-      const fileArray = Array.from(files);
-      Iif (fileArray.length === 0) return;
- 
-      // Type-gate: reject unsupported formats with a toast, never create a job for them
-      const accepted: File[] = [];
-      for (const file of fileArray) {
-        Iif (!isSupportedType(file)) {
-          showToast('Unsupported format. Use PDF or PPTX.', 'err');
-          continue;
-        }
-        accepted.push(file);
-      }
-      Iif (accepted.length === 0) return;
- 
-      const newJobs: UploadJob[] = accepted.map((file) => {
-        const validationError = validateFile(file);
-        return {
-          id: crypto.randomUUID(),
-          file,
-          documentType: selectedType,
-          status: (validationError ? 'failed' : 'queued') as UploadStatus,
-          uploadPct: 0,
-          errorKind: validationError ? ('validation' as ErrorKind) : undefined,
-          errorMessage: validationError ?? undefined,
-          retryCount: 0,
-        };
-      });
- 
-      setJobs((prev) => [...prev, ...newJobs]);
- 
-      for (const job of newJobs) {
-        Iif (job.status === 'failed') continue;
-        const { id, file, documentType } = job;
-        enqueue(() => {
-          setJobs((prev) => prev.map((j) => (j.id === id ? { ...j, status: 'uploading' } : j)));
-          void runUpload(
-            id,
-            file,
-            documentType,
-            courseId,
-            accessToken,
-            setJobs,
-            release,
-            onUploadComplete
-          );
-        });
-      }
-    },
-    [selectedType, courseId, accessToken, enqueue, release, onUploadComplete, showToast]
-  );
- 
-  const handleRetry = useCallback(
-    (jobId: string, docId: string) => {
-      void runRetry(jobId, docId, accessToken, setJobs, onUploadComplete);
-    },
-    [accessToken, onUploadComplete]
-  );
- 
-  const dismissJob = useCallback((jobId: string) => {
-    setJobs((prev) => prev.filter((j) => j.id !== jobId));
-  }, []);
- 
-  const clearDone = useCallback(() => {
-    setJobs((prev) => prev.filter((j) => j.status !== 'indexed' && j.status !== 'failed'));
-  }, []);
- 
-  const onDrop = useCallback(
-    (e: React.DragEvent) => {
-      e.preventDefault();
-      setIsDragOver(false);
-      Iif (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files);
-    },
-    [handleFiles]
-  );
- 
-  const doneCount = jobs.filter((j) => j.status === 'indexed' || j.status === 'failed').length;
- 
-  return (
-    <div>
-      {/* Document type selector */}
-      <div className="mb-3 flex gap-2">
-        {(Object.entries(MATERIAL_TYPE_LABELS) as [MaterialType, string][]).map(([type, label]) => (
-          <button
-            key={type}
-            onClick={() => setSelectedType(type)}
-            className={`px-2.5 py-1 rounded text-xs font-mono transition-colors b-thin ${
-              selectedType === type
-                ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                : 'hover:bg-blue-dark/5 dark:hover:bg-white/10 opacity-70'
-            }`}
-          >
-            {label}
-          </button>
-        ))}
-      </div>
- 
-      {/* Drop zone */}
-      <div
-        onDragOver={(e) => {
-          e.preventDefault();
-          setIsDragOver(true);
-        }}
-        onDragLeave={() => setIsDragOver(false)}
-        onDrop={onDrop}
-        onClick={() => inputRef.current?.click()}
-        className={`relative cursor-pointer rounded-lg border-2 border-dashed p-6 text-center transition-colors ${
-          isDragOver
-            ? 'border-blue-dark bg-blue-dark/5 dark:border-white dark:bg-white/5'
-            : 'border-blue-dark/30 hover:border-blue-dark/60 bg-blue-dark/[0.02] dark:border-white/20 dark:hover:border-white/40'
-        }`}
-      >
-        <input
-          ref={inputRef}
-          type="file"
-          multiple
-          accept=".pdf,.pptx"
-          className="sr-only"
-          onChange={(e) => {
-            Iif (e.target.files) handleFiles(e.target.files);
-            e.target.value = '';
-          }}
-        />
-        <svg
-          className="mx-auto h-8 w-8 opacity-40"
-          fill="none"
-          viewBox="0 0 24 24"
-          strokeWidth={1.5}
-          stroke="currentColor"
-        >
-          <path
-            strokeLinecap="round"
-            strokeLinejoin="round"
-            d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
-          />
-        </svg>
-        <p className="mt-2 text-sm">
-          <span className="font-medium ink">Click to upload</span>
-          <span className="opacity-60"> or drag and drop</span>
-        </p>
-        <p className="mt-1 font-mono text-[10px] opacity-50">
-          PDF · PPTX — max 50 MB — as {MATERIAL_TYPE_LABELS[selectedType]}
-        </p>
-      </div>
- 
-      {/* Job list */}
-      {jobs.length > 0 && (
-        <div className="mt-3 space-y-1.5">
-          {jobs.map((job) => (
-            <JobRow
-              key={job.id}
-              job={job}
-              onRetry={job.docId ? () => handleRetry(job.id, job.docId!) : undefined}
-              onDismiss={() => dismissJob(job.id)}
-            />
-          ))}
-          {doneCount > 1 && (
-            <button
-              onClick={clearDone}
-              className="w-full text-center font-mono text-[10px] opacity-40 hover:opacity-70 transition-opacity pt-0.5"
-            >
-              Clear done ({doneCount})
-            </button>
-          )}
-        </div>
-      )}
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/documents/index.html b/apps/web/coverage/lcov-report/src/components/documents/index.html deleted file mode 100644 index 6c811cf..0000000 --- a/apps/web/coverage/lcov-report/src/components/documents/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Code coverage report for src/components/documents - - - - - - - - - -
-
-

All files src/components/documents

-
- -
- 0% - Statements - 0/198 -
- - -
- 0% - Branches - 0/107 -
- - -
- 0% - Functions - 0/52 -
- - -
- 0% - Lines - 0/169 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
DocumentProcessingPanel.tsx -
-
0%0/200%0/100%0/50%0/19
DocumentRow.tsx -
-
0%0/240%0/50%0/40%0/21
DocumentUpload.tsx -
-
0%0/1540%0/920%0/430%0/129
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/providers/AuthProvider.tsx.html b/apps/web/coverage/lcov-report/src/components/providers/AuthProvider.tsx.html deleted file mode 100644 index da7e1d1..0000000 --- a/apps/web/coverage/lcov-report/src/components/providers/AuthProvider.tsx.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - Code coverage report for src/components/providers/AuthProvider.tsx - - - - - - - - - -
-
-

All files / src/components/providers AuthProvider.tsx

-
- -
- 0% - Statements - 0/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/3 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-/**
- * Session provider wrapper for NextAuth.js
- * Wraps the application with the SessionProvider to enable authentication
- */
-import { SessionProvider } from 'next-auth/react';
-import { ReactNode } from 'react';
- 
-interface AuthProviderProps {
-  children: ReactNode;
-}
- 
-/**
- * Authentication provider component
- * Wraps children with NextAuth SessionProvider
- */
-export function AuthProvider({ children }: AuthProviderProps) {
-  return <SessionProvider>{children}</SessionProvider>;
-}
- 
-export default AuthProvider;
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/providers/SessionErrorGuard.tsx.html b/apps/web/coverage/lcov-report/src/components/providers/SessionErrorGuard.tsx.html deleted file mode 100644 index 32b28ec..0000000 --- a/apps/web/coverage/lcov-report/src/components/providers/SessionErrorGuard.tsx.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - Code coverage report for src/components/providers/SessionErrorGuard.tsx - - - - - - - - - -
-
-

All files / src/components/providers SessionErrorGuard.tsx

-
- -
- 0% - Statements - 0/8 -
- - -
- 0% - Branches - 0/1 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/8 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useSession, signOut } from 'next-auth/react';
-import { useEffect } from 'react';
- 
-export function SessionErrorGuard() {
-  const { data: session } = useSession();
- 
-  useEffect(() => {
-    Iif (session?.error === 'RefreshAccessTokenError') {
-      signOut({ callbackUrl: '/login' });
-    }
-  }, [session?.error]);
- 
-  return null;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/providers/index.html b/apps/web/coverage/lcov-report/src/components/providers/index.html deleted file mode 100644 index 680132f..0000000 --- a/apps/web/coverage/lcov-report/src/components/providers/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Code coverage report for src/components/providers - - - - - - - - - -
-
-

All files src/components/providers

-
- -
- 0% - Statements - 0/12 -
- - -
- 0% - Branches - 0/1 -
- - -
- 0% - Functions - 0/3 -
- - -
- 0% - Lines - 0/11 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
AuthProvider.tsx -
-
0%0/4100%0/00%0/10%0/3
SessionErrorGuard.tsx -
-
0%0/80%0/10%0/20%0/8
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/CitationCard.tsx.html b/apps/web/coverage/lcov-report/src/components/study/CitationCard.tsx.html deleted file mode 100644 index c3e9d53..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/CitationCard.tsx.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - Code coverage report for src/components/study/CitationCard.tsx - - - - - - - - - -
-
-

All files / src/components/study CitationCard.tsx

-
- -
- 6.66% - Statements - 1/15 -
- - -
- 0% - Branches - 0/22 -
- - -
- 0% - Functions - 0/2 -
- - -
- 8.33% - Lines - 1/12 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61  -  -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useRouter } from 'next/navigation';
-import type { ApiCitationOut } from '@/lib/api/types';
- 
-interface CitationCardProps {
-  citation: ApiCitationOut;
-  courseId: string;
-  index: number;
-}
- 
-export function CitationCard({ citation, courseId, index }: CitationCardProps) {
-  const router = useRouter();
- 
-  const locationLabel = citation.page
-    ? `p.${citation.page}`
-    : citation.slide
-      ? `slide ${citation.slide}`
-      : null;
- 
-  const label = [citation.label || null, locationLabel].filter(Boolean).join(' · ') || 'Source';
-  const snippet =
-    citation.snippet.length > 180 ? citation.snippet.slice(0, 180) + '…' : citation.snippet;
- 
-  function handleClick() {
-    Iif (!citation.doc_id) return;
-    const params = new URLSearchParams();
-    if (citation.page) params.set('page', String(citation.page));
-    else Iif (citation.slide) params.set('slide', String(citation.slide));
-    const query = params.toString();
-    router.push(
-      `/courses/${courseId}/documents/${citation.doc_id}/preview${query ? `?${query}` : ''}`
-    );
-  }
- 
-  return (
-    <div
-      onClick={citation.doc_id ? handleClick : undefined}
-      className={`b-thin rounded-md px-3 py-2.5 ${citation.doc_id ? 'cursor-pointer hover:bg-blue-dark/5 transition-colors' : ''}`}
-    >
-      <div className="flex items-center justify-between gap-2 mb-1.5">
-        <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70 truncate">
-          [{index}] {label}
-        </span>
-        <span className="font-mono text-[10px] opacity-60 flex-shrink-0">
-          {Math.round(citation.score * 100)}%
-        </span>
-      </div>
-      {citation.section && (
-        <p className="font-mono text-[10px] opacity-50 mb-1 truncate">{citation.section}</p>
-      )}
-      <p className="text-[12.5px] leading-snug opacity-90">&ldquo;{snippet}&rdquo;</p>
-      {citation.doc_id && (
-        <p className="mt-1.5 font-mono text-[10px] text-blue-dark dark:text-white/60 opacity-70">
-          View in source →
-        </p>
-      )}
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html b/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html deleted file mode 100644 index 3cf4107..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/ContentChunks.tsx.html +++ /dev/null @@ -1,589 +0,0 @@ - - - - - - Code coverage report for src/components/study/ContentChunks.tsx - - - - - - - - - -
-
-

All files / src/components/study ContentChunks.tsx

-
- -
- 9.09% - Statements - 3/33 -
- - -
- 0% - Branches - 0/22 -
- - -
- 0% - Functions - 0/13 -
- - -
- 9.37% - Lines - 3/32 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169  -  -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useEffect, useState } from 'react';
-import { getDocuments } from '@/lib/services/documents';
-import { getDocumentPreviewView } from '@/lib/api/documents';
- 
-// Typed shapes for document preview data returned by the backend
-interface Section {
-  title?: string;
-  level?: number;
-  page?: number;
-}
- 
-interface Chunk {
-  text: string;
-  section?: string;
-  page?: number;
-}
- 
-interface DocumentContent {
-  documentId: string;
-  filename: string;
-  sections: Section[];
-  chunks: Chunk[];
-}
- 
-interface ContentChunksProps {
-  courseId: string;
-  accessToken?: string;
-  className?: string;
-  activeCitationDocIds?: Set<string>;
-}
- 
-export function ContentChunks({
-  courseId,
-  accessToken,
-  className,
-  activeCitationDocIds,
-}: ContentChunksProps) {
-  const [contents, setContents] = useState<DocumentContent[]>([]);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState<string | null>(null);
- 
-  useEffect(() => {
-    async function fetchContent() {
-      try {
-        const docs = await getDocuments(courseId, accessToken);
-        const readyDocs = docs.filter((d) => d.status === 'ready');
- 
-        const previews = await Promise.allSettled(
-          readyDocs.map(async (doc) => {
-            const preview = await getDocumentPreviewView(doc.id, accessToken);
-            return {
-              documentId: doc.id,
-              filename: doc.filename,
-              sections: (preview.sections ?? []).map((title) => ({ title })),
-              chunks: (preview.sampleChunks ?? []).map((c) => ({
-                text: c.text,
-                section: c.section ?? undefined,
-              })),
-            };
-          })
-        );
- 
-        const loaded = previews
-          .flatMap((r) => (r.status === 'fulfilled' ? [r.value] : []))
-          .filter((d) => d.chunks.length > 0 || d.sections.length > 0);
- 
-        setContents(loaded);
-      } catch (err) {
-        setError(err instanceof Error ? err.message : 'Failed to load course material');
-      } finally {
-        setLoading(false);
-      }
-    }
- 
-    fetchContent();
-  }, [courseId, accessToken]);
- 
-  Iif (loading) {
-    return (
-      <div className={className} aria-label="Loading course material">
-        <div className="space-y-3">
-          {[1, 2, 3].map((i) => (
-            <div key={i} className="animate-pulse">
-              <div className="h-3 w-1/3 bg-gray-200 rounded mb-2" />
-              <div className="h-3 w-full bg-gray-100 rounded mb-1" />
-              <div className="h-3 w-4/5 bg-gray-100 rounded" />
-            </div>
-          ))}
-        </div>
-      </div>
-    );
-  }
- 
-  Iif (error) {
-    return (
-      <div className={className}>
-        <p className="text-xs text-red-500">{error}</p>
-      </div>
-    );
-  }
- 
-  Iif (contents.length === 0) {
-    return null;
-  }
- 
-  return (
-    <div className={className}>
-      <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-3">
-        Course Material
-      </div>
-      <div className="space-y-5">
-        {contents.map((doc) => {
-          const isCited = activeCitationDocIds?.has(doc.documentId);
-          return (
-            <div
-              key={doc.documentId}
-              className={`rounded-md transition-colors ${isCited ? 'b-hard bg-blue-dark/5 dark:bg-blue-dark/20 p-2' : ''}`}
-            >
-              <div className="flex items-center gap-2 mb-2">
-                {isCited && (
-                  <span className="inline-block w-1.5 h-1.5 rounded-full bg-blue-dark dark:bg-white flex-shrink-0" />
-                )}
-                <p
-                  className="font-mono text-[10px] tracking-wide truncate opacity-80"
-                  title={doc.filename}
-                >
-                  {doc.filename}
-                </p>
-              </div>
- 
-              {/* Section chips */}
-              {doc.sections.length > 0 && (
-                <div className="flex flex-wrap gap-1.5 mb-2">
-                  {doc.sections.map((section, i) => (
-                    <span key={i} className="chip" style={{ border: '1px solid currentColor' }}>
-                      {section.title ?? `Section ${i + 1}`}
-                    </span>
-                  ))}
-                </div>
-              )}
- 
-              {/* Sample chunks */}
-              <div className="space-y-1.5">
-                {doc.chunks.map((chunk, i) => (
-                  <div key={i} className="b-thin rounded-md p-2.5 text-[12px] leading-relaxed">
-                    <div className="flex items-start gap-2">
-                      <span className="flex-shrink-0 font-mono text-[10px] opacity-50 mt-0.5">
-                        {i + 1}
-                      </span>
-                      <p className="flex-1 opacity-90">{chunk.text}</p>
-                    </div>
-                    {chunk.section && (
-                      <p className="mt-1 font-mono text-[10px] opacity-50 pl-5">
-                        § {chunk.section}
-                      </p>
-                    )}
-                  </div>
-                ))}
-              </div>
-            </div>
-          );
-        })}
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html b/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html deleted file mode 100644 index aee94fe..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/LessonNav.tsx.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - Code coverage report for src/components/study/LessonNav.tsx - - - - - - - - - -
-
-

All files / src/components/study LessonNav.tsx

-
- -
- 100% - Statements - 10/10 -
- - -
- 93.33% - Branches - 14/15 -
- - -
- 100% - Functions - 4/4 -
- - -
- 100% - Lines - 10/10 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104  -  -  -  -  -  -  -  -  -  -  -  -  -9x -  -  -  -  -  -  -  -9x -  -  -  -3x -  -  -  -  -  -8x -  -  -  -  -  -  -  -  -  -  -  -21x -21x -21x -21x -  -21x -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import type { Lesson } from '@/lib/services/courses';
- 
-interface LessonNavProps {
-  lessons: Lesson[];
-  selectedLesson: Lesson | null;
-  completedLessons: Set<string>;
-  onSelect: (lesson: Lesson) => void;
-  loading?: boolean;
-  studiedLessonId?: string | null;
-}
- 
-export function LessonNav({
-  lessons,
-  selectedLesson,
-  completedLessons,
-  onSelect,
-  loading = false,
-  studiedLessonId,
-}: LessonNavProps) {
-  if (loading) {
-    return (
-      <div className="space-y-1 p-3">
-        {[1, 2, 3].map((i) => (
-          <div key={i} className="h-9 bg-blue-dark/5 rounded animate-pulse" />
-        ))}
-      </div>
-    );
-  }
- 
-  if (lessons.length === 0) {
-    return (
-      <div className="px-4 py-5 text-center font-mono text-[11px] opacity-50">
-        No lessons available yet.
-      </div>
-    );
-  }
- 
-  return (
-    <nav aria-label="Course lessons">
-      <ul>
-        {lessons.map((lesson, index) => {
-          const lessonId = String(lesson.id);
-          const isSelected = selectedLesson?.id === lesson.id;
-          const isCompleted = completedLessons.has(lessonId);
-          const isStudied = studiedLessonId === lessonId && !isSelected;
- 
-          return (
-            <li key={lesson.id}>
-              <button
-                onClick={() => onSelect(lesson)}
-                className={`w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors text-[13px] ${
-                  isSelected
-                    ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                    : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
-                }`}
-                aria-current={isSelected ? 'true' : undefined}
-              >
-                <span
-                  className={`flex-shrink-0 flex items-center justify-center h-5 w-5 rounded-sm font-mono text-[10px] font-semibold b-thin ${
-                    isCompleted ? 'opacity-60' : ''
-                  }`}
-                  aria-hidden="true"
-                >
-                  {isCompleted ? (
-                    <svg
-                      className="h-3 w-3"
-                      fill="none"
-                      viewBox="0 0 24 24"
-                      strokeWidth={2.5}
-                      stroke="currentColor"
-                    >
-                      <path
-                        strokeLinecap="round"
-                        strokeLinejoin="round"
-                        d="M4.5 12.75l6 6 9-13.5"
-                      />
-                    </svg>
-                  ) : (
-                    index + 1
-                  )}
-                </span>
-                <span className="flex-1 min-w-0 font-medium truncate">{lesson.title}</span>
-                {isStudied && (
-                  <span
-                    className="flex-shrink-0 inline-block w-1.5 h-1.5 rounded-full bg-blue-dark dark:bg-white opacity-60"
-                    title="Last studied"
-                  />
-                )}
-                {isCompleted && (
-                  <span className="flex-shrink-0 font-mono text-[9px] tracking-[0.18em] uppercase opacity-60">
-                    done
-                  </span>
-                )}
-              </button>
-            </li>
-          );
-        })}
-      </ul>
-    </nav>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html b/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html deleted file mode 100644 index 6a00bc7..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/OutputPane.tsx.html +++ /dev/null @@ -1,1807 +0,0 @@ - - - - - - Code coverage report for src/components/study/OutputPane.tsx - - - - - - - - - -
-
-

All files / src/components/study OutputPane.tsx

-
- -
- 24.48% - Statements - 24/98 -
- - -
- 23.59% - Branches - 21/89 -
- - -
- 8.33% - Functions - 3/36 -
- - -
- 28.23% - Lines - 24/85 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -491 -492 -493 -494 -495 -496 -497 -498 -499 -500 -501 -502 -503 -504 -505 -506 -507 -508 -509 -510 -511 -512 -513 -514 -515 -516 -517 -518 -519 -520 -521 -522 -523 -524 -525 -526 -527 -528 -529 -530 -531 -532 -533 -534 -535 -536 -537 -538 -539 -540 -541 -542 -543 -544 -545 -546 -547 -548 -549 -550 -551 -552 -553 -554 -555 -556 -557 -558 -559 -560 -561 -562 -563 -564 -565 -566 -567 -568 -569 -570 -571 -572 -573 -574 -575  -  -2x -2x -2x -2x -  -  -2x -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -2x -  -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -18x -18x -18x -18x -18x -18x -18x -18x -  -18x -18x -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -18x -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import Link from 'next/link';
-import { useEffect, useRef, useState, type KeyboardEvent } from 'react';
-import { sendChatMessage, type Citation } from '@/lib/services/chat';
-import { sendStudyAction } from '@/lib/services/study';
-import type { ApiCitationOut, ApiStudyResponse, StudyAction } from '@/lib/api/types';
-import type { Lesson } from '@/lib/services/courses';
-import { StudyActionBar } from './StudyActionBar';
-import { StudyOutput } from './StudyOutput';
- 
-// ── Types ─────────────────────────────────────────────────────────────────────
- 
-interface ChatMessage {
-  role: 'user' | 'assistant';
-  content: string;
-  citations?: Citation[];
-}
- 
-interface ActionMessage {
-  role: 'action-result';
-  action: StudyAction;
-  query: string;
-  result: ApiStudyResponse;
-  durationMs?: number;
-}
- 
-type Message = ChatMessage | ActionMessage;
- 
-interface OutputPaneProps {
-  courseId: string;
-  accessToken?: string;
-  selectedLesson?: Lesson | null;
-  hasIndexedDocs?: boolean;
-  initialQuery?: string;
-  initialAction?: StudyAction | null;
-  onActionResult?: (result: ApiStudyResponse, lesson: Lesson | null) => void;
-}
- 
-// ── Evidence Drawer ───────────────────────────────────────────────────────────
- 
-function ScoreBar({ score, rerank }: { score: number; rerank?: number }) {
-  const r = rerank ?? score;
-  return (
-    <div className="flex items-center gap-2">
-      <div className="flex-1 h-3 b-thin relative overflow-hidden">
-        <div
-          className="absolute inset-y-0 left-0 h-full opacity-30"
-          style={{ width: `${score * 100}%`, background: '#001CE0' }}
-        />
-        <div
-          className="absolute inset-y-0 left-0 h-full"
-          style={{ width: `${r * 100}%`, background: '#001CE0' }}
-        />
-      </div>
-      <span className="font-mono text-[10px] opacity-70 w-10 text-right tabular-nums">
-        {r.toFixed(3)}
-      </span>
-    </div>
-  );
-}
- 
-function EvidenceDrawer({ citations }: { citations: ApiCitationOut[] }) {
-  // Group by doc_id for "By source" section
-  const byDoc: Record<string, { label: string; count: number }> = {};
-  citations.forEach((c) => {
-    const key = c.doc_id || 'unknown';
-    Iif (!byDoc[key]) byDoc[key] = { label: c.label || c.doc_id || 'Unknown', count: 0 };
-    byDoc[key].count++;
-  });
-  const total = citations.length || 1;
-  const sources = Object.entries(byDoc).map(([, v]) => ({
-    label: v.label,
-    pct: Math.round((v.count / total) * 100),
-  }));
- 
-  return (
-    <div className="b-thin rounded-lg bg-white dark:bg-blue-dark/20 p-4">
-      <div className="grid grid-cols-12 gap-3">
-        {/* Passage cards */}
-        <div className="col-span-12 lg:col-span-7 space-y-2">
-          {citations.map((ev, i) => (
-            <div key={i} className="b-thin rounded-md p-3">
-              <div className="flex items-center gap-3 mb-1.5">
-                <span className="font-mono text-[10px] opacity-70 w-5">[{i + 1}]</span>
-                <span className="text-[12.5px] font-medium truncate flex-1">
-                  {ev.label || ev.doc_id || 'Source'}
-                </span>
-                {ev.section && (
-                  <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
-                    {ev.section}
-                  </span>
-                )}
-              </div>
-              <div className="font-mono text-[10px] opacity-60 mb-2">
-                {ev.page > 0 && `p.${ev.page}`}
-                {ev.slide > 0 && ` · slide ${ev.slide}`}
-                {` · score ${ev.score.toFixed(3)}`}
-              </div>
-              <p className="text-[12.5px] leading-snug opacity-90">{ev.snippet}</p>
-              <ScoreBar score={ev.score} />
-            </div>
-          ))}
-        </div>
- 
-        {/* Charts */}
-        <div className="col-span-12 lg:col-span-5 space-y-3">
-          {/* Score bars legend */}
-          <div className="b-thin rounded-md p-3">
-            <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
-              Score
-            </div>
-            <div className="space-y-1.5">
-              {citations.map((ev, i) => (
-                <div key={i} className="flex items-center gap-2">
-                  <span className="font-mono text-[10px] opacity-70 w-5">[{i + 1}]</span>
-                  <ScoreBar score={ev.score} />
-                </div>
-              ))}
-            </div>
-            <div className="flex items-center gap-3 pt-2 b-thin-t mt-2 font-mono text-[10px] opacity-70">
-              <span>
-                <span
-                  className="inline-block w-2.5 h-2.5 align-middle mr-1"
-                  style={{ background: '#001CE0' }}
-                />
-                retrieval score
-              </span>
-            </div>
-          </div>
- 
-          {/* By source */}
-          <div className="b-thin rounded-md p-3">
-            <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
-              By source
-            </div>
-            <ul className="space-y-1.5">
-              {sources.map((s, i) => (
-                <li key={i}>
-                  <div className="flex items-center justify-between font-mono text-[10px] mb-0.5">
-                    <span className="truncate opacity-90">{s.label}</span>
-                    <span className="opacity-70 tabular-nums ml-2">{s.pct}%</span>
-                  </div>
-                  <div className="h-1.5 b-thin overflow-hidden">
-                    <div className="h-full" style={{ width: `${s.pct}%`, background: '#001CE0' }} />
-                  </div>
-                </li>
-              ))}
-            </ul>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
- 
-// ── Inspect Drawer ────────────────────────────────────────────────────────────
- 
-function InspectDrawer({ msg }: { msg: ActionMessage }) {
-  const { action, query, result, durationMs } = msg;
-  const lines = {
-    'retrieval.trace': [
-      `action: ${action}`,
-      `query_length: ${query.length} chars`,
-      `retrieval_used: ${result.retrieval_used}`,
-      `chunks_found: ${result.citations.length}`,
-      `generation: ${result.citations.length > 0 ? 'ran' : 'fallback'}`,
-      `output_length: ${result.answer.length} chars`,
-      durationMs != null ? `duration: ${durationMs}ms` : '(duration not tracked)',
-    ],
-    'evidence.json': [
-      '{',
-      `  "action": "${action}",`,
-      `  "k": ${result.citations.length},`,
-      `  "sources": [${[...new Set(result.citations.map((c) => c.doc_id || 'unknown'))].map((d) => `"${d}"`).join(', ')}],`,
-      `  "avg_score": ${result.citations.length ? (result.citations.reduce((s, c) => s + c.score, 0) / result.citations.length).toFixed(3) : 0}`,
-      '}',
-    ],
-    'output.meta': [
-      `model: qvac-rag`,
-      `answer_length: ${result.answer.length} chars`,
-      `citations: ${result.citations.length}`,
-      `retrieval_used: ${result.retrieval_used}`,
-    ],
-  };
- 
-  return (
-    <div className="b-thin rounded-lg bg-white dark:bg-blue-dark/20 p-4 mt-2">
-      <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-3">
-        Inspect · debug · MVP-only
-      </div>
-      <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
-        {Object.entries(lines).map(([title, content]) => (
-          <div key={title} className="b-thin rounded-md overflow-hidden">
-            <div className="px-3 py-2 b-thin-b font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-              {title}
-            </div>
-            <pre className="font-mono text-[11px] leading-relaxed p-3 whitespace-pre-wrap m-0">
-              {content.join('\n')}
-            </pre>
-          </div>
-        ))}
-      </div>
-    </div>
-  );
-}
- 
-// ── Suggested next actions ────────────────────────────────────────────────────
- 
-const NEXT_ACTIONS: Array<{ action: StudyAction; glyph: string; label: string }> = [
-  { action: 'derive', glyph: '∂', label: 'Derive / prove' },
-  { action: 'quiz', glyph: '▢', label: 'Quiz me' },
-  { action: 'oral', glyph: '◉', label: 'Oral follow-ups' },
-];
- 
-// ── Main component ────────────────────────────────────────────────────────────
- 
-export function OutputPane({
-  courseId,
-  accessToken,
-  selectedLesson,
-  hasIndexedDocs = true,
-  initialQuery = '',
-  initialAction = null,
-  onActionResult,
-}: OutputPaneProps) {
-  const [messages, setMessages] = useState<Message[]>([]);
-  const [input, setInput] = useState(initialQuery);
-  const [loading, setLoading] = useState(false);
-  const [activeAction, setActiveAction] = useState<StudyAction | null>(null);
-  const [showEvidence, setShowEvidence] = useState(false);
-  const [showInspect, setShowInspect] = useState(false);
-  const bottomRef = useRef<HTMLDivElement>(null);
-  const didAutoFireRef = useRef(false);
- 
-  useEffect(() => {
-    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
-  }, [messages, loading]);
- 
-  // Last action result for drawers
-  const lastActionResult = [...messages]
-    .reverse()
-    .find((m): m is ActionMessage => m.role === 'action-result');
- 
-  async function handleSend() {
-    const question = input.trim();
-    Iif (!question || loading) return;
-    setInput('');
-    setMessages((prev) => [...prev, { role: 'user', content: question }]);
-    setLoading(true);
-    setActiveAction(null);
-    try {
-      const result = await sendChatMessage(courseId, question, accessToken);
-      setMessages((prev) => [
-        ...prev,
-        { role: 'assistant', content: result.answer, citations: result.citations },
-      ]);
-    } catch (err) {
-      setMessages((prev) => [
-        ...prev,
-        {
-          role: 'assistant',
-          content: err instanceof Error ? `Error: ${err.message}` : 'Could not fetch a response.',
-        },
-      ]);
-    } finally {
-      setLoading(false);
-    }
-  }
- 
-  async function handleAction(action: StudyAction, queryOverride?: string) {
-    const query = queryOverride || input.trim() || selectedLesson?.title || 'this course material';
-    setLoading(true);
-    setActiveAction(action);
-    setMessages((prev) => [...prev, { role: 'user', content: `[${action}] ${query}` }]);
-    const t0 = Date.now();
-    try {
-      const result = await sendStudyAction(courseId, action, query, accessToken);
-      const durationMs = Date.now() - t0;
-      setMessages((prev) => [
-        ...prev,
-        { role: 'action-result', action, query, result, durationMs },
-      ]);
-      Iif (result.citations.length > 0) setShowEvidence(true);
-      onActionResult?.(result, selectedLesson ?? null);
-    } catch (err) {
-      setMessages((prev) => [
-        ...prev,
-        {
-          role: 'assistant',
-          content: err instanceof Error ? `Error: ${err.message}` : 'Study action failed.',
-        },
-      ]);
-    } finally {
-      setLoading(false);
-      setActiveAction(null);
-    }
-  }
- 
-  // Auto-fire when arriving from preview quick actions (?q=...&action=...)
-  useEffect(() => {
-    if (
-      didAutoFireRef.current ||
-      !initialQuery ||
-      !initialAction ||
-      !hasIndexedDocs ||
-      !accessToken
-    )
-      return;
-    didAutoFireRef.current = true;
-    handleAction(initialAction, initialQuery);
-    // handleAction is recreated each render — intentionally not listed to avoid loops.
-    // This effect re-evaluates only when auth/docs status changes.
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [hasIndexedDocs, accessToken]);
- 
-  function handleKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {
-    Iif (e.key === 'Enter' && !e.shiftKey) {
-      e.preventDefault();
-      handleSend();
-    }
-  }
- 
-  const placeholder = selectedLesson
-    ? `Ask about "${selectedLesson.title}" or pick an action above…`
-    : 'Ask a question, or type a topic and pick a study action above…';
- 
-  const evidenceCitations = lastActionResult?.result?.citations ?? [];
- 
-  return (
-    <div className="h-full flex flex-col bg-white dark:bg-blue-dark/30">
-      {/* Header */}
-      <div className="flex-shrink-0 px-5 py-3 b-thin-b flex items-center gap-3">
-        <span className="mono text-[10px] tracking-[0.22em] uppercase opacity-70">AI Tutor</span>
-        {selectedLesson && (
-          <span className="font-medium text-sm truncate">{selectedLesson.title}</span>
-        )}
-        {lastActionResult && (
-          <div className="ml-auto flex items-center gap-1">
-            <button
-              onClick={() => {
-                setShowEvidence((v) => !v);
-                setShowInspect(false);
-              }}
-              className={`font-mono text-[10px] tracking-[0.18em] uppercase px-2.5 h-7 rounded-md transition-all ${
-                showEvidence
-                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                  : 'b-thin hover:bg-blue-dark/5'
-              }`}
-            >
-              {showEvidence ? '▾' : '▸'} Evidence · {evidenceCitations.length}
-            </button>
-            <button
-              onClick={() => {
-                setShowInspect((v) => !v);
-                setShowEvidence(false);
-              }}
-              className={`font-mono text-[10px] tracking-[0.18em] uppercase px-2.5 h-7 rounded-md transition-all ${
-                showInspect
-                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                  : 'b-thin hover:bg-blue-dark/5'
-              }`}
-            >
-              {showInspect ? '▾' : '▸'} Inspect
-            </button>
-          </div>
-        )}
-      </div>
- 
-      {/* Action bar */}
-      <StudyActionBar
-        onAction={handleAction}
-        activeAction={activeAction}
-        loading={loading}
-        hasIndexedDocs={hasIndexedDocs}
-      />
- 
-      {/* Message thread */}
-      <div className="flex-1 overflow-y-auto p-4 space-y-4 ws-scroll">
-        {/* Empty states */}
-        {messages.length === 0 && !hasIndexedDocs && (
-          <div className="flex items-center justify-center h-full">
-            <div className="text-center max-w-xs">
-              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
-              <p className="font-medium mb-1">No documents indexed yet</p>
-              <p className="font-mono text-[11px] opacity-60 leading-relaxed mb-4">
-                Upload course material in the Workspace to enable study actions.
-              </p>
-              <Link href={`/courses/${courseId}`} className="btn-ghost text-sm inline-flex">
-                Go to Workspace →
-              </Link>
-            </div>
-          </div>
-        )}
-        {messages.length === 0 && hasIndexedDocs && (
-          <div className="flex items-center justify-center h-full">
-            <div className="text-center max-w-xs">
-              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
-              <p className="font-mono text-[11px] opacity-60 leading-relaxed">
-                Type a topic in the input below, then click a study action — or just ask a question.
-              </p>
-            </div>
-          </div>
-        )}
- 
-        {messages.map((msg, i) => {
-          const isLast = i === messages.length - 1;
- 
-          Iif (msg.role === 'action-result') {
-            return (
-              <div key={i} className="space-y-2">
-                <div className="inline-flex items-center gap-1.5">
-                  <span className="chip" style={{ border: '1px solid currentColor' }}>
-                    {msg.action.replace('_', ' ')}
-                  </span>
-                  <span className="font-mono text-[11px] opacity-60 truncate max-w-48">
-                    {msg.query}
-                  </span>
-                </div>
-                <div className="b-thin rounded-lg p-4">
-                  <StudyOutput
-                    result={msg.result}
-                    courseId={courseId}
-                    onOralFollowUp={(query) => handleAction('oral', query)}
-                  />
-                </div>
- 
-                {/* Suggested next actions — only on last result */}
-                {isLast && (
-                  <div className="pt-1">
-                    <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70 mb-2">
-                      Suggested next actions
-                    </div>
-                    <div className="flex flex-wrap gap-2">
-                      {NEXT_ACTIONS.filter((n) => n.action !== msg.action).map((n) => (
-                        <button
-                          key={n.action}
-                          onClick={() => handleAction(n.action, msg.query)}
-                          disabled={loading}
-                          className="btn-ghost text-sm disabled:opacity-40"
-                        >
-                          {n.glyph} {n.label}
-                        </button>
-                      ))}
-                    </div>
-                  </div>
-                )}
-              </div>
-            );
-          }
- 
-          return (
-            <div
-              key={i}
-              className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
-            >
-              <div
-                className={`max-w-[85%] rounded-lg px-4 py-3 ${
-                  msg.role === 'user'
-                    ? 'bg-blue-dark text-white'
-                    : 'b-thin bg-white dark:bg-blue-dark/40'
-                }`}
-              >
-                <p className="text-sm whitespace-pre-wrap leading-relaxed">{msg.content}</p>
-                {msg.role === 'assistant' && msg.citations && msg.citations.length > 0 && (
-                  <div className="mt-3 space-y-2 b-thin-t pt-2">
-                    <p className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-70">
-                      Sources
-                    </p>
-                    {msg.citations.map((citation, ci) => (
-                      <div
-                        key={ci}
-                        className="b-thin rounded-md px-3 py-2 bg-white dark:bg-blue-dark/20"
-                      >
-                        <p className="text-xs line-clamp-3 leading-relaxed opacity-90">
-                          &ldquo;{citation.snippet}&rdquo;
-                        </p>
-                        <p className="mt-1 font-mono text-[10px] opacity-60">
-                          score · {Math.round(citation.score * 100)}%
-                        </p>
-                      </div>
-                    ))}
-                  </div>
-                )}
-              </div>
-            </div>
-          );
-        })}
- 
-        {loading && (
-          <div className="flex justify-start w-full" aria-live="polite">
-            <div className="b-thin rounded-lg px-4 py-3 w-full max-w-[85%] space-y-2">
-              <p className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-60 mb-1">
-                Retrieving · generating…
-              </p>
-              <div
-                className="h-1.5 rounded bar-stripes"
-                style={{ background: '#001CE0', opacity: 0.6 }}
-              />
-              <div className="h-2 rounded bg-blue-dark/10 animate-pulse w-4/5 mt-1" />
-              <div className="h-2 rounded bg-blue-dark/10 animate-pulse w-3/5" />
-            </div>
-          </div>
-        )}
- 
-        <div ref={bottomRef} />
-      </div>
- 
-      {/* Evidence / Inspect drawers */}
-      {showEvidence && evidenceCitations.length > 0 && (
-        <div className="flex-shrink-0 b-thin-t p-4 max-h-80 overflow-y-auto ws-scroll">
-          <EvidenceDrawer citations={evidenceCitations} />
-        </div>
-      )}
-      {showInspect && lastActionResult && (
-        <div className="flex-shrink-0 b-thin-t p-4 max-h-64 overflow-y-auto ws-scroll">
-          <InspectDrawer msg={lastActionResult} />
-        </div>
-      )}
- 
-      {/* Input area */}
-      <div className="flex-shrink-0 b-thin-t p-4">
-        {/* Scope chips */}
-        <div className="flex items-center gap-2 mb-2">
-          <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
-            ⌖ scope · all course docs
-          </span>
-          <span className="chip text-[10px]" style={{ border: '1px solid currentColor' }}>
-            k=5 · QVAC
-          </span>
-          {lastActionResult && (
-            <span className="ml-auto font-mono text-[10px] opacity-50">
-              {lastActionResult.result.citations.length} sources ·{' '}
-              {lastActionResult.durationMs != null ? `${lastActionResult.durationMs}ms` : 'done'}
-            </span>
-          )}
-        </div>
-        <div className="flex gap-3">
-          <textarea
-            value={input}
-            onChange={(e) => setInput(e.target.value)}
-            onKeyDown={handleKeyDown}
-            placeholder={placeholder}
-            rows={2}
-            disabled={loading}
-            className="flex-1 resize-none rounded-md b-thin px-3 py-2 text-sm placeholder-blue-dark/40 dark:placeholder-white/40 bg-transparent outline-none focus:ring-1 focus:ring-blue-dark dark:focus:ring-white disabled:opacity-50"
-          />
-          <button
-            onClick={handleSend}
-            disabled={!input.trim() || loading}
-            className="flex-shrink-0 inline-flex items-center justify-center h-10 w-10 self-end btn-primary rounded-lg disabled:opacity-40 disabled:cursor-not-allowed"
-          >
-            <svg
-              className="h-5 w-5"
-              fill="none"
-              viewBox="0 0 24 24"
-              strokeWidth={2}
-              stroke="currentColor"
-            >
-              <path
-                strokeLinecap="round"
-                strokeLinejoin="round"
-                d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"
-              />
-            </svg>
-          </button>
-        </div>
-        <p className="mt-1.5 font-mono text-[10px] opacity-50">
-          Enter to send · Shift+Enter for new line
-        </p>
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/SourcePane.tsx.html b/apps/web/coverage/lcov-report/src/components/study/SourcePane.tsx.html deleted file mode 100644 index 1288a7c..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/SourcePane.tsx.html +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - Code coverage report for src/components/study/SourcePane.tsx - - - - - - - - - -
-
-

All files / src/components/study SourcePane.tsx

-
- -
- 40% - Statements - 2/5 -
- - -
- 0% - Branches - 0/7 -
- - -
- 0% - Functions - 0/2 -
- - -
- 40% - Lines - 2/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117  -  -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { LessonNav } from './LessonNav';
-import { ContentChunks } from './ContentChunks';
-import type { Lesson } from '@/lib/services/courses';
- 
-interface SourcePaneProps {
-  courseId: string;
-  accessToken?: string;
-  courseTitle?: string;
-  lessons: Lesson[];
-  selectedLesson: Lesson | null;
-  completedLessons: Set<string>;
-  onSelectLesson: (lesson: Lesson) => void;
-  onMarkComplete: (lesson: Lesson) => Promise<void>;
-  loadingLessons?: boolean;
-  activeCitationDocIds?: Set<string>;
-  lastStudiedLessonId?: string | null;
-}
- 
-export function SourcePane({
-  courseId,
-  accessToken,
-  courseTitle,
-  lessons,
-  selectedLesson,
-  completedLessons,
-  onSelectLesson,
-  onMarkComplete,
-  loadingLessons = false,
-  activeCitationDocIds,
-  lastStudiedLessonId,
-}: SourcePaneProps) {
-  const isCompleted = selectedLesson ? completedLessons.has(String(selectedLesson.id)) : false;
- 
-  return (
-    <div className="h-full flex flex-col bg-white dark:bg-blue-dark/30">
-      {/* Header */}
-      <div className="flex-shrink-0 px-5 py-3 b-thin-b flex items-center gap-3">
-        <span className="mono text-[10px] tracking-[0.22em] uppercase opacity-70">Source</span>
-        {courseTitle && <span className="font-medium text-sm truncate">{courseTitle}</span>}
-      </div>
- 
-      {/* Lesson nav */}
-      <div className="flex-shrink-0 b-thin-b overflow-y-auto max-h-52 ws-scroll">
-        <LessonNav
-          lessons={lessons}
-          selectedLesson={selectedLesson}
-          completedLessons={completedLessons}
-          onSelect={onSelectLesson}
-          loading={loadingLessons}
-          studiedLessonId={lastStudiedLessonId}
-        />
-      </div>
- 
-      {/* Lesson content */}
-      <div className="flex-1 overflow-y-auto ws-scroll">
-        {selectedLesson ? (
-          <div className="p-5 space-y-4">
-            <div className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-              Lesson {selectedLesson.id}
-            </div>
-            <h3 className="text-xl font-medium leading-snug">{selectedLesson.title}</h3>
- 
-            {selectedLesson.content && (
-              <p className="text-[13.5px] leading-relaxed opacity-90 whitespace-pre-wrap">
-                {selectedLesson.content}
-              </p>
-            )}
- 
-            <ContentChunks
-              courseId={courseId}
-              accessToken={accessToken}
-              className="mt-2"
-              activeCitationDocIds={activeCitationDocIds}
-            />
- 
-            <div className="pt-2 b-thin-t">
-              {isCompleted ? (
-                <div
-                  className="flex items-center gap-2 font-mono text-[11px]"
-                  style={{ color: '#1a7f3a' }}
-                >
-                  <svg
-                    className="h-4 w-4"
-                    fill="none"
-                    viewBox="0 0 24 24"
-                    strokeWidth={2.5}
-                    stroke="currentColor"
-                  >
-                    <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
-                  </svg>
-                  Lesson completed
-                </div>
-              ) : (
-                <button
-                  onClick={() => onMarkComplete(selectedLesson)}
-                  className="btn-ghost text-sm"
-                >
-                  Mark as complete
-                </button>
-              )}
-            </div>
-          </div>
-        ) : (
-          <div className="flex items-center justify-center h-full p-8">
-            <div className="text-center">
-              <div className="mx-auto w-10 h-10 b-thin rounded-md mb-4 stripes" />
-              <p className="font-mono text-[11px] opacity-60">Select a lesson to begin.</p>
-            </div>
-          </div>
-        )}
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/SplitPane.tsx.html b/apps/web/coverage/lcov-report/src/components/study/SplitPane.tsx.html deleted file mode 100644 index 2926ad7..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/SplitPane.tsx.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - - - Code coverage report for src/components/study/SplitPane.tsx - - - - - - - - - -
-
-

All files / src/components/study SplitPane.tsx

-
- -
- 2.7% - Statements - 1/37 -
- - -
- 0% - Branches - 0/13 -
- - -
- 0% - Functions - 0/9 -
- - -
- 2.94% - Lines - 1/34 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
- 
-interface SplitPaneProps {
-  left: ReactNode;
-  right: ReactNode;
-  defaultLeftPercent?: number;
-  minLeftPercent?: number;
-  maxLeftPercent?: number;
-}
- 
-export function SplitPane({
-  left,
-  right,
-  defaultLeftPercent = 50,
-  minLeftPercent = 25,
-  maxLeftPercent = 75,
-}: SplitPaneProps) {
-  const [leftPercent, setLeftPercent] = useState(defaultLeftPercent);
-  const [isMobile, setIsMobile] = useState(false);
-  const [activeTab, setActiveTab] = useState<'left' | 'right'>('right');
-  const containerRef = useRef<HTMLDivElement>(null);
-  const dragging = useRef(false);
- 
-  useEffect(() => {
-    const mq = window.matchMedia('(max-width: 767px)');
-    setIsMobile(mq.matches);
-    const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
-    mq.addEventListener('change', handler);
-    return () => mq.removeEventListener('change', handler);
-  }, []);
- 
-  const onMouseDown = useCallback(
-    (e: React.MouseEvent) => {
-      e.preventDefault();
-      dragging.current = true;
- 
-      const onMouseMove = (ev: MouseEvent) => {
-        Iif (!dragging.current || !containerRef.current) return;
-        const rect = containerRef.current.getBoundingClientRect();
-        const percent = ((ev.clientX - rect.left) / rect.width) * 100;
-        setLeftPercent(Math.min(maxLeftPercent, Math.max(minLeftPercent, percent)));
-      };
- 
-      const onMouseUp = () => {
-        dragging.current = false;
-        document.removeEventListener('mousemove', onMouseMove);
-        document.removeEventListener('mouseup', onMouseUp);
-        document.body.style.cursor = '';
-        document.body.style.userSelect = '';
-      };
- 
-      document.body.style.cursor = 'col-resize';
-      document.body.style.userSelect = 'none';
-      document.addEventListener('mousemove', onMouseMove);
-      document.addEventListener('mouseup', onMouseUp);
-    },
-    [minLeftPercent, maxLeftPercent]
-  );
- 
-  Iif (isMobile) {
-    return (
-      <div className="flex flex-col h-full w-full overflow-hidden">
-        {/* Tab bar */}
-        <div className="flex-shrink-0 flex b-thin-b">
-          <button
-            onClick={() => setActiveTab('left')}
-            className={`flex-1 py-2 font-mono text-[11px] tracking-[0.14em] uppercase transition-colors ${
-              activeTab === 'left'
-                ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
-            }`}
-          >
-            Sources
-          </button>
-          <button
-            onClick={() => setActiveTab('right')}
-            className={`flex-1 py-2 font-mono text-[11px] tracking-[0.14em] uppercase transition-colors ${
-              activeTab === 'right'
-                ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
-            }`}
-          >
-            Study
-          </button>
-        </div>
-        {/* Active pane */}
-        <div className="flex-1 overflow-auto min-h-0">{activeTab === 'left' ? left : right}</div>
-      </div>
-    );
-  }
- 
-  return (
-    <div ref={containerRef} className="flex h-full w-full overflow-hidden">
-      <div style={{ width: `${leftPercent}%` }} className="overflow-auto">
-        {left}
-      </div>
- 
-      <div
-        onMouseDown={onMouseDown}
-        className="flex-shrink-0 w-1.5 cursor-col-resize bg-gray-200 hover:bg-orange-300 active:bg-orange-400 transition-colors relative group"
-      >
-        <div className="absolute inset-y-0 -left-1 -right-1" />
-        <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-8 w-1 rounded-full bg-gray-400 group-hover:bg-orange-500 transition-colors" />
-      </div>
- 
-      <div style={{ width: `${100 - leftPercent}%` }} className="overflow-auto">
-        {right}
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/StudyActionBar.tsx.html b/apps/web/coverage/lcov-report/src/components/study/StudyActionBar.tsx.html deleted file mode 100644 index 035519c..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/StudyActionBar.tsx.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - Code coverage report for src/components/study/StudyActionBar.tsx - - - - - - - - - -
-
-

All files / src/components/study StudyActionBar.tsx

-
- -
- 85.71% - Statements - 6/7 -
- - -
- 54.54% - Branches - 6/11 -
- - -
- 66.66% - Functions - 2/3 -
- - -
- 85.71% - Lines - 6/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128  -  -  -  -  -  -  -  -  -  -  -  -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -144x -144x -144x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import type { StudyAction } from '@/lib/api/types';
- 
-interface ActionDef {
-  id: StudyAction;
-  label: string;
-  sub: string;
-  glyph: string;
-  shortcut: string;
-}
- 
-const ACTIONS: ActionDef[] = [
-  { id: 'explain', label: 'Explain', sub: 'Step-by-step derivation', glyph: 'Σ', shortcut: 'E' },
-  {
-    id: 'summarize',
-    label: 'Summarize',
-    sub: 'Compressed, source-anchored',
-    glyph: '≡',
-    shortcut: 'S',
-  },
-  { id: 'retrieve', label: 'Retrieve', sub: 'Top-k from evidence pack', glyph: '⌖', shortcut: 'R' },
-  {
-    id: 'open_questions',
-    label: 'Questions',
-    sub: 'Conceptual prompts for depth',
-    glyph: '?',
-    shortcut: 'O',
-  },
-  { id: 'quiz', label: 'Quiz', sub: 'Multiple-choice with rationale', glyph: '▢', shortcut: 'Q' },
-  {
-    id: 'oral',
-    label: 'Oral Exam',
-    sub: 'Adversarial, edge-case driven',
-    glyph: '◉',
-    shortcut: 'L',
-  },
-  {
-    id: 'derive',
-    label: 'Derive',
-    sub: 'Proof scaffolding from sources',
-    glyph: '∂',
-    shortcut: 'D',
-  },
-  { id: 'compare', label: 'Compare', sub: 'Reconcile across sources', glyph: '⇌', shortcut: 'C' },
-];
- 
-interface StudyActionBarProps {
-  onAction: (action: StudyAction) => void;
-  activeAction: StudyAction | null;
-  loading: boolean;
-  disabled?: boolean;
-  hasIndexedDocs?: boolean;
-}
- 
-export function StudyActionBar({
-  onAction,
-  activeAction,
-  loading,
-  disabled,
-  hasIndexedDocs = true,
-}: StudyActionBarProps) {
-  const isDisabled = loading || disabled || !hasIndexedDocs;
- 
-  return (
-    <div className="flex-shrink-0 px-5 py-4 b-thin-b bg-white dark:bg-blue-dark/30">
-      <div className="flex items-end justify-between b-thin-b pb-1.5 mb-3">
-        <span className="font-mono text-[10px] tracking-[0.22em] uppercase opacity-70">
-          Study action
-        </span>
-        <span className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-60">
-          ⌘E/S/R/O/Q/L/D/C
-        </span>
-      </div>
-      <div className="grid grid-cols-4 gap-2">
-        {ACTIONS.map((a) => {
-          const isActive = activeAction === a.id;
-          const isRunning = isActive && loading;
-          return (
-            <button
-              key={a.id}
-              onClick={() => onAction(a.id)}
-              disabled={isDisabled}
-              title={hasIndexedDocs ? a.sub : 'Upload documents first'}
-              className={`text-left p-2.5 rounded-md transition-all disabled:opacity-40 disabled:cursor-not-allowed ${
-                isActive
-                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                  : 'b-thin hover:bg-blue-dark/5 dark:hover:bg-white/10'
-              }`}
-            >
-              <div className="flex items-center gap-2">
-                {isRunning ? (
-                  <svg
-                    className="w-4 h-4 animate-spin flex-shrink-0"
-                    fill="none"
-                    viewBox="0 0 24 24"
-                  >
-                    <circle
-                      className="opacity-25"
-                      cx="12"
-                      cy="12"
-                      r="10"
-                      stroke="currentColor"
-                      strokeWidth="4"
-                    />
-                    <path
-                      className="opacity-75"
-                      fill="currentColor"
-                      d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
-                    />
-                  </svg>
-                ) : (
-                  <span className="mono text-base leading-none w-5 flex-shrink-0">{a.glyph}</span>
-                )}
-                <span className="text-[12px] font-medium leading-tight truncate">{a.label}</span>
-                <span className="ml-auto mono text-[9px] opacity-60 flex-shrink-0">
-                  ⌘{a.shortcut}
-                </span>
-              </div>
-              <div className="font-mono text-[10px] opacity-70 mt-1.5 leading-snug">{a.sub}</div>
-            </button>
-          );
-        })}
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html b/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html deleted file mode 100644 index cdef2da..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/StudyOutput.tsx.html +++ /dev/null @@ -1,931 +0,0 @@ - - - - - - Code coverage report for src/components/study/StudyOutput.tsx - - - - - - - - - -
-
-

All files / src/components/study StudyOutput.tsx

-
- -
- 5.66% - Statements - 3/53 -
- - -
- 0% - Branches - 0/83 -
- - -
- 0% - Functions - 0/31 -
- - -
- 7.14% - Lines - 3/42 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283  -  -2x -2x -  -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { useMemo, useState } from 'react';
-import ReactMarkdown from 'react-markdown';
-import type { ApiStudyResponse } from '@/lib/api/types';
-import { CitationCard } from './CitationCard';
- 
-interface StudyOutputProps {
-  result: ApiStudyResponse;
-  courseId: string;
-  onOralFollowUp?: (query: string) => void;
-}
- 
-export function StudyOutput({ result, courseId, onOralFollowUp }: StudyOutputProps) {
-  const [showSources, setShowSources] = useState(result.action === 'retrieve');
- 
-  const hasOutput = result.answer && result.answer.trim().length > 0;
-  const hasCitations = result.citations.length > 0;
- 
-  return (
-    <div className="space-y-4">
-      {!hasOutput && !hasCitations && (
-        <p className="font-mono text-[11px] opacity-50 italic">
-          No results found in course materials.
-        </p>
-      )}
- 
-      {!hasOutput && hasCitations && result.action !== 'retrieve' && (
-        <div
-          className="b-thin rounded-md px-4 py-3 text-sm"
-          style={{ borderColor: '#a55a00', color: '#a55a00' }}
-        >
-          LLM generation unavailable (OPENAI_API_KEY not configured). Showing source passages below.
-        </div>
-      )}
- 
-      {hasOutput && result.action === 'quiz' ? (
-        <QuizOutput text={result.answer} />
-      ) : hasOutput && result.action === 'oral' ? (
-        <OralOutput text={result.answer} onSubmit={onOralFollowUp} />
-      ) : hasOutput && result.action === 'open_questions' ? (
-        <QuestionsOutput text={result.answer} />
-      ) : hasOutput ? (
-        <ReactMarkdown className="md-prose">{result.answer}</ReactMarkdown>
-      ) : null}
- 
-      {/* Sources toggle (non-retrieve actions) */}
-      {hasCitations && result.action !== 'retrieve' && (
-        <div>
-          <button
-            onClick={() => setShowSources((v) => !v)}
-            className="flex items-center gap-1.5 font-mono text-[11px] tracking-[0.14em] uppercase opacity-70 hover:opacity-100 transition-opacity"
-          >
-            <svg
-              className={`h-3 w-3 transition-transform ${showSources ? 'rotate-90' : ''}`}
-              fill="none"
-              viewBox="0 0 24 24"
-              strokeWidth={2.5}
-              stroke="currentColor"
-            >
-              <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
-            </svg>
-            {showSources ? 'Hide' : 'Show'} {result.citations.length} source
-            {result.citations.length !== 1 ? 's' : ''}
-          </button>
-          {showSources && (
-            <div className="mt-2 space-y-2">
-              {result.citations.map((citation, i) => (
-                <CitationCard key={i} citation={citation} courseId={courseId} index={i + 1} />
-              ))}
-            </div>
-          )}
-        </div>
-      )}
- 
-      {/* Retrieve: always show citations directly */}
-      {result.action === 'retrieve' && hasCitations && (
-        <div className="space-y-2">
-          {result.citations.map((citation, i) => (
-            <CitationCard key={i} citation={citation} courseId={courseId} index={i + 1} />
-          ))}
-        </div>
-      )}
-    </div>
-  );
-}
- 
-// ── Quiz ──────────────────────────────────────────────────────────────────────
- 
-interface ParsedQuestion {
-  question: string;
-  options: string[];
-  correctLetter: string;
-}
- 
-function parseQuizQuestion(raw: string): ParsedQuestion {
-  const lines = raw
-    .split('\n')
-    .map((l) => l.trim())
-    .filter(Boolean);
-  const qLine = lines.find((l) => /^Q\d*[:.]/i.test(l));
-  const question = qLine ? qLine.replace(/^Q\d*[:.]\s*/i, '') : raw.trim();
-  const options = lines.filter((l) => /^[A-D][).]\s/.test(l));
-  const answerLine = lines.find((l) => /^Answer:/i.test(l));
-  const correctLetter = answerLine
-    ? answerLine
-        .replace(/^Answer:\s*/i, '')
-        .trim()
-        .charAt(0)
-        .toUpperCase()
-    : '';
-  return { question, options, correctLetter };
-}
- 
-function QuizQuestion({ raw, index }: { raw: string; index: number }) {
-  const [selected, setSelected] = useState('');
-  const [revealed, setRevealed] = useState(false);
-  const { question, options, correctLetter } = useMemo(() => parseQuizQuestion(raw), [raw]);
- 
-  return (
-    <div className="b-thin rounded-lg p-4 bg-white dark:bg-blue-dark/20">
-      <p className="font-mono text-[10px] tracking-[0.18em] uppercase opacity-50 mb-2">
-        Q{index + 1}
-      </p>
-      <p className="text-[13.5px] font-medium leading-snug mb-3">{question}</p>
- 
-      {options.length > 0 ? (
-        <div className="space-y-2">
-          {options.map((opt) => {
-            const letter = opt.charAt(0).toUpperCase();
-            const isCorrect = letter === correctLetter;
-            const isSelected = selected === letter;
-            return (
-              <button
-                key={letter}
-                onClick={() => !revealed && setSelected(letter)}
-                disabled={revealed}
-                className={`w-full b-thin rounded-md px-3 py-2 text-left text-[13px] transition-colors ${
-                  revealed && isCorrect
-                    ? 'bg-[rgba(26,127,58,0.08)] dark:bg-[rgba(26,127,58,0.15)]'
-                    : revealed && isSelected && !isCorrect
-                      ? 'bg-[rgba(179,38,30,0.08)] dark:bg-[rgba(179,38,30,0.15)]'
-                      : isSelected
-                        ? 'bg-blue-dark text-white'
-                        : 'hover:bg-blue-dark/5 dark:hover:bg-white/5'
-                }`}
-                style={
-                  revealed && isCorrect
-                    ? { borderColor: '#1a7f3a' }
-                    : revealed && isSelected && !isCorrect
-                      ? { borderColor: '#b3261e' }
-                      : {}
-                }
-              >
-                {opt}
-              </button>
-            );
-          })}
- 
-          <div className="flex items-center gap-3 pt-1">
-            {!revealed && selected && (
-              <button onClick={() => setRevealed(true)} className="btn-ghost text-[11px]">
-                Check answer
-              </button>
-            )}
-            {revealed && (
-              <p
-                className="font-mono text-[11px]"
-                style={{ color: selected === correctLetter ? '#1a7f3a' : '#b3261e' }}
-              >
-                {selected === correctLetter ? '✓ Correct' : `✗ Correct: ${correctLetter}`}
-              </p>
-            )}
-          </div>
-        </div>
-      ) : (
-        /* Fallback: options not parseable — show plain reveal */
-        <div>
-          <button
-            onClick={() => setRevealed((v) => !v)}
-            className="font-mono text-[11px] tracking-[0.14em] uppercase opacity-70 hover:opacity-100"
-          >
-            {revealed ? 'Hide answer' : 'Reveal answer'}
-          </button>
-          {revealed && correctLetter && (
-            <div
-              className="mt-2 b-thin rounded-md px-3 py-2"
-              style={{ borderColor: '#1a7f3a', background: 'rgba(26,127,58,0.06)' }}
-            >
-              <p className="font-mono text-[12px]" style={{ color: '#1a7f3a' }}>
-                Answer: {correctLetter}
-              </p>
-            </div>
-          )}
-        </div>
-      )}
-    </div>
-  );
-}
- 
-function QuizOutput({ text }: { text: string }) {
-  const questions = text.split(/\n(?=Q:)/g).filter((s) => s.trim());
-  Iif (questions.length === 0) {
-    return (
-      <pre className="whitespace-pre-wrap font-sans text-[13.5px] leading-relaxed">{text}</pre>
-    );
-  }
-  return (
-    <div className="space-y-4">
-      {questions.map((q, i) => (
-        <QuizQuestion key={i} raw={q} index={i} />
-      ))}
-    </div>
-  );
-}
- 
-// ── Oral ──────────────────────────────────────────────────────────────────────
- 
-function OralOutput({ text, onSubmit }: { text: string; onSubmit?: (query: string) => void }) {
-  const [answers, setAnswers] = useState<Record<number, string>>({});
-  const questions = text.split(/\n(?=Q\d+:)/g).filter((s) => s.trim());
- 
-  function handleSubmit(idx: number, questionText: string) {
-    const answer = (answers[idx] ?? '').trim();
-    Iif (!answer || !onSubmit) return;
-    onSubmit(`${questionText.trim()}\n\nMy answer: ${answer}`);
-  }
- 
-  function answerBox(idx: number, questionText: string) {
-    return (
-      <div className="mt-3 space-y-2">
-        <textarea
-          value={answers[idx] ?? ''}
-          onChange={(e) => setAnswers((prev) => ({ ...prev, [idx]: e.target.value }))}
-          placeholder="Type your answer here…"
-          rows={3}
-          className="w-full resize-none rounded-md b-thin px-3 py-2 text-sm bg-transparent outline-none focus:ring-1 focus:ring-blue-dark"
-        />
-        {(answers[idx] ?? '').trim() && onSubmit && (
-          <button onClick={() => handleSubmit(idx, questionText)} className="btn-ghost text-[11px]">
-            Submit answer →
-          </button>
-        )}
-      </div>
-    );
-  }
- 
-  Iif (questions.length === 0) {
-    return (
-      <div>
-        <ReactMarkdown className="md-prose">{text}</ReactMarkdown>
-        {answerBox(0, text)}
-      </div>
-    );
-  }
- 
-  return (
-    <div className="space-y-4">
-      {questions.map((q, i) => (
-        <div key={i} className="b-thin rounded-lg p-4 bg-white dark:bg-blue-dark/20">
-          <ReactMarkdown className="md-prose">{q.trim()}</ReactMarkdown>
-          {answerBox(i, q)}
-        </div>
-      ))}
-    </div>
-  );
-}
- 
-// ── Open questions ────────────────────────────────────────────────────────────
- 
-function QuestionsOutput({ text }: { text: string }) {
-  const lines = text.split('\n').filter((l) => l.trim());
-  return (
-    <div className="space-y-2">
-      {lines.map((line, i) => (
-        <p key={i} className="text-[13.5px] leading-relaxed">
-          {line}
-        </p>
-      ))}
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/study/index.html b/apps/web/coverage/lcov-report/src/components/study/index.html deleted file mode 100644 index fe7c163..0000000 --- a/apps/web/coverage/lcov-report/src/components/study/index.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - Code coverage report for src/components/study - - - - - - - - - -
-
-

All files src/components/study

-
- -
- 19.37% - Statements - 50/258 -
- - -
- 15.64% - Branches - 41/262 -
- - -
- 9% - Functions - 9/100 -
- - -
- 22.02% - Lines - 50/227 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
CitationCard.tsx -
-
6.66%1/150%0/220%0/28.33%1/12
ContentChunks.tsx -
-
9.09%3/330%0/220%0/139.37%3/32
LessonNav.tsx -
-
100%10/1093.33%14/15100%4/4100%10/10
OutputPane.tsx -
-
24.48%24/9823.59%21/898.33%3/3628.23%24/85
SourcePane.tsx -
-
40%2/50%0/70%0/240%2/5
SplitPane.tsx -
-
2.7%1/370%0/130%0/92.94%1/34
StudyActionBar.tsx -
-
85.71%6/754.54%6/1166.66%2/385.71%6/7
StudyOutput.tsx -
-
5.66%3/530%0/830%0/317.14%3/42
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/BadgeDisplay.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/BadgeDisplay.tsx.html deleted file mode 100644 index 208ce0f..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/BadgeDisplay.tsx.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - Code coverage report for src/components/ui/BadgeDisplay.tsx - - - - - - - - - -
-
-

All files / src/components/ui BadgeDisplay.tsx

-
- -
- 0% - Statements - 0/3 -
- - -
- 0% - Branches - 0/6 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/3 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import type { Badge } from '@/lib/services/progress';
- 
-interface BadgeDisplayProps {
-  badge: Badge;
-  size?: 'sm' | 'md';
-}
- 
-export function BadgeDisplay({ badge, size = 'md' }: BadgeDisplayProps) {
-  const iconSize = size === 'sm' ? 'text-lg' : 'text-3xl';
-  const padding = size === 'sm' ? 'px-2 py-1' : 'px-3 py-2';
- 
-  return (
-    <div
-      className={`inline-flex items-center gap-2 rounded-lg bg-yellow-50 border border-yellow-200 ${padding}`}
-      title={badge.description}
-      role="img"
-      aria-label={`Badge: ${badge.name}`}
-    >
-      <span className={iconSize} aria-hidden="true">
-        {badge.icon}
-      </span>
-      <div>
-        <p className="text-xs font-semibold text-yellow-800">{badge.name}</p>
-        {size === 'md' && <p className="text-xs text-yellow-600">{badge.description}</p>}
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html deleted file mode 100644 index b1a7f16..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/BrandMark.tsx.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - Code coverage report for src/components/ui/BrandMark.tsx - - - - - - - - - -
-
-

All files / src/components/ui BrandMark.tsx

-
- -
- 0% - Statements - 0/2 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/2 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import Link from 'next/link';
- 
-export function BrandMark() {
-  return (
-    <Link href="/courses" className="inline-flex items-center gap-3">
-      <svg viewBox="0 0 36 24" className="w-9 h-6 ink" fill="currentColor" aria-hidden>
-        <rect x="0" y="2" width="2" height="14" />
-        <rect x="2" y="0" width="2" height="2" />
-        <rect x="4" y="2" width="2" height="2" />
-        <rect x="6" y="4" width="2" height="14" />
-        <rect x="4" y="14" width="2" height="2" />
-        <rect x="2" y="16" width="2" height="2" />
-        <rect x="10" y="6" width="2" height="12" />
-        <rect x="12" y="4" width="2" height="2" />
-        <rect x="14" y="6" width="2" height="6" />
-        <rect x="12" y="12" width="2" height="2" />
-        <rect x="20" y="2" width="2" height="20" />
-        <rect x="22" y="0" width="2" height="2" />
-        <rect x="22" y="22" width="2" height="2" />
-        <rect x="28" y="6" width="2" height="12" />
-        <rect x="30" y="4" width="2" height="2" />
-        <rect x="32" y="6" width="2" height="12" />
-        <rect x="30" y="18" width="2" height="2" />
-      </svg>
-      <span className="font-mono text-[11px] tracking-[0.18em] uppercase ink font-semibold hidden sm:inline">
-        BitPolito · Academy
-      </span>
-    </Link>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html deleted file mode 100644 index 81aad64..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/ErrorBoundary.tsx.html +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - Code coverage report for src/components/ui/ErrorBoundary.tsx - - - - - - - - - -
-
-

All files / src/components/ui ErrorBoundary.tsx

-
- -
- 9.09% - Statements - 1/11 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/4 -
- - -
- 10% - Lines - 1/10 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { Component, type ErrorInfo, type ReactNode } from 'react';
- 
-interface Props {
-  children: ReactNode;
-  fallback?: ReactNode;
-}
- 
-interface State {
-  hasError: boolean;
-}
- 
-export class ErrorBoundary extends Component<Props, State> {
-  state: State = { hasError: false };
- 
-  static getDerivedStateFromError(): State {
-    return { hasError: true };
-  }
- 
-  componentDidCatch(error: Error, info: ErrorInfo) {
-    console.error('[ErrorBoundary] Caught rendering error:', error, info.componentStack);
-  }
- 
-  handleReset = () => {
-    this.setState({ hasError: false });
-  };
- 
-  render() {
-    Iif (this.state.hasError) {
-      Iif (this.props.fallback) return this.props.fallback;
- 
-      return (
-        <div className="flex flex-col items-center justify-center h-full p-6 text-center">
-          <svg
-            className="h-10 w-10 text-gray-300 mb-3"
-            fill="none"
-            viewBox="0 0 24 24"
-            strokeWidth={1.5}
-            stroke="currentColor"
-          >
-            <path
-              strokeLinecap="round"
-              strokeLinejoin="round"
-              d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
-            />
-          </svg>
-          <p className="text-sm text-gray-600 mb-3">Something went wrong in this section.</p>
-          <button
-            onClick={this.handleReset}
-            className="text-sm font-medium text-orange-600 hover:text-orange-700 underline"
-          >
-            Try again
-          </button>
-        </div>
-      );
-    }
- 
-    return this.props.children;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html deleted file mode 100644 index 961fb7c..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/ProgressBar.tsx.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - - - Code coverage report for src/components/ui/ProgressBar.tsx - - - - - - - - - -
-
-

All files / src/components/ui ProgressBar.tsx

-
- -
- 100% - Statements - 4/4 -
- - -
- 92.3% - Branches - 12/13 -
- - -
- 100% - Functions - 1/1 -
- - -
- 100% - Lines - 4/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46  -  -  -  -  -  -  -  -  -  -13x -  -  -  -  -  -  -13x -13x -13x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-interface ProgressBarProps {
-  percent: number;
-  label?: string;
-  showPercent?: boolean;
-  size?: 'sm' | 'md';
-  className?: string;
-}
- 
-export function ProgressBar({
-  percent,
-  label,
-  showPercent = true,
-  size = 'sm',
-  className = '',
-}: ProgressBarProps) {
-  const clamped = Math.max(0, Math.min(100, percent));
-  const barHeight = size === 'sm' ? 'h-1.5' : 'h-2.5';
-  const barColor = clamped === 100 ? 'bg-green-500' : 'bg-orange-500';
- 
-  return (
-    <div className={className}>
-      {(label || showPercent) && (
-        <div className="flex justify-between items-center mb-1">
-          {label && <span className="text-xs text-gray-500">{label}</span>}
-          {showPercent && <span className="text-xs font-medium text-gray-700">{clamped}%</span>}
-        </div>
-      )}
-      <div
-        className={`w-full bg-gray-200 rounded-full ${barHeight}`}
-        role="progressbar"
-        aria-valuenow={clamped}
-        aria-valuemin={0}
-        aria-valuemax={100}
-        aria-label={label ?? `Progress: ${clamped}%`}
-      >
-        <div
-          className={`${barColor} ${barHeight} rounded-full transition-all duration-500`}
-          style={{ width: `${clamped}%` }}
-        />
-      </div>
-    </div>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html deleted file mode 100644 index 7dd6b8e..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/Toast.tsx.html +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - Code coverage report for src/components/ui/Toast.tsx - - - - - - - - - -
-
-

All files / src/components/ui Toast.tsx

-
- -
- 0% - Statements - 0/23 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/13 -
- - -
- 0% - Lines - 0/18 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import { createContext, useCallback, useContext, useRef, useState } from 'react';
- 
-// ── Types ─────────────────────────────────────────────────────────────────────
- 
-export type ToastType = 'ok' | 'err' | 'warn';
- 
-interface ToastMsg {
-  id: number;
-  message: string;
-  type: ToastType;
-}
- 
-interface ToastContextValue {
-  showToast: (message: string, type?: ToastType) => void;
-}
- 
-// ── Context ───────────────────────────────────────────────────────────────────
- 
-const ToastContext = createContext<ToastContextValue>({ showToast: () => {} });
- 
-export function useToast() {
-  return useContext(ToastContext);
-}
- 
-// ── Style map ─────────────────────────────────────────────────────────────────
- 
-const TYPE_CONFIG: Record<ToastType, { bg: string; icon: string }> = {
-  ok: { bg: '#1a7f3a', icon: '✓' },
-  err: { bg: '#b3261e', icon: '✕' },
-  warn: { bg: '#a55a00', icon: '!' },
-};
- 
-// ── Provider ──────────────────────────────────────────────────────────────────
- 
-export function ToastProvider({ children }: { children: React.ReactNode }) {
-  const [toasts, setToasts] = useState<ToastMsg[]>([]);
-  const counter = useRef(0);
- 
-  const showToast = useCallback((message: string, type: ToastType = 'ok') => {
-    const id = ++counter.current;
-    setToasts((prev) => [...prev, { id, message, type }]);
-    setTimeout(() => {
-      setToasts((prev) => prev.filter((t) => t.id !== id));
-    }, 4000);
-  }, []);
- 
-  const dismiss = useCallback((id: number) => {
-    setToasts((prev) => prev.filter((t) => t.id !== id));
-  }, []);
- 
-  return (
-    <ToastContext.Provider value={{ showToast }}>
-      {children}
-      {toasts.length > 0 && (
-        <div
-          className="fixed bottom-5 right-5 z-50 flex flex-col gap-2 pointer-events-none"
-          aria-live="assertive"
-          aria-label="Notifications"
-        >
-          {toasts.map((t) => {
-            const { bg, icon } = TYPE_CONFIG[t.type];
-            return (
-              <div
-                key={t.id}
-                role="alert"
-                className="pointer-events-auto flex items-center gap-3 px-4 py-2.5 rounded-lg shadow-lg text-white font-mono text-[12px] max-w-xs"
-                style={{ background: bg }}
-              >
-                <span className="text-sm font-bold flex-shrink-0" aria-hidden="true">
-                  {icon}
-                </span>
-                <span className="flex-1 leading-snug break-words">{t.message}</span>
-                <button
-                  onClick={() => dismiss(t.id)}
-                  className="flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity ml-1"
-                  aria-label="Dismiss notification"
-                >
-                  ×
-                </button>
-              </div>
-            );
-          })}
-        </div>
-      )}
-    </ToastContext.Provider>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html b/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html deleted file mode 100644 index bc72b38..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/TopBar.tsx.html +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - Code coverage report for src/components/ui/TopBar.tsx - - - - - - - - - -
-
-

All files / src/components/ui TopBar.tsx

-
- -
- 0% - Statements - 0/31 -
- - -
- 0% - Branches - 0/30 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/31 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
'use client';
- 
-import Link from 'next/link';
-import { useEffect, useState } from 'react';
-import { usePathname } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import { BrandMark } from './BrandMark';
- 
-export function TopBar() {
-  const pathname = usePathname();
-  const { data: session } = useSession();
-  const [dark, setDark] = useState(false);
- 
-  // Sync with system preference and persisted value on mount
-  useEffect(() => {
-    const saved = localStorage.getItem('theme');
-    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
-    const isDark = saved === 'dark' || (!saved && prefersDark);
-    setDark(isDark);
-    document.documentElement.classList.toggle('dark', isDark);
-  }, []);
- 
-  function toggleDark() {
-    const next = !dark;
-    setDark(next);
-    document.documentElement.classList.toggle('dark', next);
-    localStorage.setItem('theme', next ? 'dark' : 'light');
-  }
- 
-  const courseMatch = pathname.match(/^\/courses\/([^/]+)/);
-  const courseId = courseMatch?.[1];
- 
-  const isStudy = !!courseId && pathname.includes('/study');
-  const isPreview = !!courseId && pathname.includes('/preview');
-  const isWorkspace = !!courseId && !isStudy && !isPreview;
-  const isCourses = !courseId;
- 
-  const tabs = [
-    { id: 'courses', label: 'Courses', href: '/courses', active: isCourses },
-    ...(courseId
-      ? [
-          {
-            id: 'workspace',
-            label: 'Workspace',
-            href: `/courses/${courseId}`,
-            active: isWorkspace,
-          },
-          { id: 'study', label: 'Study', href: `/courses/${courseId}/study`, active: isStudy },
-        ]
-      : []),
-  ];
- 
-  const isDebug = !!courseId && pathname.includes('/debug');
- 
-  const name = session?.user?.name || session?.user?.email || '';
-  const initials =
-    name
-      .split(/[\s@]/)
-      .filter(Boolean)
-      .map((p: string) => p[0])
-      .join('')
-      .slice(0, 2)
-      .toUpperCase() || 'U';
- 
-  return (
-    <header className="sticky top-0 z-40 bg-surface dark:bg-blue-dark b-thin-b">
-      <div className="max-w-8xl mx-auto px-6 h-14 flex items-center gap-6">
-        <BrandMark />
- 
-        <nav className="flex items-center gap-1 ml-2">
-          {tabs.map((tab) => (
-            <Link
-              key={tab.id}
-              href={tab.href}
-              className={`px-3 h-8 rounded-md font-mono text-[11px] tracking-[0.14em] uppercase whitespace-nowrap transition-all inline-flex items-center ${
-                tab.active
-                  ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                  : 'hover:bg-blue-dark/5 dark:hover:bg-white/10'
-              }`}
-            >
-              {tab.label}
-            </Link>
-          ))}
-        </nav>
- 
-        {process.env.NODE_ENV === 'development' && courseId && (
-          <Link
-            href={`/courses/${courseId}/debug`}
-            className={`px-3 h-8 rounded-md font-mono text-[11px] tracking-[0.14em] uppercase whitespace-nowrap transition-all inline-flex items-center ${
-              isDebug
-                ? 'bg-blue-dark text-white dark:bg-white dark:text-blue-dark'
-                : 'opacity-50 hover:opacity-100 hover:bg-blue-dark/5 dark:hover:bg-white/10'
-            }`}
-          >
-            Debug
-          </Link>
-        )}
- 
-        <div className="ml-auto flex items-center gap-2">
-          {/* Dark mode toggle */}
-          <button
-            onClick={toggleDark}
-            className="h-8 w-8 b-thin rounded-md flex items-center justify-center hover:bg-blue-dark/5 dark:hover:bg-white/10 transition-colors"
-            title={dark ? 'Switch to light mode' : 'Switch to dark mode'}
-            aria-label="Toggle dark mode"
-          >
-            {dark ? (
-              /* Sun icon */
-              <svg viewBox="0 0 24 24" className="w-3.5 h-3.5" fill="currentColor">
-                <circle cx="12" cy="12" r="4" />
-                <g stroke="currentColor" strokeWidth="2" strokeLinecap="round">
-                  <line x1="12" y1="2" x2="12" y2="5" />
-                  <line x1="12" y1="19" x2="12" y2="22" />
-                  <line x1="2" y1="12" x2="5" y2="12" />
-                  <line x1="19" y1="12" x2="22" y2="12" />
-                  <line x1="4.5" y1="4.5" x2="6.5" y2="6.5" />
-                  <line x1="17.5" y1="17.5" x2="19.5" y2="19.5" />
-                  <line x1="4.5" y1="19.5" x2="6.5" y2="17.5" />
-                  <line x1="17.5" y1="6.5" x2="19.5" y2="4.5" />
-                </g>
-              </svg>
-            ) : (
-              /* Moon icon */
-              <svg viewBox="0 0 24 24" className="w-3.5 h-3.5" fill="currentColor">
-                <path d="M21 12.8A9 9 0 0 1 11.2 3a7 7 0 1 0 9.8 9.8z" />
-              </svg>
-            )}
-          </button>
- 
-          {/* User avatar */}
-          <div
-            className="h-8 w-8 b-thin rounded-md flex items-center justify-center font-mono text-[11px] font-semibold"
-            title={name}
-          >
-            {initials}
-          </div>
-        </div>
-      </div>
-    </header>
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/components/ui/index.html b/apps/web/coverage/lcov-report/src/components/ui/index.html deleted file mode 100644 index 223a4b2..0000000 --- a/apps/web/coverage/lcov-report/src/components/ui/index.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - Code coverage report for src/components/ui - - - - - - - - - -
-
-

All files src/components/ui

-
- -
- 6.75% - Statements - 5/74 -
- - -
- 22.64% - Branches - 12/53 -
- - -
- 4% - Functions - 1/25 -
- - -
- 7.35% - Lines - 5/68 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
BadgeDisplay.tsx -
-
0%0/30%0/60%0/10%0/3
BrandMark.tsx -
-
0%0/2100%0/00%0/10%0/2
ErrorBoundary.tsx -
-
9.09%1/110%0/20%0/410%1/10
ProgressBar.tsx -
-
100%4/492.3%12/13100%1/1100%4/4
Toast.tsx -
-
0%0/230%0/20%0/130%0/18
TopBar.tsx -
-
0%0/310%0/300%0/50%0/31
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/index.html b/apps/web/coverage/lcov-report/src/index.html deleted file mode 100644 index 3ebccd2..0000000 --- a/apps/web/coverage/lcov-report/src/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src - - - - - - - - - -
-
-

All files src

-
- -
- 0% - Statements - 0/3 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 0% - Lines - 0/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
middleware.ts -
-
0%0/3100%0/0100%0/00%0/1
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api.ts.html b/apps/web/coverage/lcov-report/src/lib/api.ts.html deleted file mode 100644 index e49185b..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api.ts.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - Code coverage report for src/lib/api.ts - - - - - - - - - -
-
-

All files / src/lib api.ts

-
- -
- 5% - Statements - 1/20 -
- - -
- 17.64% - Branches - 3/17 -
- - -
- 0% - Functions - 0/4 -
- - -
- 5.26% - Lines - 1/19 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58  -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
const API_BASE_URL =
-  process.env.NEXT_PUBLIC_API_BASE_URL ||
-  (process.env.NEXT_PUBLIC_API_URL
-    ? `${process.env.NEXT_PUBLIC_API_URL}/api`
-    : 'http://localhost:8000/api');
- 
-export class ApiError extends Error {
-  constructor(
-    public status: number,
-    message: string,
-    public details?: Record<string, unknown>
-  ) {
-    super(message);
-    this.name = 'ApiError';
-  }
-}
- 
-interface FetchOptions extends Omit<RequestInit, 'body'> {
-  body?: unknown;
-  accessToken?: string;
-}
- 
-async function handleResponse<T>(response: Response): Promise<T> {
-  Iif (!response.ok) {
-    const errorBody = await response.json().catch(() => ({}));
-    throw new ApiError(
-      response.status,
-      errorBody.detail || errorBody.message || `Request failed (${response.status})`,
-      errorBody
-    );
-  }
-  return response.json();
-}
- 
-export async function apiFetch<T>(endpoint: string, options: FetchOptions = {}): Promise<T> {
-  const { body, accessToken, headers: customHeaders, ...rest } = options;
- 
-  const headers: Record<string, string> = {
-    ...(customHeaders as Record<string, string>),
-  };
- 
-  Iif (accessToken) {
-    headers['Authorization'] = `Bearer ${accessToken}`;
-  }
- 
-  Iif (body !== undefined && !(body instanceof FormData)) {
-    headers['Content-Type'] = 'application/json';
-  }
- 
-  const response = await fetch(`${API_BASE_URL}${endpoint}`, {
-    ...rest,
-    headers,
-    body: body instanceof FormData ? body : body !== undefined ? JSON.stringify(body) : undefined,
-  });
- 
-  return handleResponse<T>(response);
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api/adapters.ts.html b/apps/web/coverage/lcov-report/src/lib/api/adapters.ts.html deleted file mode 100644 index 94d5d37..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api/adapters.ts.html +++ /dev/null @@ -1,331 +0,0 @@ - - - - - - Code coverage report for src/lib/api/adapters.ts - - - - - - - - - -
-
-

All files / src/lib/api adapters.ts

-
- -
- 0% - Statements - 0/26 -
- - -
- 0% - Branches - 0/13 -
- - -
- 0% - Functions - 0/4 -
- - -
- 0% - Lines - 0/21 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import type {
-  ApiDocumentListItem,
-  ApiDocumentDetail,
-  ApiDocumentPreview,
-  DocumentListRow,
-  DocumentDetailView,
-  DocumentPreviewView,
-  DocumentStatus,
-} from './types';
- 
-function mimeToLabel(mime: string | null, filename: string): string {
-  Iif (mime) {
-    const sub = mime.split('/')[1];
-    Iif (sub === 'pdf') return 'PDF';
-    Iif (sub === 'vnd.openxmlformats-officedocument.presentationml.presentation') return 'PPTX';
-    Iif (sub === 'vnd.openxmlformats-officedocument.wordprocessingml.document') return 'DOCX';
-    Iif (sub === 'plain') return 'TXT';
-    Iif (sub) return sub.toUpperCase();
-  }
-  const ext = filename.split('.').pop()?.toUpperCase();
-  return ext || 'FILE';
-}
- 
-const TERMINAL_STATUSES: ReadonlySet<DocumentStatus> = new Set(['ready', 'error']);
- 
-export function toDocumentListRow(item: ApiDocumentListItem): DocumentListRow {
-  return {
-    id: item.id,
-    courseId: item.course_id,
-    filename: item.filename,
-    fileType: mimeToLabel(item.mime_type, item.filename),
-    size: item.size,
-    status: item.status,
-    processingStage: item.processing_stage,
-    isTerminal: TERMINAL_STATUSES.has(item.status),
-    errorMessage: item.error_message,
-    documentType: item.document_type ?? 'lecture',
-    createdAt: item.created_at,
-    updatedAt: item.updated_at,
-  };
-}
- 
-export function toDocumentDetailView(item: ApiDocumentDetail): DocumentDetailView {
-  let normalizedMetadata: Record<string, unknown> | null = null;
-  Iif (item.metadata_json) {
-    try {
-      normalizedMetadata = JSON.parse(item.metadata_json);
-    } catch {
-      normalizedMetadata = null;
-    }
-  }
- 
-  return {
-    id: item.id,
-    courseId: item.course_id,
-    filename: item.filename,
-    fileType: mimeToLabel(item.mime_type, item.filename),
-    size: item.size,
-    status: item.status,
-    processingStage: item.processing_stage,
-    errorMessage: item.error_message,
-    documentType: item.document_type ?? 'lecture',
-    parserUsed: item.parser_used,
-    pageCount: item.page_count,
-    chunkCount: item.chunk_count,
-    indexingStatus: item.indexing_status,
-    normalizedMetadata,
-    createdAt: item.created_at,
-    updatedAt: item.updated_at,
-  };
-}
- 
-export function toDocumentPreviewView(item: ApiDocumentPreview): DocumentPreviewView {
-  return {
-    id: item.id,
-    filename: item.filename,
-    extractedTextPreview: item.extracted_text_preview,
-    pageCount: item.page_count,
-    sections: item.sections,
-    sampleChunks: item.sample_chunks,
-  };
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api/courses.ts.html b/apps/web/coverage/lcov-report/src/lib/api/courses.ts.html deleted file mode 100644 index f0acb9b..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api/courses.ts.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - Code coverage report for src/lib/api/courses.ts - - - - - - - - - -
-
-

All files / src/lib/api courses.ts

-
- -
- 0% - Statements - 0/9 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/4 -
- - -
- 0% - Lines - 0/9 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
-import type { ApiCourse, ApiLesson, CreateCourseRequest } from './types';
- 
-export async function fetchCourses(
-  skip = 0,
-  limit = 100,
-  accessToken?: string
-): Promise<ApiCourse[]> {
-  return apiFetch<ApiCourse[]>(`/courses?skip=${skip}&limit=${limit}`, {
-    accessToken,
-  });
-}
- 
-export async function fetchCourse(courseId: string, accessToken?: string): Promise<ApiCourse> {
-  return apiFetch<ApiCourse>(`/courses/${courseId}`, { accessToken });
-}
- 
-export async function fetchCourseLessons(
-  courseId: string,
-  accessToken?: string
-): Promise<ApiLesson[]> {
-  return apiFetch<ApiLesson[]>(`/courses/${courseId}/lessons`, { accessToken });
-}
- 
-export async function createCourse(
-  data: CreateCourseRequest,
-  accessToken?: string
-): Promise<ApiCourse> {
-  return apiFetch<ApiCourse>('/courses', {
-    method: 'POST',
-    body: data,
-    accessToken,
-  });
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api/documents.ts.html b/apps/web/coverage/lcov-report/src/lib/api/documents.ts.html deleted file mode 100644 index b8a187b..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api/documents.ts.html +++ /dev/null @@ -1,652 +0,0 @@ - - - - - - Code coverage report for src/lib/api/documents.ts - - - - - - - - - -
-
-

All files / src/lib/api documents.ts

-
- -
- 0% - Statements - 0/69 -
- - -
- 0% - Branches - 0/21 -
- - -
- 0% - Functions - 0/18 -
- - -
- 0% - Lines - 0/62 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch, ApiError } from '@/lib/api';
- 
-const _API_BASE =
-  process.env.NEXT_PUBLIC_API_BASE_URL ||
-  (process.env.NEXT_PUBLIC_API_URL
-    ? `${process.env.NEXT_PUBLIC_API_URL}/api`
-    : 'http://localhost:8000/api');
-import type {
-  ApiDocumentListItem,
-  ApiDocumentDetail,
-  ApiDocumentPreview,
-  ApiDocumentStatusResponse,
-  DocumentListRow,
-  DocumentDetailView,
-  DocumentPreviewView,
-} from './types';
-import { toDocumentListRow, toDocumentDetailView, toDocumentPreviewView } from './adapters';
- 
-// ── Raw API calls (wire types) ──────────────────────────────────────────
- 
-export async function fetchDocumentsList(
-  courseId: string,
-  accessToken?: string
-): Promise<ApiDocumentListItem[]> {
-  return apiFetch<ApiDocumentListItem[]>(`/courses/${courseId}/documents`, {
-    accessToken,
-  });
-}
- 
-export async function fetchDocumentStatus(
-  documentId: string,
-  accessToken?: string
-): Promise<ApiDocumentStatusResponse> {
-  return apiFetch<ApiDocumentStatusResponse>(`/documents/${documentId}/status`, {
-    accessToken,
-  });
-}
- 
-export async function fetchDocumentDetail(
-  documentId: string,
-  accessToken?: string
-): Promise<ApiDocumentDetail> {
-  return apiFetch<ApiDocumentDetail>(`/documents/${documentId}`, {
-    accessToken,
-  });
-}
- 
-export async function fetchDocumentPreview(
-  documentId: string,
-  accessToken?: string
-): Promise<ApiDocumentPreview> {
-  return apiFetch<ApiDocumentPreview>(`/documents/${documentId}/preview`, {
-    accessToken,
-  });
-}
- 
-export async function uploadDocument(
-  courseId: string,
-  file: File,
-  accessToken?: string,
-  documentType = 'lecture'
-): Promise<ApiDocumentListItem> {
-  const formData = new FormData();
-  formData.append('file', file);
-  formData.append('document_type', documentType);
-  return apiFetch<ApiDocumentListItem>(`/courses/${courseId}/documents`, {
-    method: 'POST',
-    body: formData,
-    accessToken,
-  });
-}
- 
-export async function retryDocument(
-  documentId: string,
-  accessToken?: string
-): Promise<ApiDocumentStatusResponse> {
-  return apiFetch<ApiDocumentStatusResponse>(`/documents/${documentId}/retry`, {
-    method: 'POST',
-    accessToken,
-  });
-}
- 
-export async function deleteDocument(
-  documentId: string,
-  accessToken?: string
-): Promise<{ message: string }> {
-  return apiFetch<{ message: string }>(`/documents/${documentId}`, {
-    method: 'DELETE',
-    accessToken,
-  });
-}
- 
-export function uploadDocumentWithProgress(
-  courseId: string,
-  file: File,
-  accessToken: string | undefined,
-  documentType: string,
-  onProgress: (pct: number) => void
-): Promise<ApiDocumentListItem> {
-  return new Promise((resolve, reject) => {
-    const formData = new FormData();
-    formData.append('file', file);
-    formData.append('document_type', documentType);
- 
-    const xhr = new XMLHttpRequest();
-    xhr.open('POST', `${_API_BASE}/courses/${courseId}/documents`);
-    Iif (accessToken) xhr.setRequestHeader('Authorization', `Bearer ${accessToken}`);
- 
-    xhr.upload.addEventListener('progress', (e) => {
-      Iif (e.lengthComputable) onProgress(Math.round((e.loaded / e.total) * 100));
-    });
- 
-    xhr.addEventListener('load', () => {
-      if (xhr.status >= 200 && xhr.status < 300) {
-        try {
-          resolve(JSON.parse(xhr.responseText) as ApiDocumentListItem);
-        } catch {
-          reject(new Error('Invalid server response'));
-        }
-      } else {
-        let message = `Upload failed (${xhr.status})`;
-        try {
-          const body = JSON.parse(xhr.responseText) as { detail?: string };
-          Iif (body.detail) message = body.detail;
-        } catch {
-          /* use default message */
-        }
-        reject(new Error(message));
-      }
-    });
- 
-    xhr.addEventListener('error', () => reject(new Error('Network error during upload')));
-    xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));
- 
-    xhr.send(formData);
-  });
-}
- 
-// ── Adapted calls (UI types) ───────────────────────────────────────────
- 
-export async function getDocumentListRows(
-  courseId: string,
-  accessToken?: string
-): Promise<DocumentListRow[]> {
-  const items = await fetchDocumentsList(courseId, accessToken);
-  return items.map(toDocumentListRow);
-}
- 
-export async function getDocumentDetailView(
-  documentId: string,
-  accessToken?: string
-): Promise<DocumentDetailView> {
-  const item = await fetchDocumentDetail(documentId, accessToken);
-  return toDocumentDetailView(item);
-}
- 
-export async function getDocumentPreviewView(
-  documentId: string,
-  accessToken?: string
-): Promise<DocumentPreviewView> {
-  const item = await fetchDocumentPreview(documentId, accessToken);
-  return toDocumentPreviewView(item);
-}
- 
-// ── Polling helper ──────────────────────────────────────────────────────
- 
-export async function pollDocumentUntilTerminal(
-  documentId: string,
-  accessToken?: string,
-  intervalMs = 3000,
-  maxAttempts = 60
-): Promise<ApiDocumentStatusResponse> {
-  for (let i = 0; i < maxAttempts; i++) {
-    try {
-      const status = await fetchDocumentStatus(documentId, accessToken);
-      Iif (status.status === 'ready' || status.status === 'error') {
-        return status;
-      }
-    } catch (err) {
-      if (err instanceof ApiError && err.status >= 500) {
-        // transient — keep polling
-      } else {
-        throw err;
-      }
-    }
-    await new Promise((resolve) => setTimeout(resolve, intervalMs));
-  }
-  throw new Error('Document processing timed out');
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api/index.html b/apps/web/coverage/lcov-report/src/lib/api/index.html deleted file mode 100644 index 42c3bae..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - Code coverage report for src/lib/api - - - - - - - - - -
-
-

All files src/lib/api

-
- -
- 0% - Statements - 0/108 -
- - -
- 0% - Branches - 0/36 -
- - -
- 0% - Functions - 0/26 -
- - -
- 0% - Lines - 0/96 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
adapters.ts -
-
0%0/260%0/130%0/40%0/21
courses.ts -
-
0%0/90%0/20%0/40%0/9
documents.ts -
-
0%0/690%0/210%0/180%0/62
index.ts -
-
0%0/4100%0/0100%0/00%0/4
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/api/index.ts.html b/apps/web/coverage/lcov-report/src/lib/api/index.ts.html deleted file mode 100644 index 96f64ca..0000000 --- a/apps/web/coverage/lcov-report/src/lib/api/index.ts.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - Code coverage report for src/lib/api/index.ts - - - - - - - - - -
-
-

All files / src/lib/api index.ts

-
- -
- 0% - Statements - 0/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5  -  -  -  - 
export * from './types';
-export * from './adapters';
-export * from './documents';
-export * from './courses';
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/auth/config.ts.html b/apps/web/coverage/lcov-report/src/lib/auth/config.ts.html deleted file mode 100644 index d5bafd9..0000000 --- a/apps/web/coverage/lcov-report/src/lib/auth/config.ts.html +++ /dev/null @@ -1,436 +0,0 @@ - - - - - - Code coverage report for src/lib/auth/config.ts - - - - - - - - - -
-
-

All files / src/lib/auth config.ts

-
- -
- 0% - Statements - 0/37 -
- - -
- 0% - Branches - 0/30 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/33 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import type { AuthOptions } from 'next-auth';
-import type { JWT } from 'next-auth/jwt';
-import CredentialsProvider from 'next-auth/providers/credentials';
- 
-const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-const ACCESS_TOKEN_TTL_MS = 30 * 60 * 1000; // 30 min — must match backend ACCESS_TOKEN_EXPIRE_MINUTES
-const REFRESH_BUFFER_MS = 60 * 1000; // refresh 1 min before expiry
- 
-async function refreshAccessToken(token: JWT): Promise<JWT> {
-  try {
-    const res = await fetch(`${API_URL}/api/auth/refresh`, {
-      method: 'POST',
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ refresh_token: token.refreshToken }),
-    });
- 
-    const data = await res.json();
-    Iif (!res.ok) throw data;
- 
-    return {
-      ...token,
-      accessToken: data.access_token,
-      refreshToken: data.refresh_token ?? token.refreshToken,
-      accessTokenExpires: Date.now() + ACCESS_TOKEN_TTL_MS,
-      error: undefined,
-    };
-  } catch {
-    return { ...token, error: 'RefreshAccessTokenError' };
-  }
-}
- 
-export const authOptions: AuthOptions = {
-  providers: [
-    CredentialsProvider({
-      name: 'credentials',
-      credentials: {
-        email: { label: 'Email', type: 'email' },
-        password: { label: 'Password', type: 'password' },
-      },
-      async authorize(credentials) {
-        Iif (!credentials?.email || !credentials?.password) {
-          throw new Error('Email and password are required');
-        }
- 
-        try {
-          const res = await fetch(`${API_URL}/api/auth/login`, {
-            method: 'POST',
-            headers: { 'Content-Type': 'application/json' },
-            body: JSON.stringify({
-              email: credentials.email,
-              password: credentials.password,
-            }),
-          });
- 
-          Iif (!res.ok) {
-            const error = await res.json().catch(() => ({}));
-            throw new Error(error.detail || 'Invalid email or password');
-          }
- 
-          const data = await res.json();
- 
-          return {
-            id: data.user?.id || data.id,
-            email: data.user?.email || credentials.email,
-            name: data.user?.display_name || null,
-            accessToken: data.access_token || data.tokens?.access_token,
-            refreshToken: data.refresh_token || data.tokens?.refresh_token,
-            role: data.user?.role || 'student',
-            displayName: data.user?.display_name || null,
-          };
-        } catch (error) {
-          Iif (error instanceof Error) throw error;
-          throw new Error('Authentication failed');
-        }
-      },
-    }),
-  ],
-  callbacks: {
-    async jwt({ token, user }) {
-      Iif (user) {
-        return {
-          ...token,
-          accessToken: user.accessToken,
-          refreshToken: user.refreshToken,
-          role: user.role,
-          displayName: user.displayName,
-          accessTokenExpires: Date.now() + ACCESS_TOKEN_TTL_MS,
-        };
-      }
- 
-      // Token still valid
-      Iif (Date.now() < (token.accessTokenExpires ?? 0) - REFRESH_BUFFER_MS) {
-        return token;
-      }
- 
-      // Token expired — attempt refresh
-      return refreshAccessToken(token);
-    },
-    async session({ session, token }) {
-      session.user.id = token.sub;
-      session.user.accessToken = token.accessToken;
-      session.user.role = token.role;
-      session.user.displayName = token.displayName;
-      session.error = token.error;
-      return session;
-    },
-  },
-  pages: {
-    signIn: '/login',
-    error: '/login',
-  },
-  session: {
-    strategy: 'jwt',
-    maxAge: 7 * 24 * 60 * 60, // 7 days — refresh token lifetime
-  },
-  secret: process.env.NEXTAUTH_SECRET,
-};
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/auth/index.html b/apps/web/coverage/lcov-report/src/lib/auth/index.html deleted file mode 100644 index ae8988c..0000000 --- a/apps/web/coverage/lcov-report/src/lib/auth/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/lib/auth - - - - - - - - - -
-
-

All files src/lib/auth

-
- -
- 0% - Statements - 0/37 -
- - -
- 0% - Branches - 0/30 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/33 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
config.ts -
-
0%0/370%0/300%0/50%0/33
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/index.html b/apps/web/coverage/lcov-report/src/lib/index.html deleted file mode 100644 index 72aed3f..0000000 --- a/apps/web/coverage/lcov-report/src/lib/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/lib - - - - - - - - - -
-
-

All files src/lib

-
- -
- 5% - Statements - 1/20 -
- - -
- 17.64% - Branches - 3/17 -
- - -
- 0% - Functions - 0/4 -
- - -
- 5.26% - Lines - 1/19 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
api.ts -
-
5%1/2017.64%3/170%0/45.26%1/19
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/middleware/auth-guard.ts.html b/apps/web/coverage/lcov-report/src/lib/middleware/auth-guard.ts.html deleted file mode 100644 index 5a9983e..0000000 --- a/apps/web/coverage/lcov-report/src/lib/middleware/auth-guard.ts.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - Code coverage report for src/lib/middleware/auth-guard.ts - - - - - - - - - -
-
-

All files / src/lib/middleware auth-guard.ts

-
- -
- 0% - Statements - 0/17 -
- - -
- 0% - Branches - 0/7 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/16 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { NextResponse } from 'next/server';
-import { getToken } from 'next-auth/jwt';
-import type { NextRequest } from 'next/server';
- 
-const PUBLIC_PATHS = ['/login', '/signup', '/api/auth'];
- 
-export async function authMiddleware(request: NextRequest) {
-  const { pathname } = request.nextUrl;
- 
-  const isPublic = PUBLIC_PATHS.some(
-    (path) => pathname === path || pathname.startsWith(path + '/')
-  );
- 
-  Iif (isPublic || pathname.startsWith('/_next') || pathname.startsWith('/favicon')) {
-    return NextResponse.next();
-  }
- 
-  const token = await getToken({
-    req: request,
-    secret: process.env.NEXTAUTH_SECRET,
-  });
- 
-  Iif (!token) {
-    const loginUrl = new URL('/login', request.url);
-    loginUrl.searchParams.set('callbackUrl', pathname);
-    return NextResponse.redirect(loginUrl);
-  }
- 
-  return NextResponse.next();
-}
- 
-export const config = {
-  matcher: ['/((?!_next/static|_next/image|favicon.ico|public/).*)'],
-};
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/middleware/index.html b/apps/web/coverage/lcov-report/src/lib/middleware/index.html deleted file mode 100644 index dab696e..0000000 --- a/apps/web/coverage/lcov-report/src/lib/middleware/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/lib/middleware - - - - - - - - - -
-
-

All files src/lib/middleware

-
- -
- 0% - Statements - 0/17 -
- - -
- 0% - Branches - 0/7 -
- - -
- 0% - Functions - 0/2 -
- - -
- 0% - Lines - 0/16 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
auth-guard.ts -
-
0%0/170%0/70%0/20%0/16
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/chat.ts.html b/apps/web/coverage/lcov-report/src/lib/services/chat.ts.html deleted file mode 100644 index b386bd2..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/chat.ts.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - Code coverage report for src/lib/services/chat.ts - - - - - - - - - -
-
-

All files / src/lib/services chat.ts

-
- -
- 0% - Statements - 0/4 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
- 
-export interface Citation {
-  snippet: string;
-  score: number;
-}
- 
-export interface ChatResponse {
-  answer: string;
-  citations: Citation[];
-  retrievalUsed: boolean;
-}
- 
-export async function sendChatMessage(
-  courseId: string,
-  message: string,
-  accessToken?: string
-): Promise<ChatResponse> {
-  const raw = await apiFetch<Record<string, unknown>>(`/courses/${courseId}/chat`, {
-    method: 'POST',
-    body: { message },
-    accessToken,
-  });
-  return {
-    answer: raw.answer as string,
-    citations: (raw.citations as Citation[]) ?? [],
-    retrievalUsed: raw.retrieval_used as boolean,
-  };
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html b/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html deleted file mode 100644 index b3fb810..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/courses.ts.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - Code coverage report for src/lib/services/courses.ts - - - - - - - - - -
-
-

All files / src/lib/services courses.ts

-
- -
- 0% - Statements - 0/13 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/12 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
- 
-// MVP limit: pagination not implemented. Acceptable for expected scale (5–10 courses).
-// When real pagination is needed, replace getCourses calls with a paginated version.
-export const MVP_COURSES_LIMIT = 100;
- 
-export interface Course {
-  id: number;
-  title: string;
-  description?: string;
-}
- 
-export interface Lesson {
-  id: number;
-  title: string;
-  content?: string;
-}
- 
-export interface CourseWithLessons extends Course {
-  lessons: Lesson[];
-}
- 
-export async function getCourses(skip = 0, limit = 100, accessToken?: string): Promise<Course[]> {
-  return apiFetch<Course[]>(`/courses?skip=${skip}&limit=${limit}`, {
-    accessToken,
-  });
-}
- 
-export async function getCourse(courseId: string, accessToken?: string): Promise<Course> {
-  return apiFetch<Course>(`/courses/${courseId}`, { accessToken });
-}
- 
-export async function getCourseLessons(courseId: string, accessToken?: string): Promise<Lesson[]> {
-  return apiFetch<Lesson[]>(`/courses/${courseId}/lessons`, { accessToken });
-}
- 
-export async function getLesson(lessonId: string, accessToken?: string): Promise<Lesson> {
-  return apiFetch<Lesson>(`/lessons/${lessonId}`, { accessToken });
-}
- 
-export async function createCourse(title: string, description?: string): Promise<Course> {
-  return apiFetch<Course>('/courses', {
-    method: 'POST',
-    body: { title, description },
-  });
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/debug.ts.html b/apps/web/coverage/lcov-report/src/lib/services/debug.ts.html deleted file mode 100644 index 204cacb..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/debug.ts.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Code coverage report for src/lib/services/debug.ts - - - - - - - - - -
-
-

All files / src/lib/services debug.ts

-
- -
- 0% - Statements - 0/13 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/5 -
- - -
- 0% - Lines - 0/13 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
-import type { EvidencePack } from '@/lib/api/types';
- 
-export interface PipelineHealth {
-  chroma_status: string;
-  collection_sizes: Record<string, number>;
-  uploads_dir_size_mb: number;
-  python_version: string;
-  chroma_db_path: string;
-}
- 
-export async function getPipelineHealth(accessToken?: string): Promise<PipelineHealth> {
-  return apiFetch<PipelineHealth>('/debug/pipeline/health', { accessToken });
-}
- 
-export async function getDocumentChunks(
-  docId: string,
-  accessToken?: string
-): Promise<Array<Record<string, unknown>>> {
-  return apiFetch<Array<Record<string, unknown>>>(`/debug/documents/${docId}/chunks`, {
-    accessToken,
-  });
-}
- 
-export async function getParsedOutput(
-  docId: string,
-  accessToken?: string
-): Promise<Record<string, unknown>> {
-  return apiFetch<Record<string, unknown>>(`/debug/documents/${docId}/parsed`, { accessToken });
-}
- 
-export async function testRetrieval(
-  courseId: string,
-  query: string,
-  topK = 5,
-  accessToken?: string
-): Promise<{
-  query: string;
-  course_id: string;
-  total: number;
-  chunks: Array<Record<string, unknown>>;
-}> {
-  const params = new URLSearchParams({ query, top_k: String(topK) });
-  return apiFetch(`/debug/courses/${courseId}/retrieval?${params}`, {
-    method: 'POST',
-    accessToken,
-  });
-}
- 
-export async function getEvidencePack(
-  courseId: string,
-  query: string,
-  action = 'explain',
-  accessToken?: string
-): Promise<EvidencePack> {
-  const params = new URLSearchParams({ query, action });
-  return apiFetch<EvidencePack>(`/debug/courses/${courseId}/evidence?${params}`, { accessToken });
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html b/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html deleted file mode 100644 index 62f11c4..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/documents.ts.html +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - Code coverage report for src/lib/services/documents.ts - - - - - - - - - -
-
-

All files / src/lib/services documents.ts

-
- -
- 0% - Statements - 0/23 -
- - -
- 0% - Branches - 0/9 -
- - -
- 0% - Functions - 0/6 -
- - -
- 0% - Lines - 0/21 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch, ApiError } from '@/lib/api';
- 
-export type DocumentStatus = 'uploading' | 'processing' | 'ready' | 'error';
- 
-export interface CourseDocument {
-  id: string;
-  filename: string;
-  size: number;
-  status: DocumentStatus;
-  error_message?: string;
-  created_at: string;
-}
- 
-export interface DocumentStatusResponse {
-  id: string;
-  status: DocumentStatus;
-  progress?: number;
-  error_message?: string;
-}
- 
-export async function getDocuments(
-  courseId: string,
-  accessToken?: string
-): Promise<CourseDocument[]> {
-  return apiFetch<CourseDocument[]>(`/courses/${courseId}/documents`, {
-    accessToken,
-  });
-}
- 
-export async function uploadDocument(
-  courseId: string,
-  file: File,
-  accessToken?: string
-): Promise<CourseDocument> {
-  const formData = new FormData();
-  formData.append('file', file);
- 
-  return apiFetch<CourseDocument>(`/courses/${courseId}/documents`, {
-    method: 'POST',
-    body: formData,
-    accessToken,
-  });
-}
- 
-export async function getDocumentStatus(
-  documentId: string,
-  accessToken?: string
-): Promise<DocumentStatusResponse> {
-  return apiFetch<DocumentStatusResponse>(`/documents/${documentId}/status`, {
-    accessToken,
-  });
-}
- 
-export async function deleteDocument(
-  documentId: string,
-  accessToken?: string
-): Promise<{ message: string }> {
-  return apiFetch<{ message: string }>(`/documents/${documentId}`, {
-    method: 'DELETE',
-    accessToken,
-  });
-}
- 
-/**
- * Poll a document's status until it reaches a terminal state.
- * Returns the final status, or throws if polling times out.
- */
-export async function pollDocumentStatus(
-  documentId: string,
-  accessToken?: string,
-  intervalMs = 3000,
-  maxAttempts = 60
-): Promise<DocumentStatusResponse> {
-  for (let i = 0; i < maxAttempts; i++) {
-    try {
-      const status = await getDocumentStatus(documentId, accessToken);
-      Iif (status.status === 'ready' || status.status === 'error') {
-        return status;
-      }
-    } catch (err) {
-      if (err instanceof ApiError && err.status >= 500) {
-        // Server error — keep polling
-      } else {
-        throw err;
-      }
-    }
-    await new Promise((resolve) => setTimeout(resolve, intervalMs));
-  }
-  throw new Error('Document processing timed out');
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/index.html b/apps/web/coverage/lcov-report/src/lib/services/index.html deleted file mode 100644 index f6fa4fe..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/index.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - Code coverage report for src/lib/services - - - - - - - - - -
-
-

All files src/lib/services

-
- -
- 1.36% - Statements - 1/73 -
- - -
- 0% - Branches - 0/19 -
- - -
- 0% - Functions - 0/24 -
- - -
- 1.44% - Lines - 1/69 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
chat.ts -
-
0%0/40%0/20%0/10%0/4
courses.ts -
-
0%0/130%0/20%0/50%0/12
debug.ts -
-
0%0/130%0/20%0/50%0/13
documents.ts -
-
0%0/230%0/90%0/60%0/21
progress.ts -
-
0%0/160%0/40%0/60%0/15
study.ts -
-
25%1/4100%0/00%0/125%1/4
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/progress.ts.html b/apps/web/coverage/lcov-report/src/lib/services/progress.ts.html deleted file mode 100644 index 22fb978..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/progress.ts.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - Code coverage report for src/lib/services/progress.ts - - - - - - - - - -
-
-

All files / src/lib/services progress.ts

-
- -
- 0% - Statements - 0/16 -
- - -
- 0% - Branches - 0/4 -
- - -
- 0% - Functions - 0/6 -
- - -
- 0% - Lines - 0/15 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
- 
-export interface CourseProgress {
-  courseId: string;
-  percent: number;
-  status: string;
-  lessonCount: number;
-  completedCount: number;
-  updatedAt: string;
-  completedLessonIds: string[];
-}
- 
-export interface Badge {
-  id: string;
-  slug: string;
-  name: string;
-  description: string;
-  icon: string;
-}
- 
-export interface UserBadge {
-  id: string;
-  badge: Badge;
-  earnedAt: string;
-  contextId: string | null;
-}
- 
-export interface ProgressUpdateResult {
-  lessonProgress: {
-    lessonId: string;
-    status: 'not_started' | 'in_progress' | 'completed';
-    lastScore: number | null;
-  };
-  courseProgress: CourseProgress;
-  newBadges: Badge[];
-}
- 
-function mapProgress(raw: Record<string, unknown>): CourseProgress {
-  return {
-    courseId: raw.course_id as string,
-    percent: raw.percent as number,
-    status: raw.status as string,
-    lessonCount: raw.lesson_count as number,
-    completedCount: raw.completed_count as number,
-    updatedAt: raw.updated_at as string,
-    completedLessonIds: (raw.completed_lesson_ids as string[]) ?? [],
-  };
-}
- 
-function mapBadge(raw: Record<string, unknown>): Badge {
-  return {
-    id: raw.id as string,
-    slug: raw.slug as string,
-    name: raw.name as string,
-    description: raw.description as string,
-    icon: raw.icon as string,
-  };
-}
- 
-export async function getCourseProgress(
-  courseId: string,
-  accessToken?: string
-): Promise<CourseProgress> {
-  const raw = await apiFetch<Record<string, unknown>>(`/progress/${courseId}`, { accessToken });
-  return mapProgress(raw);
-}
- 
-export async function markLessonComplete(
-  lessonId: string,
-  courseId: string,
-  accessToken?: string
-): Promise<ProgressUpdateResult> {
-  const raw = await apiFetch<Record<string, unknown>>('/progress/update', {
-    method: 'POST',
-    body: { lesson_id: lessonId, course_id: courseId, status: 'completed' },
-    accessToken,
-  });
- 
-  const lp = raw.lesson_progress as Record<string, unknown>;
-  const cp = raw.course_progress as Record<string, unknown>;
-  const nb = (raw.new_badges as Record<string, unknown>[]) ?? [];
- 
-  return {
-    lessonProgress: {
-      lessonId: lp.lesson_id as string,
-      status: lp.status as 'not_started' | 'in_progress' | 'completed',
-      lastScore: lp.last_score as number | null,
-    },
-    courseProgress: mapProgress(cp),
-    newBadges: nb.map(mapBadge),
-  };
-}
- 
-export async function getUserBadges(accessToken?: string): Promise<UserBadge[]> {
-  const raw = await apiFetch<Record<string, unknown>[]>('/badges/user', { accessToken });
-  return raw.map((item) => ({
-    id: item.id as string,
-    badge: mapBadge(item.badge as Record<string, unknown>),
-    earnedAt: item.earned_at as string,
-    contextId: item.context_id as string | null,
-  }));
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/lib/services/study.ts.html b/apps/web/coverage/lcov-report/src/lib/services/study.ts.html deleted file mode 100644 index 521a449..0000000 --- a/apps/web/coverage/lcov-report/src/lib/services/study.ts.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - Code coverage report for src/lib/services/study.ts - - - - - - - - - -
-
-

All files / src/lib/services study.ts

-
- -
- 25% - Statements - 1/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 25% - Lines - 1/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -192x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { apiFetch } from '@/lib/api';
-import type { ApiStudyRequest, ApiStudyResponse, StudyAction } from '@/lib/api/types';
- 
-export type { StudyAction, ApiStudyResponse as StudyResponse };
- 
-export async function sendStudyAction(
-  courseId: string,
-  action: StudyAction,
-  query: string,
-  accessToken?: string
-): Promise<ApiStudyResponse> {
-  const body: ApiStudyRequest = { action, query };
-  return apiFetch<ApiStudyResponse>(`/courses/${courseId}/study`, {
-    method: 'POST',
-    body,
-    accessToken,
-  });
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov-report/src/middleware.ts.html b/apps/web/coverage/lcov-report/src/middleware.ts.html deleted file mode 100644 index 006350e..0000000 --- a/apps/web/coverage/lcov-report/src/middleware.ts.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - Code coverage report for src/middleware.ts - - - - - - - - - -
-
-

All files / src middleware.ts

-
- -
- 0% - Statements - 0/3 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 0% - Lines - 0/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6  -  -  -  -  - 
/**
- * Next.js Middleware
- * Handles authentication routing and protection
- */
-export { authMiddleware as default, config } from '@/lib/middleware/auth-guard';
- -
-
- - - - - - - - \ No newline at end of file diff --git a/apps/web/coverage/lcov.info b/apps/web/coverage/lcov.info deleted file mode 100644 index f5d46bd..0000000 --- a/apps/web/coverage/lcov.info +++ /dev/null @@ -1,3372 +0,0 @@ -TN: -SF:src/middleware.ts -FNF:0 -FNH:0 -DA:5,0 -LF:1 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/error.tsx -FN:5,GlobalError -FN:12,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,GlobalError -FNDA:0,(anonymous_2) -DA:3,0 -DA:5,0 -DA:12,0 -DA:13,0 -LF:4 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/layout.tsx -FN:16,RootLayout -FNF:1 -FNH:0 -FNDA:0,RootLayout -DA:2,0 -DA:3,0 -DA:4,0 -DA:6,0 -DA:11,0 -DA:16,0 -LF:6 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/page.tsx -FN:7,Home -FN:11,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,Home -FNDA:0,(anonymous_2) -DA:3,0 -DA:4,0 -DA:5,0 -DA:7,0 -DA:8,0 -DA:9,0 -DA:11,0 -DA:12,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:19,0 -DA:29,0 -LF:13 -LH:0 -BRDA:12,0,0,0 -BRDA:12,0,1,0 -BRDA:14,1,0,0 -BRDA:19,2,0,0 -BRDA:19,3,0,0 -BRDA:19,3,1,0 -BRF:6 -BRH:0 -end_of_record -TN: -SF:src/app/(auth)/layout.tsx -FN:8,AuthLayout -FNF:1 -FNH:0 -FNDA:0,AuthLayout -DA:2,0 -DA:8,0 -LF:2 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/(auth)/login/page.tsx -FN:23,LoginForm -FN:38,(anonymous_3) -FN:52,(anonymous_4) -FN:118,(anonymous_5) -FN:148,(anonymous_6) -FN:221,LoginPage -FNF:6 -FNH:6 -FNDA:327,LoginForm -FNDA:15,(anonymous_3) -FNDA:15,(anonymous_4) -FNDA:179,(anonymous_5) -FNDA:106,(anonymous_6) -FNDA:20,LoginPage -DA:3,2 -DA:4,2 -DA:5,2 -DA:6,2 -DA:15,2 -DA:17,2 -DA:18,2 -DA:21,2 -DA:24,327 -DA:25,327 -DA:26,327 -DA:27,327 -DA:28,327 -DA:30,327 -DA:31,325 -DA:32,325 -DA:33,325 -DA:36,325 -DA:38,325 -DA:39,15 -DA:40,15 -DA:41,3 -DA:42,12 -DA:43,2 -DA:45,15 -DA:46,6 -DA:48,15 -DA:49,15 -DA:52,325 -DA:53,15 -DA:54,15 -DA:55,15 -DA:56,9 -DA:57,9 -DA:58,9 -DA:64,8 -DA:66,2 -DA:69,2 -DA:70,6 -DA:71,6 -DA:72,6 -DA:75,0 -DA:77,8 -DA:118,179 -DA:148,106 -DA:221,2 -LF:46 -LH:45 -BRDA:26,0,0,327 -BRDA:26,0,1,262 -BRDA:36,1,0,36 -BRDA:36,1,1,289 -BRDA:40,2,0,3 -BRDA:40,2,1,12 -BRDA:42,3,0,2 -BRDA:45,4,0,6 -BRDA:55,5,0,6 -BRDA:64,6,0,2 -BRDA:64,6,1,6 -BRDA:66,7,0,0 -BRDA:66,7,1,2 -BRDA:70,8,0,6 -BRDA:87,9,0,325 -BRDA:93,10,0,325 -BRDA:99,11,0,325 -BRDA:119,12,0,6 -BRDA:119,12,1,319 -BRDA:121,13,0,6 -BRDA:121,13,1,319 -BRDA:122,14,0,6 -BRDA:122,14,1,319 -BRDA:125,15,0,325 -BRDA:149,16,0,7 -BRDA:149,16,1,318 -BRDA:151,17,0,7 -BRDA:151,17,1,318 -BRDA:152,18,0,7 -BRDA:152,18,1,318 -BRDA:155,19,0,325 -BRDA:168,20,0,9 -BRDA:168,20,1,316 -BRF:33 -BRH:32 -end_of_record -TN: -SF:src/app/(auth)/signup/page.tsx -FN:27,SignupPage -FN:37,(anonymous_3) -FN:74,(anonymous_4) -FN:91,(anonymous_5) -FN:154,(anonymous_6) -FN:184,(anonymous_7) -FN:214,(anonymous_8) -FN:251,(anonymous_9) -FNF:8 -FNH:7 -FNDA:767,SignupPage -FNDA:18,(anonymous_3) -FNDA:18,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:17,(anonymous_6) -FNDA:281,(anonymous_7) -FNDA:217,(anonymous_8) -FNDA:209,(anonymous_9) -DA:3,2 -DA:4,2 -DA:5,2 -DA:6,2 -DA:8,2 -DA:19,2 -DA:21,2 -DA:22,2 -DA:25,2 -DA:27,2 -DA:28,767 -DA:30,767 -DA:31,765 -DA:32,765 -DA:33,765 -DA:34,765 -DA:35,765 -DA:37,765 -DA:38,18 -DA:40,18 -DA:41,1 -DA:42,17 -DA:43,1 -DA:46,18 -DA:47,0 -DA:48,18 -DA:49,1 -DA:50,17 -DA:51,1 -DA:52,16 -DA:53,1 -DA:54,15 -DA:55,1 -DA:56,14 -DA:57,14 -DA:60,18 -DA:61,1 -DA:62,17 -DA:63,1 -DA:66,18 -DA:67,0 -DA:70,18 -DA:71,18 -DA:74,765 -DA:75,18 -DA:76,18 -DA:77,18 -DA:78,0 -DA:79,0 -DA:80,0 -DA:86,0 -DA:87,0 -DA:90,0 -DA:91,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:99,0 -DA:101,0 -DA:104,0 -DA:111,0 -DA:112,0 -DA:113,0 -DA:114,0 -DA:115,0 -DA:118,0 -DA:120,0 -DA:154,17 -DA:184,281 -DA:214,217 -DA:251,209 -LF:72 -LH:49 -BRDA:8,0,0,2 -BRDA:8,0,1,2 -BRDA:40,1,0,1 -BRDA:40,1,1,17 -BRDA:42,2,0,1 -BRDA:46,3,0,0 -BRDA:46,3,1,18 -BRDA:48,4,0,1 -BRDA:48,4,1,17 -BRDA:50,5,0,1 -BRDA:50,5,1,16 -BRDA:52,6,0,1 -BRDA:52,6,1,15 -BRDA:54,7,0,1 -BRDA:54,7,1,14 -BRDA:56,8,0,14 -BRDA:60,9,0,1 -BRDA:60,9,1,17 -BRDA:62,10,0,1 -BRDA:66,11,0,0 -BRDA:66,12,0,18 -BRDA:66,12,1,2 -BRDA:77,13,0,18 -BRDA:83,14,0,0 -BRDA:83,14,1,0 -BRDA:86,15,0,0 -BRDA:90,16,0,0 -BRDA:90,16,1,0 -BRDA:94,17,0,0 -BRDA:94,17,1,0 -BRDA:96,18,0,0 -BRDA:96,18,1,0 -BRDA:96,19,0,0 -BRDA:96,19,1,0 -BRDA:97,20,0,0 -BRDA:97,20,1,0 -BRDA:99,21,0,0 -BRDA:99,21,1,0 -BRDA:111,22,0,0 -BRDA:111,22,1,0 -BRDA:113,23,0,0 -BRDA:130,24,0,765 -BRDA:155,25,0,0 -BRDA:155,25,1,765 -BRDA:157,26,0,0 -BRDA:157,26,1,765 -BRDA:158,27,0,0 -BRDA:158,27,1,765 -BRDA:161,28,0,765 -BRDA:185,29,0,2 -BRDA:185,29,1,763 -BRDA:187,30,0,2 -BRDA:187,30,1,763 -BRDA:188,31,0,2 -BRDA:188,31,1,763 -BRDA:191,32,0,765 -BRDA:215,33,0,18 -BRDA:215,33,1,747 -BRDA:217,34,0,18 -BRDA:217,34,1,747 -BRDA:218,35,0,18 -BRDA:218,35,1,747 -BRDA:222,36,0,18 -BRDA:252,37,0,2 -BRDA:252,37,1,763 -BRDA:254,38,0,2 -BRDA:254,38,1,763 -BRDA:255,39,0,2 -BRDA:255,39,1,763 -BRDA:258,40,0,765 -BRDA:271,41,0,0 -BRDA:271,41,1,765 -BRF:72 -BRH:48 -end_of_record -TN: -SF:src/app/api/auth/[...nextauth]/route.ts -FNF:0 -FNH:0 -DA:4,0 -DA:5,0 -DA:7,0 -DA:9,0 -LF:4 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/courses/error.tsx -FN:5,CoursesError -FN:12,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,CoursesError -FNDA:0,(anonymous_2) -DA:3,0 -DA:5,0 -DA:12,0 -DA:13,0 -LF:4 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/courses/layout.tsx -FN:4,CoursesLayout -FNF:1 -FNH:0 -FNDA:0,CoursesLayout -DA:1,0 -DA:2,0 -DA:4,0 -LF:3 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/courses/page.tsx -FN:20,CoursesPage -FN:31,(anonymous_2) -FN:32,onKey -FN:39,(anonymous_4) -FN:42,(anonymous_5) -FN:51,fetchAll -FN:57,(anonymous_7) -FN:64,(anonymous_8) -FN:69,(anonymous_9) -FN:70,(anonymous_10) -FN:72,(anonymous_11) -FN:91,handleCreate -FN:93,(anonymous_13) -FN:103,(anonymous_14) -FN:188,(anonymous_15) -FN:201,(anonymous_16) -FN:207,(anonymous_17) -FN:212,(anonymous_18) -FN:223,(anonymous_19) -FN:229,StatBox -FNF:20 -FNH:0 -FNDA:0,CoursesPage -FNDA:0,(anonymous_2) -FNDA:0,onKey -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,fetchAll -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,handleCreate -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,StatBox -DA:3,0 -DA:4,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:8,0 -DA:9,0 -DA:20,0 -DA:21,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:31,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:38,0 -DA:39,0 -DA:42,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:47,0 -DA:49,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:56,0 -DA:57,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:69,0 -DA:70,0 -DA:72,0 -DA:74,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:80,0 -DA:81,0 -DA:83,0 -DA:85,0 -DA:88,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:97,0 -DA:104,0 -DA:188,0 -DA:201,0 -DA:208,0 -DA:212,0 -DA:223,0 -LF:64 -LH:0 -BRDA:33,0,0,0 -BRDA:33,1,0,0 -BRDA:33,1,1,0 -BRDA:33,1,2,0 -BRDA:43,2,0,0 -BRDA:47,3,0,0 -BRDA:65,4,0,0 -BRDA:70,5,0,0 -BRDA:70,5,1,0 -BRDA:83,6,0,0 -BRDA:83,6,1,0 -BRDA:97,7,0,0 -BRDA:97,8,0,0 -BRDA:97,8,1,0 -BRDA:97,8,2,0 -BRDA:136,9,0,0 -BRDA:136,9,1,0 -BRDA:182,10,0,0 -BRDA:195,11,0,0 -BRDA:208,12,0,0 -BRDA:208,12,1,0 -BRDA:222,13,0,0 -BRDA:233,14,0,0 -BRDA:233,14,1,0 -BRDA:234,15,0,0 -BRDA:234,15,1,0 -BRF:26 -BRH:0 -end_of_record -TN: -SF:src/app/courses/[courseId]/layout.tsx -FN:1,CourseLayout -FNF:1 -FNH:0 -FNDA:0,CourseLayout -DA:1,0 -LF:1 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/courses/[courseId]/page.tsx -FN:22,formatSize -FN:37,Lifecycle -FN:42,(anonymous_3) -FN:73,CourseWorkspacePage -FN:89,(anonymous_5) -FN:89,(anonymous_6) -FN:91,(anonymous_7) -FN:92,load -FN:105,(anonymous_9) -FN:106,loadDocs -FN:147,(anonymous_11) -FN:157,(anonymous_12) -FN:159,(anonymous_13) -FN:161,(anonymous_14) -FN:163,(anonymous_15) -FN:171,(anonymous_16) -FN:201,(anonymous_17) -FN:208,(anonymous_18) -FN:239,(anonymous_19) -FN:242,(anonymous_20) -FN:268,(anonymous_21) -FN:277,(anonymous_22) -FN:282,(anonymous_23) -FN:283,(anonymous_24) -FN:337,(anonymous_25) -FN:346,(anonymous_26) -FN:354,(anonymous_27) -FN:381,Stat2 -FN:391,DocRow -FN:435,(anonymous_30) -FNF:30 -FNH:0 -FNDA:0,formatSize -FNDA:0,Lifecycle -FNDA:0,(anonymous_3) -FNDA:0,CourseWorkspacePage -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,load -FNDA:0,(anonymous_9) -FNDA:0,loadDocs -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,Stat2 -FNDA:0,DocRow -FNDA:0,(anonymous_30) -DA:3,0 -DA:4,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:8,0 -DA:9,0 -DA:11,0 -DA:15,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:28,0 -DA:35,0 -DA:38,0 -DA:39,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:73,0 -DA:74,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:87,0 -DA:89,0 -DA:91,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:97,0 -DA:99,0 -DA:102,0 -DA:105,0 -DA:107,0 -DA:108,0 -DA:109,0 -DA:110,0 -DA:111,0 -DA:113,0 -DA:115,0 -DA:118,0 -DA:121,0 -DA:138,0 -DA:147,0 -DA:157,0 -DA:158,0 -DA:159,0 -DA:161,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:166,0 -DA:167,0 -DA:168,0 -DA:171,0 -DA:202,0 -DA:208,0 -DA:240,0 -DA:242,0 -DA:269,0 -DA:278,0 -DA:282,0 -DA:283,0 -DA:338,0 -DA:347,0 -DA:354,0 -DA:382,0 -DA:402,0 -DA:403,0 -DA:436,0 -DA:437,0 -LF:80 -LH:0 -BRDA:23,0,0,0 -BRDA:24,1,0,0 -BRDA:39,2,0,0 -BRDA:39,2,1,0 -BRDA:43,3,0,0 -BRDA:43,3,1,0 -BRDA:44,4,0,0 -BRDA:44,4,1,0 -BRDA:49,5,0,0 -BRDA:49,5,1,0 -BRDA:51,6,0,0 -BRDA:51,6,1,0 -BRDA:58,7,0,0 -BRDA:64,8,0,0 -BRDA:97,9,0,0 -BRDA:97,9,1,0 -BRDA:102,10,0,0 -BRDA:111,11,0,0 -BRDA:111,12,0,0 -BRDA:111,12,1,0 -BRDA:118,13,0,0 -BRDA:121,14,0,0 -BRDA:138,15,0,0 -BRDA:138,16,0,0 -BRDA:138,16,1,0 -BRDA:145,17,0,0 -BRDA:145,17,1,0 -BRDA:159,18,0,0 -BRDA:159,18,1,0 -BRDA:164,19,0,0 -BRDA:165,20,0,0 -BRDA:166,21,0,0 -BRDA:166,22,0,0 -BRDA:166,22,1,0 -BRDA:167,23,0,0 -BRDA:171,24,0,0 -BRDA:171,24,1,0 -BRDA:186,25,0,0 -BRDA:202,26,0,0 -BRDA:202,26,1,0 -BRDA:244,27,0,0 -BRDA:244,27,1,0 -BRDA:266,28,0,0 -BRDA:266,28,1,0 -BRDA:272,29,0,0 -BRDA:272,29,1,0 -BRDA:274,30,0,0 -BRDA:274,30,1,0 -BRDA:294,31,0,0 -BRDA:301,32,0,0 -BRDA:301,32,1,0 -BRDA:312,33,0,0 -BRDA:319,34,0,0 -BRDA:319,34,1,0 -BRDA:382,35,0,0 -BRDA:382,35,1,0 -BRDA:382,36,0,0 -BRDA:382,36,1,0 -BRDA:402,37,0,0 -BRDA:402,37,1,0 -BRDA:403,38,0,0 -BRDA:403,38,1,0 -BRDA:409,39,0,0 -BRDA:409,39,1,0 -BRDA:417,40,0,0 -BRDA:417,40,1,0 -BRDA:427,41,0,0 -BRDA:427,41,1,0 -BRF:68 -BRH:0 -end_of_record -TN: -SF:src/app/courses/[courseId]/debug/page.tsx -FN:14,DebugPage -FN:30,(anonymous_2) -FN:39,(anonymous_3) -FN:43,handleTestRetrieval -FN:56,handleGetEvidence -FN:73,(anonymous_6) -FN:124,(anonymous_7) -FN:156,(anonymous_8) -FN:162,(anonymous_9) -FN:165,(anonymous_10) -FN:207,(anonymous_11) -FNF:11 -FNH:0 -FNDA:0,DebugPage -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,handleTestRetrieval -FNDA:0,handleGetEvidence -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -DA:3,0 -DA:4,0 -DA:5,0 -DA:11,0 -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:19,0 -DA:21,0 -DA:22,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:35,0 -DA:39,0 -DA:40,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:50,0 -DA:52,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:63,0 -DA:65,0 -DA:73,0 -DA:125,0 -DA:156,0 -DA:162,0 -DA:166,0 -DA:208,0 -LF:44 -LH:0 -BRDA:35,0,0,0 -BRDA:35,0,1,0 -BRDA:44,1,0,0 -BRDA:50,2,0,0 -BRDA:50,2,1,0 -BRDA:57,3,0,0 -BRDA:109,4,0,0 -BRDA:110,5,0,0 -BRDA:114,6,0,0 -BRDA:114,6,1,0 -BRDA:129,7,0,0 -BRDA:140,8,0,0 -BRDA:140,8,1,0 -BRDA:175,9,0,0 -BRDA:175,9,1,0 -BRDA:182,10,0,0 -BRDA:182,10,1,0 -BRDA:189,11,0,0 -BRDA:192,12,0,0 -BRDA:192,12,1,0 -BRDA:200,13,0,0 -BRDA:220,14,0,0 -BRDA:225,15,0,0 -BRDA:225,15,1,0 -BRF:24 -BRH:0 -end_of_record -TN: -SF:src/app/courses/[courseId]/documents/[documentId]/preview/page.tsx -FN:12,uniqueSections -FN:14,(anonymous_3) -FN:18,(anonymous_4) -FN:23,Spinner -FN:33,OutlinePane -FN:42,(anonymous_7) -FN:58,(anonymous_8) -FN:63,(anonymous_9) -FN:84,ViewerPane -FN:96,(anonymous_11) -FN:121,(anonymous_12) -FN:128,(anonymous_13) -FN:163,ChunkBrowser -FN:191,(anonymous_15) -FN:196,(anonymous_16) -FN:247,PreviewContent -FN:264,(anonymous_18) -FN:278,(anonymous_19) -FN:283,(anonymous_20) -FN:304,(anonymous_21) -FN:323,(anonymous_22) -FN:356,(anonymous_23) -FN:356,(anonymous_24) -FN:367,(anonymous_25) -FN:367,(anonymous_26) -FN:409,DocumentPreviewPage -FNF:26 -FNH:0 -FNDA:0,uniqueSections -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,Spinner -FNDA:0,OutlinePane -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,ViewerPane -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,ChunkBrowser -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,PreviewContent -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,DocumentPreviewPage -DA:3,0 -DA:4,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:16,0 -DA:18,0 -DA:42,0 -DA:59,0 -DA:60,0 -DA:63,0 -DA:93,0 -DA:94,0 -DA:96,0 -DA:97,0 -DA:100,0 -DA:122,0 -DA:123,0 -DA:129,0 -DA:174,0 -DA:175,0 -DA:176,0 -DA:177,0 -DA:192,0 -DA:193,0 -DA:196,0 -DA:248,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:253,0 -DA:254,0 -DA:255,0 -DA:257,0 -DA:259,0 -DA:260,0 -DA:261,0 -DA:262,0 -DA:264,0 -DA:265,0 -DA:266,0 -DA:267,0 -DA:268,0 -DA:269,0 -DA:270,0 -DA:272,0 -DA:274,0 -DA:278,0 -DA:279,0 -DA:283,0 -DA:284,0 -DA:285,0 -DA:286,0 -DA:290,0 -DA:292,0 -DA:304,0 -DA:315,0 -DA:316,0 -DA:323,0 -DA:356,0 -DA:367,0 -DA:409,0 -LF:65 -LH:0 -BRDA:15,0,0,0 -BRDA:15,0,1,0 -BRDA:16,1,0,0 -BRDA:52,2,0,0 -BRDA:65,3,0,0 -BRDA:65,3,1,0 -BRDA:70,4,0,0 -BRDA:70,4,1,0 -BRDA:100,5,0,0 -BRDA:104,6,0,0 -BRDA:127,7,0,0 -BRDA:127,7,1,0 -BRDA:134,8,0,0 -BRDA:134,8,1,0 -BRDA:142,9,0,0 -BRDA:142,9,1,0 -BRDA:145,10,0,0 -BRDA:145,10,1,0 -BRDA:147,11,0,0 -BRDA:175,12,0,0 -BRDA:175,12,1,0 -BRDA:175,12,2,0 -BRDA:186,13,0,0 -BRDA:198,14,0,0 -BRDA:198,14,1,0 -BRDA:204,15,0,0 -BRDA:204,15,1,0 -BRDA:206,16,0,0 -BRDA:206,16,1,0 -BRDA:208,17,0,0 -BRDA:208,17,1,0 -BRDA:218,18,0,0 -BRDA:257,19,0,0 -BRDA:257,19,1,0 -BRDA:265,20,0,0 -BRDA:272,21,0,0 -BRDA:272,21,1,0 -BRDA:284,22,0,0 -BRDA:290,23,0,0 -BRDA:292,24,0,0 -BRDA:292,25,0,0 -BRDA:292,25,1,0 -BRDA:297,26,0,0 -BRDA:297,26,1,0 -BRDA:315,27,0,0 -BRDA:315,27,1,0 -BRDA:344,28,0,0 -BRDA:344,28,1,0 -BRDA:346,29,0,0 -BRDA:346,29,1,0 -BRDA:347,30,0,0 -BRDA:347,30,1,0 -BRDA:347,31,0,0 -BRDA:347,31,1,0 -BRDA:348,32,0,0 -BRDA:348,32,1,0 -BRDA:353,33,0,0 -BRF:57 -BRH:0 -end_of_record -TN: -SF:src/app/courses/[courseId]/study/page.tsx -FN:21,StudyPage -FN:44,(anonymous_2) -FN:47,(anonymous_3) -FN:53,(anonymous_4) -FN:57,(anonymous_5) -FN:58,load -FN:68,(anonymous_7) -FN:76,loadProgress -FN:96,(anonymous_9) -FN:101,(anonymous_10) -FN:105,(anonymous_11) -FN:152,(anonymous_12) -FNF:12 -FNH:1 -FNDA:69,StudyPage -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,load -FNDA:0,(anonymous_7) -FNDA:0,loadProgress -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -DA:3,1 -DA:4,1 -DA:5,1 -DA:7,1 -DA:8,1 -DA:14,1 -DA:15,1 -DA:16,1 -DA:17,1 -DA:18,1 -DA:19,1 -DA:21,1 -DA:22,69 -DA:23,69 -DA:24,0 -DA:25,0 -DA:26,0 -DA:28,0 -DA:29,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:51,0 -DA:53,0 -DA:54,0 -DA:57,0 -DA:59,0 -DA:60,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:72,0 -DA:77,0 -DA:78,0 -DA:79,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:95,0 -DA:97,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:114,0 -DA:153,0 -LF:64 -LH:14 -BRDA:28,0,0,0 -BRDA:28,0,1,0 -BRDA:29,1,0,0 -BRDA:29,1,1,0 -BRDA:45,2,0,0 -BRDA:51,3,0,0 -BRDA:51,3,1,0 -BRDA:67,4,0,0 -BRDA:77,5,0,0 -BRDA:81,6,0,0 -BRDA:89,7,0,0 -BRDA:97,8,0,0 -BRDA:103,9,0,0 -BRDA:114,10,0,0 -BRDA:128,11,0,0 -BRDA:146,12,0,0 -BRDA:149,13,0,0 -BRDA:149,13,1,0 -BRF:18 -BRH:0 -end_of_record -TN: -SF:src/app/dashboard/error.tsx -FN:5,DashboardError -FN:12,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,DashboardError -FNDA:0,(anonymous_2) -DA:3,0 -DA:5,0 -DA:12,0 -DA:13,0 -LF:4 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/app/dashboard/page.tsx -FN:9,DashboardPage -FN:15,(anonymous_3) -FN:18,fetchCourses -FN:48,(anonymous_5) -FNF:4 -FNH:0 -FNDA:0,DashboardPage -FNDA:0,(anonymous_3) -FNDA:0,fetchCourses -FNDA:0,(anonymous_5) -DA:3,0 -DA:4,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:9,0 -DA:10,0 -DA:11,0 -DA:12,0 -DA:13,0 -DA:15,0 -DA:16,0 -DA:19,0 -DA:20,0 -DA:21,0 -DA:25,0 -DA:29,0 -DA:32,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:48,0 -DA:49,0 -LF:23 -LH:0 -BRDA:16,0,0,0 -BRDA:32,1,0,0 -BRDA:43,2,0,0 -BRDA:72,3,0,0 -BRDA:72,3,1,0 -BRDA:87,4,0,0 -BRDA:87,4,1,0 -BRDA:93,5,0,0 -BRDA:93,5,1,0 -BRDA:106,6,0,0 -BRDA:106,6,1,0 -BRF:11 -BRH:0 -end_of_record -TN: -SF:src/components/courses/CourseCard.tsx -FN:18,Mini -FN:29,CourseCard -FNF:2 -FNH:0 -FNDA:0,Mini -FNDA:0,CourseCard -DA:3,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:33,0 -LF:5 -LH:0 -BRDA:21,0,0,0 -BRDA:21,0,1,0 -BRDA:29,1,0,0 -BRDA:30,2,0,0 -BRDA:30,2,1,0 -BRDA:31,3,0,0 -BRDA:31,3,1,0 -BRDA:33,4,0,0 -BRDA:33,4,1,0 -BRDA:35,5,0,0 -BRDA:35,5,1,0 -BRDA:37,6,0,0 -BRDA:37,6,1,0 -BRDA:49,7,0,0 -BRDA:49,7,1,0 -BRDA:70,8,0,0 -BRDA:77,9,0,0 -BRDA:81,10,0,0 -BRDA:81,10,1,0 -BRF:19 -BRH:0 -end_of_record -TN: -SF:src/components/courses/CreateCourseModal.tsx -FN:10,CreateCourseModal -FN:17,(anonymous_2) -FN:19,onKey -FN:23,(anonymous_4) -FN:26,handleCreate -FN:49,(anonymous_6) -FN:82,(anonymous_7) -FN:86,(anonymous_8) -FN:102,(anonymous_9) -FNF:9 -FNH:0 -FNDA:0,CreateCourseModal -FNDA:0,(anonymous_2) -FNDA:0,onKey -FNDA:0,(anonymous_4) -FNDA:0,handleCreate -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -DA:3,0 -DA:10,0 -DA:11,0 -DA:12,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:17,0 -DA:18,0 -DA:20,0 -DA:22,0 -DA:23,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:36,0 -DA:37,0 -DA:49,0 -DA:83,0 -DA:84,0 -DA:87,0 -DA:102,0 -LF:26 -LH:0 -BRDA:20,0,0,0 -BRDA:27,1,0,0 -BRDA:34,2,0,0 -BRDA:34,2,1,0 -BRDA:36,3,0,0 -BRDA:36,3,1,0 -BRDA:87,4,0,0 -BRDA:87,5,0,0 -BRDA:87,5,1,0 -BRDA:109,6,0,0 -BRDA:122,7,0,0 -BRDA:122,7,1,0 -BRDA:123,8,0,0 -BRDA:123,8,1,0 -BRF:14 -BRH:0 -end_of_record -TN: -SF:src/components/courses/ProcessingIndicator.tsx -FN:52,ProcessingIndicator -FNF:1 -FNH:0 -FNDA:0,ProcessingIndicator -DA:14,0 -DA:41,0 -DA:52,0 -DA:53,0 -DA:54,0 -LF:5 -LH:0 -BRDA:52,0,0,0 -BRDA:54,1,0,0 -BRDA:54,1,1,0 -BRDA:54,2,0,0 -BRDA:54,2,1,0 -BRDA:54,2,2,0 -BRDA:62,3,0,0 -BRF:7 -BRH:0 -end_of_record -TN: -SF:src/components/documents/DocumentProcessingPanel.tsx -FN:13,DetailRow -FN:24,DocumentProcessingPanel -FN:33,(anonymous_3) -FN:45,(anonymous_4) -FN:99,(anonymous_5) -FNF:5 -FNH:0 -FNDA:0,DetailRow -FNDA:0,DocumentProcessingPanel -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -DA:3,0 -DA:4,0 -DA:24,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:41,0 -DA:45,0 -DA:46,0 -DA:49,0 -DA:59,0 -DA:70,0 -DA:99,0 -LF:19 -LH:0 -BRDA:18,0,0,0 -BRDA:39,1,0,0 -BRDA:39,1,1,0 -BRDA:49,2,0,0 -BRDA:59,3,0,0 -BRDA:70,4,0,0 -BRDA:81,5,0,0 -BRDA:87,6,0,0 -BRDA:97,7,0,0 -BRDA:97,7,1,0 -BRF:10 -BRH:0 -end_of_record -TN: -SF:src/components/documents/DocumentRow.tsx -FN:15,formatFileSize -FN:27,DocumentRow -FN:35,handleDelete -FN:46,handlePreview -FNF:4 -FNH:0 -FNDA:0,formatFileSize -FNDA:0,DocumentRow -FNDA:0,handleDelete -FNDA:0,handlePreview -DA:3,0 -DA:4,0 -DA:6,0 -DA:7,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:21,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:33,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:42,0 -DA:47,0 -LF:21 -LH:0 -BRDA:16,0,0,0 -BRDA:17,1,0,0 -BRDA:33,2,0,0 -BRDA:33,2,1,0 -BRDA:36,3,0,0 -BRF:5 -BRH:0 -end_of_record -TN: -SF:src/components/documents/DocumentUpload.tsx -FN:64,isSupportedType -FN:71,validateFile -FN:78,runUpload -FN:88,(anonymous_4) -FN:89,(anonymous_5) -FN:89,(anonymous_6) -FN:92,(anonymous_7) -FN:118,(anonymous_8) -FN:133,runRetry -FN:140,(anonymous_10) -FN:141,(anonymous_11) -FN:155,(anonymous_12) -FN:156,(anonymous_13) -FN:156,(anonymous_14) -FN:182,(anonymous_15) -FN:198,JobRow -FN:365,DocumentUpload -FN:374,(anonymous_18) -FN:383,(anonymous_19) -FN:393,(anonymous_20) -FN:408,(anonymous_21) -FN:422,(anonymous_22) -FN:427,(anonymous_23) -FN:428,(anonymous_24) -FN:428,(anonymous_25) -FN:446,(anonymous_26) -FN:452,(anonymous_27) -FN:453,(anonymous_28) -FN:453,(anonymous_29) -FN:456,(anonymous_30) -FN:457,(anonymous_31) -FN:457,(anonymous_32) -FN:461,(anonymous_33) -FN:469,(anonymous_34) -FN:475,(anonymous_35) -FN:478,(anonymous_36) -FN:492,(anonymous_37) -FN:496,(anonymous_38) -FN:498,(anonymous_39) -FN:511,(anonymous_40) -FN:541,(anonymous_41) -FN:545,(anonymous_42) -FN:546,(anonymous_43) -FNF:43 -FNH:0 -FNDA:0,isSupportedType -FNDA:0,validateFile -FNDA:0,runUpload -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,runRetry -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,JobRow -FNDA:0,DocumentUpload -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -FNDA:0,(anonymous_29) -FNDA:0,(anonymous_30) -FNDA:0,(anonymous_31) -FNDA:0,(anonymous_32) -FNDA:0,(anonymous_33) -FNDA:0,(anonymous_34) -FNDA:0,(anonymous_35) -FNDA:0,(anonymous_36) -FNDA:0,(anonymous_37) -FNDA:0,(anonymous_38) -FNDA:0,(anonymous_39) -FNDA:0,(anonymous_40) -FNDA:0,(anonymous_41) -FNDA:0,(anonymous_42) -FNDA:0,(anonymous_43) -DA:3,0 -DA:8,0 -DA:9,0 -DA:11,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:21,0 -DA:23,0 -DA:29,0 -DA:65,0 -DA:66,0 -DA:68,0 -DA:72,0 -DA:73,0 -DA:88,0 -DA:89,0 -DA:91,0 -DA:92,0 -DA:93,0 -DA:95,0 -DA:97,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:106,0 -DA:107,0 -DA:112,0 -DA:113,0 -DA:116,0 -DA:118,0 -DA:120,0 -DA:121,0 -DA:123,0 -DA:129,0 -DA:140,0 -DA:141,0 -DA:142,0 -DA:155,0 -DA:156,0 -DA:158,0 -DA:159,0 -DA:161,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:166,0 -DA:167,0 -DA:168,0 -DA:170,0 -DA:171,0 -DA:176,0 -DA:177,0 -DA:180,0 -DA:182,0 -DA:184,0 -DA:185,0 -DA:187,0 -DA:192,0 -DA:207,0 -DA:208,0 -DA:209,0 -DA:212,0 -DA:365,0 -DA:366,0 -DA:367,0 -DA:368,0 -DA:369,0 -DA:370,0 -DA:371,0 -DA:372,0 -DA:374,0 -DA:375,0 -DA:376,0 -DA:377,0 -DA:378,0 -DA:379,0 -DA:383,0 -DA:384,0 -DA:385,0 -DA:386,0 -DA:388,0 -DA:392,0 -DA:394,0 -DA:395,0 -DA:398,0 -DA:399,0 -DA:400,0 -DA:401,0 -DA:402,0 -DA:404,0 -DA:406,0 -DA:408,0 -DA:409,0 -DA:410,0 -DA:422,0 -DA:424,0 -DA:425,0 -DA:426,0 -DA:427,0 -DA:428,0 -DA:429,0 -DA:445,0 -DA:447,0 -DA:452,0 -DA:453,0 -DA:456,0 -DA:457,0 -DA:460,0 -DA:462,0 -DA:463,0 -DA:464,0 -DA:469,0 -DA:476,0 -DA:478,0 -DA:493,0 -DA:494,0 -DA:496,0 -DA:498,0 -DA:512,0 -DA:513,0 -DA:542,0 -DA:545,0 -DA:546,0 -LF:129 -LH:0 -BRDA:65,0,0,0 -BRDA:65,0,1,0 -BRDA:66,1,0,0 -BRDA:68,2,0,0 -BRDA:68,2,1,0 -BRDA:72,3,0,0 -BRDA:89,4,0,0 -BRDA:89,4,1,0 -BRDA:101,5,0,0 -BRDA:106,6,0,0 -BRDA:110,7,0,0 -BRDA:110,7,1,0 -BRDA:116,8,0,0 -BRDA:116,9,0,0 -BRDA:116,9,1,0 -BRDA:126,10,0,0 -BRDA:126,10,1,0 -BRDA:142,11,0,0 -BRDA:142,11,1,0 -BRDA:156,12,0,0 -BRDA:156,12,1,0 -BRDA:165,13,0,0 -BRDA:170,14,0,0 -BRDA:174,15,0,0 -BRDA:174,15,1,0 -BRDA:180,16,0,0 -BRDA:180,17,0,0 -BRDA:180,17,1,0 -BRDA:190,18,0,0 -BRDA:190,18,1,0 -BRDA:207,19,0,0 -BRDA:207,19,1,0 -BRDA:209,20,0,0 -BRDA:209,20,1,0 -BRDA:212,21,0,0 -BRDA:212,21,1,0 -BRDA:214,22,0,0 -BRDA:214,22,1,0 -BRDA:215,23,0,0 -BRDA:215,23,1,0 -BRDA:216,24,0,0 -BRDA:216,24,1,0 -BRDA:218,25,0,0 -BRDA:218,25,1,0 -BRDA:220,26,0,0 -BRDA:220,26,1,0 -BRDA:227,27,0,0 -BRDA:227,27,1,0 -BRDA:234,28,0,0 -BRDA:245,29,0,0 -BRDA:260,30,0,0 -BRDA:281,31,0,0 -BRDA:303,32,0,0 -BRDA:303,32,1,0 -BRDA:305,33,0,0 -BRDA:305,33,1,0 -BRDA:314,34,0,0 -BRDA:314,34,1,0 -BRDA:314,34,2,0 -BRDA:324,35,0,0 -BRDA:344,36,0,0 -BRDA:354,37,0,0 -BRDA:354,37,1,0 -BRDA:377,38,0,0 -BRDA:384,39,0,0 -BRDA:384,39,1,0 -BRDA:395,40,0,0 -BRDA:400,41,0,0 -BRDA:406,42,0,0 -BRDA:414,43,0,0 -BRDA:414,43,1,0 -BRDA:416,44,0,0 -BRDA:416,44,1,0 -BRDA:417,45,0,0 -BRDA:417,45,1,0 -BRDA:425,46,0,0 -BRDA:428,47,0,0 -BRDA:428,47,1,0 -BRDA:457,48,0,0 -BRDA:457,48,1,0 -BRDA:464,49,0,0 -BRDA:469,50,0,0 -BRDA:469,50,1,0 -BRDA:480,51,0,0 -BRDA:480,51,1,0 -BRDA:500,52,0,0 -BRDA:500,52,1,0 -BRDA:512,53,0,0 -BRDA:539,54,0,0 -BRDA:545,55,0,0 -BRDA:545,55,1,0 -BRDA:549,56,0,0 -BRF:92 -BRH:0 -end_of_record -TN: -SF:src/components/providers/AuthProvider.tsx -FN:18,AuthProvider -FNF:1 -FNH:0 -FNDA:0,AuthProvider -DA:7,0 -DA:18,0 -DA:22,0 -LF:3 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/components/providers/SessionErrorGuard.tsx -FN:6,SessionErrorGuard -FN:9,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,SessionErrorGuard -FNDA:0,(anonymous_2) -DA:3,0 -DA:4,0 -DA:6,0 -DA:7,0 -DA:9,0 -DA:10,0 -DA:11,0 -DA:15,0 -LF:8 -LH:0 -BRDA:10,0,0,0 -BRF:1 -BRH:0 -end_of_record -TN: -SF:src/components/study/CitationCard.tsx -FN:12,CitationCard -FN:25,handleClick -FNF:2 -FNH:0 -FNDA:0,CitationCard -FNDA:0,handleClick -DA:3,2 -DA:12,0 -DA:13,0 -DA:15,0 -DA:21,0 -DA:23,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:31,0 -LF:12 -LH:1 -BRDA:15,0,0,0 -BRDA:15,0,1,0 -BRDA:17,1,0,0 -BRDA:17,1,1,0 -BRDA:21,2,0,0 -BRDA:21,2,1,0 -BRDA:21,3,0,0 -BRDA:21,3,1,0 -BRDA:23,4,0,0 -BRDA:23,4,1,0 -BRDA:26,5,0,0 -BRDA:28,6,0,0 -BRDA:28,6,1,0 -BRDA:29,7,0,0 -BRDA:32,8,0,0 -BRDA:32,8,1,0 -BRDA:38,9,0,0 -BRDA:38,9,1,0 -BRDA:39,10,0,0 -BRDA:39,10,1,0 -BRDA:49,11,0,0 -BRDA:53,12,0,0 -BRF:22 -BRH:0 -end_of_record -TN: -SF:src/components/study/ContentChunks.tsx -FN:34,ContentChunks -FN:44,(anonymous_2) -FN:45,fetchContent -FN:48,(anonymous_4) -FN:51,(anonymous_5) -FN:56,(anonymous_6) -FN:57,(anonymous_7) -FN:66,(anonymous_8) -FN:67,(anonymous_9) -FN:84,(anonymous_10) -FN:114,(anonymous_11) -FN:136,(anonymous_12) -FN:146,(anonymous_13) -FNF:13 -FNH:0 -FNDA:0,ContentChunks -FNDA:0,(anonymous_2) -FNDA:0,fetchContent -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -DA:3,1 -DA:4,1 -DA:5,1 -DA:34,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:44,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:50,0 -DA:52,0 -DA:53,0 -DA:56,0 -DA:57,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:69,0 -DA:71,0 -DA:73,0 -DA:77,0 -DA:80,0 -DA:85,0 -DA:96,0 -DA:104,0 -DA:105,0 -DA:115,0 -DA:116,0 -DA:137,0 -DA:147,0 -LF:32 -LH:3 -BRDA:56,0,0,0 -BRDA:56,0,1,0 -BRDA:57,1,0,0 -BRDA:57,1,1,0 -BRDA:59,2,0,0 -BRDA:59,2,1,0 -BRDA:66,3,0,0 -BRDA:66,3,1,0 -BRDA:67,4,0,0 -BRDA:67,4,1,0 -BRDA:71,5,0,0 -BRDA:71,5,1,0 -BRDA:80,6,0,0 -BRDA:96,7,0,0 -BRDA:104,8,0,0 -BRDA:119,9,0,0 -BRDA:119,9,1,0 -BRDA:122,10,0,0 -BRDA:134,11,0,0 -BRDA:138,12,0,0 -BRDA:138,12,1,0 -BRDA:154,13,0,0 -BRF:22 -BRH:0 -end_of_record -TN: -SF:src/components/study/LessonNav.tsx -FN:14,LessonNav -FN:25,(anonymous_2) -FN:43,(anonymous_3) -FN:52,(anonymous_4) -FNF:4 -FNH:4 -FNDA:9,LessonNav -FNDA:3,(anonymous_2) -FNDA:21,(anonymous_3) -FNDA:1,(anonymous_4) -DA:14,9 -DA:22,9 -DA:26,3 -DA:32,8 -DA:44,21 -DA:45,21 -DA:46,21 -DA:47,21 -DA:49,21 -DA:52,1 -LF:10 -LH:10 -BRDA:19,0,0,8 -BRDA:22,1,0,1 -BRDA:32,2,0,1 -BRDA:47,3,0,21 -BRDA:47,3,1,0 -BRDA:54,4,0,1 -BRDA:54,4,1,20 -BRDA:58,5,0,1 -BRDA:58,5,1,20 -BRDA:62,6,0,4 -BRDA:62,6,1,17 -BRDA:66,7,0,4 -BRDA:66,7,1,17 -BRDA:85,8,0,21 -BRDA:91,9,0,21 -BRF:15 -BRH:14 -end_of_record -TN: -SF:src/components/study/OutputPane.tsx -FN:42,ScoreBar -FN:63,EvidenceDrawer -FN:66,(anonymous_4) -FN:72,(anonymous_5) -FN:82,(anonymous_6) -FN:114,(anonymous_7) -FN:138,(anonymous_8) -FN:159,InspectDrawer -FN:175,(anonymous_10) -FN:175,(anonymous_11) -FN:176,(anonymous_12) -FN:193,(anonymous_13) -FN:218,OutputPane -FN:236,(anonymous_15) -FN:243,(anonymous_16) -FN:245,handleSend -FN:249,(anonymous_18) -FN:254,(anonymous_19) -FN:259,(anonymous_20) -FN:271,handleAction -FN:275,(anonymous_22) -FN:280,(anonymous_23) -FN:287,(anonymous_24) -FN:301,(anonymous_25) -FN:317,handleKeyDown -FN:341,(anonymous_27) -FN:342,(anonymous_28) -FN:354,(anonymous_29) -FN:355,(anonymous_30) -FN:406,(anonymous_31) -FN:424,(anonymous_32) -FN:435,(anonymous_33) -FN:435,(anonymous_34) -FN:438,(anonymous_35) -FN:470,(anonymous_36) -FN:541,(anonymous_37) -FNF:36 -FNH:3 -FNDA:0,ScoreBar -FNDA:0,EvidenceDrawer -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,InspectDrawer -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:18,OutputPane -FNDA:18,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,handleSend -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,handleAction -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:18,(anonymous_25) -FNDA:0,handleKeyDown -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -FNDA:0,(anonymous_29) -FNDA:0,(anonymous_30) -FNDA:0,(anonymous_31) -FNDA:0,(anonymous_32) -FNDA:0,(anonymous_33) -FNDA:0,(anonymous_34) -FNDA:0,(anonymous_35) -FNDA:0,(anonymous_36) -FNDA:0,(anonymous_37) -DA:3,2 -DA:4,2 -DA:5,2 -DA:6,2 -DA:9,2 -DA:10,2 -DA:43,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:71,0 -DA:72,0 -DA:83,0 -DA:115,0 -DA:139,0 -DA:160,0 -DA:161,0 -DA:175,0 -DA:176,0 -DA:194,0 -DA:210,2 -DA:218,18 -DA:227,18 -DA:228,18 -DA:229,18 -DA:230,18 -DA:231,18 -DA:232,18 -DA:233,18 -DA:234,18 -DA:236,18 -DA:237,18 -DA:241,18 -DA:243,0 -DA:246,0 -DA:247,0 -DA:248,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:252,0 -DA:253,0 -DA:254,0 -DA:259,0 -DA:267,0 -DA:272,0 -DA:273,0 -DA:274,0 -DA:275,0 -DA:276,0 -DA:277,0 -DA:278,0 -DA:279,0 -DA:280,0 -DA:284,0 -DA:285,0 -DA:287,0 -DA:295,0 -DA:296,0 -DA:301,18 -DA:302,18 -DA:309,18 -DA:310,0 -DA:311,0 -DA:318,0 -DA:319,0 -DA:320,0 -DA:324,18 -DA:328,18 -DA:342,0 -DA:343,0 -DA:355,0 -DA:356,0 -DA:407,0 -DA:409,0 -DA:410,0 -DA:424,0 -DA:435,0 -DA:436,0 -DA:438,0 -DA:452,0 -DA:471,0 -DA:541,0 -LF:85 -LH:24 -BRDA:43,0,0,0 -BRDA:43,0,1,0 -BRDA:67,1,0,0 -BRDA:67,1,1,0 -BRDA:68,2,0,0 -BRDA:68,3,0,0 -BRDA:68,3,1,0 -BRDA:68,3,2,0 -BRDA:71,4,0,0 -BRDA:71,4,1,0 -BRDA:87,5,0,0 -BRDA:87,5,1,0 -BRDA:87,5,2,0 -BRDA:89,6,0,0 -BRDA:96,7,0,0 -BRDA:96,7,1,0 -BRDA:97,8,0,0 -BRDA:97,8,1,0 -BRDA:167,9,0,0 -BRDA:167,9,1,0 -BRDA:169,10,0,0 -BRDA:169,10,1,0 -BRDA:175,11,0,0 -BRDA:175,11,1,0 -BRDA:176,12,0,0 -BRDA:176,12,1,0 -BRDA:222,13,0,18 -BRDA:223,14,0,18 -BRDA:224,15,0,18 -BRDA:247,16,0,0 -BRDA:247,17,0,0 -BRDA:247,17,1,0 -BRDA:263,18,0,0 -BRDA:263,18,1,0 -BRDA:272,19,0,0 -BRDA:272,19,1,0 -BRDA:272,19,2,0 -BRDA:272,19,3,0 -BRDA:284,20,0,0 -BRDA:285,21,0,0 -BRDA:285,21,1,0 -BRDA:291,22,0,0 -BRDA:291,22,1,0 -BRDA:302,23,0,18 -BRDA:303,24,0,18 -BRDA:303,24,1,18 -BRDA:303,24,2,0 -BRDA:303,24,3,0 -BRDA:303,24,4,0 -BRDA:318,25,0,0 -BRDA:318,26,0,0 -BRDA:318,26,1,0 -BRDA:324,27,0,1 -BRDA:324,27,1,17 -BRDA:328,28,0,18 -BRDA:328,28,1,18 -BRDA:335,29,0,18 -BRDA:338,30,0,18 -BRDA:346,31,0,0 -BRDA:346,31,1,0 -BRDA:351,32,0,0 -BRDA:351,32,1,0 -BRDA:359,33,0,0 -BRDA:359,33,1,0 -BRDA:364,34,0,0 -BRDA:364,34,1,0 -BRDA:381,35,0,18 -BRDA:381,35,1,18 -BRDA:395,36,0,18 -BRDA:395,36,1,18 -BRDA:409,37,0,0 -BRDA:429,38,0,0 -BRDA:455,39,0,0 -BRDA:455,39,1,0 -BRDA:459,40,0,0 -BRDA:459,40,1,0 -BRDA:465,41,0,0 -BRDA:465,41,1,0 -BRDA:465,41,2,0 -BRDA:490,42,0,18 -BRDA:510,43,0,18 -BRDA:510,43,1,0 -BRDA:515,44,0,18 -BRDA:515,44,1,0 -BRDA:531,45,0,18 -BRDA:534,46,0,0 -BRDA:534,46,1,0 -BRDA:550,47,0,18 -BRDA:550,47,1,0 -BRF:89 -BRH:21 -end_of_record -TN: -SF:src/components/study/SourcePane.tsx -FN:21,SourcePane -FN:97,(anonymous_2) -FNF:2 -FNH:0 -FNDA:0,SourcePane -FNDA:0,(anonymous_2) -DA:3,1 -DA:4,1 -DA:21,0 -DA:34,0 -DA:97,0 -LF:5 -LH:2 -BRDA:30,0,0,0 -BRDA:34,1,0,0 -BRDA:34,1,1,0 -BRDA:41,2,0,0 -BRDA:59,3,0,0 -BRDA:65,4,0,0 -BRDA:80,5,0,0 -BRF:7 -BRH:0 -end_of_record -TN: -SF:src/components/study/SplitPane.tsx -FN:13,SplitPane -FN:26,(anonymous_2) -FN:29,(anonymous_3) -FN:31,(anonymous_4) -FN:35,(anonymous_5) -FN:39,(anonymous_6) -FN:46,(anonymous_7) -FN:68,(anonymous_8) -FN:78,(anonymous_9) -FNF:9 -FNH:0 -FNDA:0,SplitPane -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -DA:3,1 -DA:13,0 -DA:20,0 -DA:21,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:34,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:62,0 -DA:68,0 -DA:78,0 -LF:34 -LH:1 -BRDA:16,0,0,0 -BRDA:17,1,0,0 -BRDA:18,2,0,0 -BRDA:40,3,0,0 -BRDA:40,4,0,0 -BRDA:40,4,1,0 -BRDA:62,5,0,0 -BRDA:70,6,0,0 -BRDA:70,6,1,0 -BRDA:80,7,0,0 -BRDA:80,7,1,0 -BRDA:89,8,0,0 -BRDA:89,8,1,0 -BRF:13 -BRH:0 -end_of_record -TN: -SF:src/components/study/StudyActionBar.tsx -FN:56,StudyActionBar -FN:76,(anonymous_2) -FN:82,(anonymous_3) -FNF:3 -FNH:2 -FNDA:18,StudyActionBar -FNDA:144,(anonymous_2) -FNDA:0,(anonymous_3) -DA:13,2 -DA:56,18 -DA:63,18 -DA:77,144 -DA:78,144 -DA:79,144 -DA:82,0 -LF:7 -LH:6 -BRDA:61,0,0,0 -BRDA:63,1,0,18 -BRDA:63,1,1,18 -BRDA:63,1,2,18 -BRDA:78,2,0,144 -BRDA:78,2,1,0 -BRDA:84,3,0,144 -BRDA:84,3,1,0 -BRDA:86,4,0,0 -BRDA:86,4,1,144 -BRDA:93,5,0,0 -BRF:11 -BRH:6 -end_of_record -TN: -SF:src/components/study/StudyOutput.tsx -FN:14,StudyOutput -FN:51,(anonymous_3) -FN:51,(anonymous_4) -FN:68,(anonymous_5) -FN:79,(anonymous_6) -FN:96,parseQuizQuestion -FN:99,(anonymous_8) -FN:101,(anonymous_9) -FN:103,(anonymous_10) -FN:104,(anonymous_11) -FN:115,QuizQuestion -FN:118,(anonymous_13) -FN:129,(anonymous_14) -FN:136,(anonymous_15) -FN:162,(anonymous_16) -FN:180,(anonymous_17) -FN:180,(anonymous_18) -FN:201,QuizOutput -FN:202,(anonymous_20) -FN:210,(anonymous_21) -FN:219,OralOutput -FN:221,(anonymous_23) -FN:223,handleSubmit -FN:229,answerBox -FN:234,(anonymous_26) -FN:234,(anonymous_27) -FN:240,(anonymous_28) -FN:259,(anonymous_29) -FN:271,QuestionsOutput -FN:272,(anonymous_31) -FN:275,(anonymous_32) -FNF:31 -FNH:0 -FNDA:0,StudyOutput -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,parseQuizQuestion -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,QuizQuestion -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,QuizOutput -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,OralOutput -FNDA:0,(anonymous_23) -FNDA:0,handleSubmit -FNDA:0,answerBox -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -FNDA:0,(anonymous_29) -FNDA:0,QuestionsOutput -FNDA:0,(anonymous_31) -FNDA:0,(anonymous_32) -DA:3,2 -DA:4,2 -DA:6,2 -DA:14,0 -DA:15,0 -DA:17,0 -DA:18,0 -DA:51,0 -DA:69,0 -DA:80,0 -DA:97,0 -DA:99,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:112,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:130,0 -DA:131,0 -DA:132,0 -DA:133,0 -DA:136,0 -DA:162,0 -DA:180,0 -DA:202,0 -DA:203,0 -DA:211,0 -DA:220,0 -DA:221,0 -DA:224,0 -DA:225,0 -DA:226,0 -DA:234,0 -DA:240,0 -DA:248,0 -DA:260,0 -DA:272,0 -DA:276,0 -LF:42 -LH:3 -BRDA:17,0,0,0 -BRDA:17,0,1,0 -BRDA:22,1,0,0 -BRDA:22,1,1,0 -BRDA:28,2,0,0 -BRDA:28,2,1,0 -BRDA:28,2,2,0 -BRDA:37,3,0,0 -BRDA:37,3,1,0 -BRDA:37,4,0,0 -BRDA:37,4,1,0 -BRDA:39,5,0,0 -BRDA:39,5,1,0 -BRDA:39,6,0,0 -BRDA:39,6,1,0 -BRDA:41,7,0,0 -BRDA:41,7,1,0 -BRDA:41,8,0,0 -BRDA:41,8,1,0 -BRDA:43,9,0,0 -BRDA:43,9,1,0 -BRDA:48,10,0,0 -BRDA:48,10,1,0 -BRDA:55,11,0,0 -BRDA:55,11,1,0 -BRDA:63,12,0,0 -BRDA:63,12,1,0 -BRDA:64,13,0,0 -BRDA:64,13,1,0 -BRDA:66,14,0,0 -BRDA:77,15,0,0 -BRDA:77,15,1,0 -BRDA:102,16,0,0 -BRDA:102,16,1,0 -BRDA:105,17,0,0 -BRDA:105,17,1,0 -BRDA:128,18,0,0 -BRDA:136,19,0,0 -BRDA:136,19,1,0 -BRDA:139,20,0,0 -BRDA:139,20,1,0 -BRDA:139,21,0,0 -BRDA:139,21,1,0 -BRDA:141,22,0,0 -BRDA:141,22,1,0 -BRDA:141,23,0,0 -BRDA:141,23,1,0 -BRDA:141,23,2,0 -BRDA:143,24,0,0 -BRDA:143,24,1,0 -BRDA:148,25,0,0 -BRDA:148,25,1,0 -BRDA:148,26,0,0 -BRDA:148,26,1,0 -BRDA:150,27,0,0 -BRDA:150,27,1,0 -BRDA:150,28,0,0 -BRDA:150,28,1,0 -BRDA:150,28,2,0 -BRDA:161,29,0,0 -BRDA:161,29,1,0 -BRDA:166,30,0,0 -BRDA:169,31,0,0 -BRDA:169,31,1,0 -BRDA:171,32,0,0 -BRDA:171,32,1,0 -BRDA:183,33,0,0 -BRDA:183,33,1,0 -BRDA:185,34,0,0 -BRDA:185,34,1,0 -BRDA:203,35,0,0 -BRDA:224,36,0,0 -BRDA:224,36,1,0 -BRDA:225,37,0,0 -BRDA:225,38,0,0 -BRDA:225,38,1,0 -BRDA:233,39,0,0 -BRDA:233,39,1,0 -BRDA:239,40,0,0 -BRDA:239,40,1,0 -BRDA:239,41,0,0 -BRDA:239,41,1,0 -BRDA:248,42,0,0 -BRF:83 -BRH:0 -end_of_record -TN: -SF:src/components/ui/BadgeDisplay.tsx -FN:10,BadgeDisplay -FNF:1 -FNH:0 -FNDA:0,BadgeDisplay -DA:10,0 -DA:11,0 -DA:12,0 -LF:3 -LH:0 -BRDA:10,0,0,0 -BRDA:11,1,0,0 -BRDA:11,1,1,0 -BRDA:12,2,0,0 -BRDA:12,2,1,0 -BRDA:26,3,0,0 -BRF:6 -BRH:0 -end_of_record -TN: -SF:src/components/ui/BrandMark.tsx -FN:3,BrandMark -FNF:1 -FNH:0 -FNDA:0,BrandMark -DA:1,0 -DA:3,0 -LF:2 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/components/ui/ErrorBoundary.tsx -FN:17,(anonymous_1) -FN:21,(anonymous_2) -FN:29,(anonymous_3) -FN:25,(anonymous_5) -FNF:4 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_5) -DA:3,1 -DA:14,0 -DA:15,0 -DA:18,0 -DA:22,0 -DA:25,0 -DA:26,0 -DA:30,0 -DA:31,0 -DA:59,0 -LF:10 -LH:1 -BRDA:30,0,0,0 -BRDA:31,1,0,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/components/ui/ProgressBar.tsx -FN:11,ProgressBar -FNF:1 -FNH:1 -FNDA:13,ProgressBar -DA:11,13 -DA:18,13 -DA:19,13 -DA:20,13 -LF:4 -LH:4 -BRDA:14,0,0,12 -BRDA:15,1,0,13 -BRDA:16,2,0,13 -BRDA:19,3,0,13 -BRDA:19,3,1,0 -BRDA:20,4,0,3 -BRDA:20,4,1,10 -BRDA:24,5,0,13 -BRDA:24,5,1,12 -BRDA:26,6,0,12 -BRDA:27,7,0,12 -BRDA:36,8,0,13 -BRDA:36,8,1,12 -BRF:13 -BRH:12 -end_of_record -TN: -SF:src/components/ui/Toast.tsx -FN:21,(anonymous_3) -FN:23,useToast -FN:37,ToastProvider -FN:41,(anonymous_6) -FN:43,(anonymous_7) -FN:44,(anonymous_8) -FN:45,(anonymous_9) -FN:45,(anonymous_10) -FN:49,(anonymous_11) -FN:50,(anonymous_12) -FN:50,(anonymous_13) -FN:62,(anonymous_14) -FN:76,(anonymous_15) -FNF:13 -FNH:0 -FNDA:0,(anonymous_3) -FNDA:0,useToast -FNDA:0,ToastProvider -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -DA:3,0 -DA:21,0 -DA:23,0 -DA:24,0 -DA:29,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:49,0 -DA:50,0 -DA:63,0 -DA:64,0 -DA:76,0 -LF:18 -LH:0 -BRDA:41,0,0,0 -BRDA:56,1,0,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/components/ui/TopBar.tsx -FN:9,TopBar -FN:15,(anonymous_3) -FN:23,toggleDark -FN:60,(anonymous_5) -FN:71,(anonymous_6) -FNF:5 -FNH:0 -FNDA:0,TopBar -FNDA:0,(anonymous_3) -FNDA:0,toggleDark -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -DA:3,0 -DA:4,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:9,0 -DA:10,0 -DA:11,0 -DA:12,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:19,0 -DA:20,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:30,0 -DA:31,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:38,0 -DA:53,0 -DA:55,0 -DA:57,0 -DA:60,0 -DA:72,0 -LF:31 -LH:0 -BRDA:18,0,0,0 -BRDA:18,0,1,0 -BRDA:18,0,2,0 -BRDA:27,1,0,0 -BRDA:27,1,1,0 -BRDA:33,2,0,0 -BRDA:33,2,1,0 -BRDA:34,3,0,0 -BRDA:34,3,1,0 -BRDA:35,4,0,0 -BRDA:35,4,1,0 -BRDA:35,4,2,0 -BRDA:40,5,0,0 -BRDA:40,5,1,0 -BRDA:53,6,0,0 -BRDA:53,6,1,0 -BRDA:55,7,0,0 -BRDA:55,7,1,0 -BRDA:55,7,2,0 -BRDA:57,8,0,0 -BRDA:57,8,1,0 -BRDA:76,9,0,0 -BRDA:76,9,1,0 -BRDA:86,10,0,0 -BRDA:86,10,1,0 -BRDA:90,11,0,0 -BRDA:90,11,1,0 -BRDA:104,12,0,0 -BRDA:104,12,1,0 -BRDA:109,13,0,0 -BRF:30 -BRH:0 -end_of_record -TN: -SF:src/lib/api.ts -FN:8,(anonymous_3) -FN:23,handleResponse -FN:25,(anonymous_5) -FN:35,apiFetch -FNF:4 -FNH:0 -FNDA:0,(anonymous_3) -FNDA:0,handleResponse -FNDA:0,(anonymous_5) -FNDA:0,apiFetch -DA:2,2 -DA:7,0 -DA:9,0 -DA:11,0 -DA:13,0 -DA:14,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:32,0 -DA:35,0 -DA:36,0 -DA:38,0 -DA:42,0 -DA:43,0 -DA:46,0 -DA:47,0 -DA:50,0 -DA:56,0 -LF:19 -LH:1 -BRDA:2,0,0,2 -BRDA:2,0,1,2 -BRDA:3,1,0,0 -BRDA:3,1,1,2 -BRDA:24,2,0,0 -BRDA:28,3,0,0 -BRDA:28,3,1,0 -BRDA:28,3,2,0 -BRDA:35,4,0,0 -BRDA:42,5,0,0 -BRDA:46,6,0,0 -BRDA:46,7,0,0 -BRDA:46,7,1,0 -BRDA:53,8,0,0 -BRDA:53,8,1,0 -BRDA:53,9,0,0 -BRDA:53,9,1,0 -BRF:17 -BRH:3 -end_of_record -TN: -SF:src/lib/api/adapters.ts -FN:11,mimeToLabel -FN:26,toDocumentListRow -FN:43,toDocumentDetailView -FN:73,toDocumentPreviewView -FNF:4 -FNH:0 -FNDA:0,mimeToLabel -FNDA:0,toDocumentListRow -FNDA:0,toDocumentDetailView -FNDA:0,toDocumentPreviewView -DA:12,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:20,0 -DA:21,0 -DA:24,0 -DA:26,0 -DA:27,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:49,0 -DA:53,0 -DA:73,0 -DA:74,0 -LF:21 -LH:0 -BRDA:12,0,0,0 -BRDA:14,1,0,0 -BRDA:15,2,0,0 -BRDA:16,3,0,0 -BRDA:17,4,0,0 -BRDA:18,5,0,0 -BRDA:21,6,0,0 -BRDA:21,6,1,0 -BRDA:37,7,0,0 -BRDA:37,7,1,0 -BRDA:45,8,0,0 -BRDA:62,9,0,0 -BRDA:62,9,1,0 -BRF:13 -BRH:0 -end_of_record -TN: -SF:src/lib/api/courses.ts -FN:4,fetchCourses -FN:14,fetchCourse -FN:18,fetchCourseLessons -FN:25,createCourse -FNF:4 -FNH:0 -FNDA:0,fetchCourses -FNDA:0,fetchCourse -FNDA:0,fetchCourseLessons -FNDA:0,createCourse -DA:1,0 -DA:4,0 -DA:9,0 -DA:14,0 -DA:15,0 -DA:18,0 -DA:22,0 -DA:25,0 -DA:29,0 -LF:9 -LH:0 -BRDA:5,0,0,0 -BRDA:6,1,0,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/lib/api/documents.ts -FN:21,fetchDocumentsList -FN:30,fetchDocumentStatus -FN:39,fetchDocumentDetail -FN:48,fetchDocumentPreview -FN:57,uploadDocument -FN:73,retryDocument -FN:83,deleteDocument -FN:93,uploadDocumentWithProgress -FN:100,(anonymous_21) -FN:109,(anonymous_22) -FN:113,(anonymous_23) -FN:132,(anonymous_24) -FN:133,(anonymous_25) -FN:141,getDocumentListRows -FN:149,getDocumentDetailView -FN:157,getDocumentPreviewView -FN:167,pollDocumentUntilTerminal -FN:186,(anonymous_30) -FNF:18 -FNH:0 -FNDA:0,fetchDocumentsList -FNDA:0,fetchDocumentStatus -FNDA:0,fetchDocumentDetail -FNDA:0,fetchDocumentPreview -FNDA:0,uploadDocument -FNDA:0,retryDocument -FNDA:0,deleteDocument -FNDA:0,uploadDocumentWithProgress -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,getDocumentListRows -FNDA:0,getDocumentDetailView -FNDA:0,getDocumentPreviewView -FNDA:0,pollDocumentUntilTerminal -FNDA:0,(anonymous_30) -DA:1,0 -DA:4,0 -DA:17,0 -DA:21,0 -DA:25,0 -DA:30,0 -DA:34,0 -DA:39,0 -DA:43,0 -DA:48,0 -DA:52,0 -DA:57,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:73,0 -DA:77,0 -DA:83,0 -DA:87,0 -DA:93,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:105,0 -DA:106,0 -DA:107,0 -DA:109,0 -DA:110,0 -DA:113,0 -DA:114,0 -DA:115,0 -DA:116,0 -DA:118,0 -DA:121,0 -DA:122,0 -DA:123,0 -DA:124,0 -DA:128,0 -DA:132,0 -DA:133,0 -DA:135,0 -DA:141,0 -DA:145,0 -DA:146,0 -DA:149,0 -DA:153,0 -DA:154,0 -DA:157,0 -DA:161,0 -DA:162,0 -DA:167,0 -DA:173,0 -DA:174,0 -DA:175,0 -DA:176,0 -DA:177,0 -DA:180,0 -DA:183,0 -DA:186,0 -DA:188,0 -LF:62 -LH:0 -BRDA:4,0,0,0 -BRDA:4,0,1,0 -BRDA:5,1,0,0 -BRDA:5,1,1,0 -BRDA:61,2,0,0 -BRDA:107,3,0,0 -BRDA:110,4,0,0 -BRDA:114,5,0,0 -BRDA:114,5,1,0 -BRDA:114,6,0,0 -BRDA:114,6,1,0 -BRDA:124,7,0,0 -BRDA:170,8,0,0 -BRDA:171,9,0,0 -BRDA:176,10,0,0 -BRDA:176,11,0,0 -BRDA:176,11,1,0 -BRDA:180,12,0,0 -BRDA:180,12,1,0 -BRDA:180,13,0,0 -BRDA:180,13,1,0 -BRF:21 -BRH:0 -end_of_record -TN: -SF:src/lib/api/index.ts -FNF:0 -FNH:0 -DA:1,0 -DA:2,0 -DA:3,0 -DA:4,0 -LF:4 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/lib/auth/config.ts -FN:9,refreshAccessToken -FN:40,(anonymous_3) -FN:56,(anonymous_4) -FN:79,(anonymous_5) -FN:99,(anonymous_6) -FNF:5 -FNH:0 -FNDA:0,refreshAccessToken -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -DA:3,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:10,0 -DA:11,0 -DA:17,0 -DA:18,0 -DA:20,0 -DA:28,0 -DA:32,0 -DA:41,0 -DA:42,0 -DA:45,0 -DA:46,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:60,0 -DA:62,0 -DA:72,0 -DA:73,0 -DA:80,0 -DA:81,0 -DA:92,0 -DA:93,0 -DA:97,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 -LF:33 -LH:0 -BRDA:5,0,0,0 -BRDA:5,0,1,0 -BRDA:18,1,0,0 -BRDA:23,2,0,0 -BRDA:23,2,1,0 -BRDA:41,3,0,0 -BRDA:41,4,0,0 -BRDA:41,4,1,0 -BRDA:55,5,0,0 -BRDA:57,6,0,0 -BRDA:57,6,1,0 -BRDA:63,7,0,0 -BRDA:63,7,1,0 -BRDA:64,8,0,0 -BRDA:64,8,1,0 -BRDA:65,9,0,0 -BRDA:65,9,1,0 -BRDA:66,10,0,0 -BRDA:66,10,1,0 -BRDA:67,11,0,0 -BRDA:67,11,1,0 -BRDA:68,12,0,0 -BRDA:68,12,1,0 -BRDA:69,13,0,0 -BRDA:69,13,1,0 -BRDA:72,14,0,0 -BRDA:80,15,0,0 -BRDA:92,16,0,0 -BRDA:92,17,0,0 -BRDA:92,17,1,0 -BRF:30 -BRH:0 -end_of_record -TN: -SF:src/lib/middleware/auth-guard.ts -FN:7,authMiddleware -FN:11,(anonymous_4) -FNF:2 -FNH:0 -FNDA:0,authMiddleware -FNDA:0,(anonymous_4) -DA:1,0 -DA:2,0 -DA:5,0 -DA:7,0 -DA:8,0 -DA:10,0 -DA:11,0 -DA:14,0 -DA:15,0 -DA:18,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:29,0 -DA:32,0 -LF:16 -LH:0 -BRDA:11,0,0,0 -BRDA:11,0,1,0 -BRDA:14,1,0,0 -BRDA:14,2,0,0 -BRDA:14,2,1,0 -BRDA:14,2,2,0 -BRDA:23,3,0,0 -BRF:7 -BRH:0 -end_of_record -TN: -SF:src/lib/services/chat.ts -FN:14,sendChatMessage -FNF:1 -FNH:0 -FNDA:0,sendChatMessage -DA:1,0 -DA:14,0 -DA:19,0 -DA:24,0 -LF:4 -LH:0 -BRDA:26,0,0,0 -BRDA:26,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/lib/services/courses.ts -FN:23,getCourses -FN:29,getCourse -FN:33,getCourseLessons -FN:37,getLesson -FN:41,createCourse -FNF:5 -FNH:0 -FNDA:0,getCourses -FNDA:0,getCourse -FNDA:0,getCourseLessons -FNDA:0,getLesson -FNDA:0,createCourse -DA:1,0 -DA:5,0 -DA:23,0 -DA:24,0 -DA:29,0 -DA:30,0 -DA:33,0 -DA:34,0 -DA:37,0 -DA:38,0 -DA:41,0 -DA:42,0 -LF:12 -LH:0 -BRDA:23,0,0,0 -BRDA:23,1,0,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/lib/services/debug.ts -FN:12,getPipelineHealth -FN:16,getDocumentChunks -FN:25,getParsedOutput -FN:32,testRetrieval -FN:50,getEvidencePack -FNF:5 -FNH:0 -FNDA:0,getPipelineHealth -FNDA:0,getDocumentChunks -FNDA:0,getParsedOutput -FNDA:0,testRetrieval -FNDA:0,getEvidencePack -DA:1,0 -DA:12,0 -DA:13,0 -DA:16,0 -DA:20,0 -DA:25,0 -DA:29,0 -DA:32,0 -DA:43,0 -DA:44,0 -DA:50,0 -DA:56,0 -DA:57,0 -LF:13 -LH:0 -BRDA:35,0,0,0 -BRDA:53,1,0,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/lib/services/documents.ts -FN:21,getDocuments -FN:30,uploadDocument -FN:45,getDocumentStatus -FN:54,deleteDocument -FN:68,pollDocumentStatus -FN:87,(anonymous_11) -FNF:6 -FNH:0 -FNDA:0,getDocuments -FNDA:0,uploadDocument -FNDA:0,getDocumentStatus -FNDA:0,deleteDocument -FNDA:0,pollDocumentStatus -FNDA:0,(anonymous_11) -DA:1,0 -DA:21,0 -DA:25,0 -DA:30,0 -DA:35,0 -DA:36,0 -DA:38,0 -DA:45,0 -DA:49,0 -DA:54,0 -DA:58,0 -DA:68,0 -DA:74,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:81,0 -DA:84,0 -DA:87,0 -DA:89,0 -LF:21 -LH:0 -BRDA:71,0,0,0 -BRDA:72,1,0,0 -BRDA:77,2,0,0 -BRDA:77,3,0,0 -BRDA:77,3,1,0 -BRDA:81,4,0,0 -BRDA:81,4,1,0 -BRDA:81,5,0,0 -BRDA:81,5,1,0 -BRF:9 -BRH:0 -end_of_record -TN: -SF:src/lib/services/progress.ts -FN:38,mapProgress -FN:50,mapBadge -FN:60,getCourseProgress -FN:68,markLessonComplete -FN:94,getUserBadges -FN:96,(anonymous_9) -FNF:6 -FNH:0 -FNDA:0,mapProgress -FNDA:0,mapBadge -FNDA:0,getCourseProgress -FNDA:0,markLessonComplete -FNDA:0,getUserBadges -FNDA:0,(anonymous_9) -DA:1,0 -DA:39,0 -DA:51,0 -DA:60,0 -DA:64,0 -DA:65,0 -DA:68,0 -DA:73,0 -DA:79,0 -DA:80,0 -DA:81,0 -DA:83,0 -DA:94,0 -DA:95,0 -DA:96,0 -LF:15 -LH:0 -BRDA:46,0,0,0 -BRDA:46,0,1,0 -BRDA:81,1,0,0 -BRDA:81,1,1,0 -BRF:4 -BRH:0 -end_of_record -TN: -SF:src/lib/services/study.ts -FN:6,sendStudyAction -FNF:1 -FNH:0 -FNDA:0,sendStudyAction -DA:1,2 -DA:6,0 -DA:12,0 -DA:13,0 -LF:4 -LH:1 -BRF:0 -BRH:0 -end_of_record From 4b29484262bb6e15f4ed06efe6822bd294b2f98d Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Mon, 11 May 2026 14:15:35 +0200 Subject: [PATCH 7/7] fix(deps): remove pytest from production deps and align python-dotenv constraint pytest and pytest-asyncio are dev-only tools and belong exclusively in pyproject.toml [dev] extras, not in requirements.txt which is used for production installs. Also aligns python-dotenv to >=1.2.2 across both files (pyproject.toml already used >=, requirements.txt had == pin). --- services/ai/requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/ai/requirements.txt b/services/ai/requirements.txt index 6e474a0..dc7ce2b 100644 --- a/services/ai/requirements.txt +++ b/services/ai/requirements.txt @@ -8,9 +8,7 @@ passlib==1.7.4 bcrypt==4.1.1 python-multipart>=0.0.9 httpx>=0.27.0 -pytest==9.0.3 -pytest-asyncio==0.24.0 -python-dotenv==1.2.2 +python-dotenv>=1.2.2 psycopg2-binary==2.9.9 # Security zxcvbn==4.4.28