Repository: seanprashad/leetcode-patterns Branch: main Commit: 5c24b6a9272d Files: 72 Total size: 519.1 KB Directory structure: gitextract_d7fndcjj/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── feature_request.yml │ │ └── question_request.yml │ └── workflows/ │ ├── ci.yml │ ├── deploy.yml │ └── update-questions.yml ├── .gitignore ├── .husky/ │ └── pre-push ├── .npmrc ├── LICENSE ├── README.md ├── cron/ │ ├── leetcode/ │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── models.py │ │ └── rest.py │ └── update_questions.py ├── eslint.config.mjs ├── next.config.ts ├── package.json ├── postcss.config.mjs ├── public/ │ ├── .nojekyll │ ├── manifest.json │ ├── robots.txt │ └── sw.js ├── scripts/ │ └── generate-sw-precache.mjs ├── src/ │ ├── app/ │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── not-found.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── layout/ │ │ │ ├── AuthContext.test.tsx │ │ │ ├── AuthContext.tsx │ │ │ ├── GitHubLink.tsx │ │ │ ├── Logo.tsx │ │ │ ├── ServiceWorkerRegistrar.tsx │ │ │ ├── ThemeToggle.test.tsx │ │ │ ├── ThemeToggle.tsx │ │ │ ├── UserMenu.test.tsx │ │ │ ├── UserMenu.tsx │ │ │ └── ViewSwitcher.tsx │ │ ├── panels/ │ │ │ ├── AboutPanel.tsx │ │ │ ├── AcknowledgementsPanel.tsx │ │ │ ├── TipsPanel.tsx │ │ │ └── panels.test.tsx │ │ ├── questions/ │ │ │ ├── ConfirmModal.tsx │ │ │ ├── FilterToolbar.tsx │ │ │ ├── GroupHeaderRow.tsx │ │ │ ├── NoteModal.tsx │ │ │ ├── ProgressBar.tsx │ │ │ ├── QuestionRow.tsx │ │ │ ├── QuestionsTable.test.tsx │ │ │ ├── QuestionsTable.tsx │ │ │ └── ReviewDateModal.tsx │ │ └── roadmaps/ │ │ ├── RoadmapView.test.tsx │ │ └── RoadmapView.tsx │ ├── data/ │ │ ├── questions.json │ │ └── roadmaps.ts │ ├── lib/ │ │ ├── analytics.test.ts │ │ ├── analytics.ts │ │ ├── register-sw.test.ts │ │ ├── register-sw.ts │ │ ├── reminders.test.ts │ │ ├── reminders.ts │ │ ├── storage.test.ts │ │ ├── storage.ts │ │ ├── supabase.ts │ │ ├── sw.test.ts │ │ ├── sync.test.ts │ │ └── sync.ts │ ├── test/ │ │ └── setup.ts │ └── types/ │ └── question.ts ├── tsconfig.json └── vitest.config.mts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: Report a bug or issue with Leetcode Patterns labels: ["bug"] body: - type: textarea id: description attributes: label: Description description: A clear description of the bug. placeholder: Describe the issue... validations: required: true - type: textarea id: steps attributes: label: Steps to Reproduce description: How can we reproduce this issue? placeholder: | 1. Go to '...' 2. Click on '...' 3. See error validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: What did you expect to happen? validations: required: true - type: textarea id: screenshots attributes: label: Screenshots description: If applicable, add screenshots to help explain the issue. validations: required: false - type: dropdown id: browser attributes: label: Browser options: - Chrome - Firefox - Safari - Edge - Other validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature Request description: Suggest a new feature or improvement labels: ["enhancement"] body: - type: textarea id: description attributes: label: Description description: A clear description of the feature you'd like. placeholder: I'd like to see... validations: required: true - type: textarea id: motivation attributes: label: Motivation description: Why would this feature be useful? validations: required: false - type: textarea id: alternatives attributes: label: Alternatives Considered description: Any alternative solutions or features you've considered. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/question_request.yml ================================================ name: Question Request description: Request a new question to be added to the list labels: ["question-request"] body: - type: input id: title attributes: label: Question Title description: The title of the LeetCode question. placeholder: e.g. Two Sum validations: required: true - type: input id: url attributes: label: LeetCode URL description: Link to the question on LeetCode. placeholder: https://leetcode.com/problems/two-sum/ validations: required: true - type: dropdown id: difficulty attributes: label: Difficulty options: - Easy - Medium - Hard validations: required: true - type: input id: pattern attributes: label: Pattern(s) description: Which pattern(s) does this question belong to? placeholder: e.g. Arrays, Two Pointers validations: required: true - type: textarea id: reason attributes: label: Why should this be added? description: Why is this question a good fit for the list? validations: required: false ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: pull_request: types: [opened, synchronize, reopened, closed, labeled] branches: [main] permissions: contents: write pull-requests: write jobs: lint: if: github.event.action != 'closed' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm run lint test: if: github.event.action != 'closed' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npx vitest run --coverage preview: if: >- always() && contains(github.event.pull_request.labels.*.name, 'preview') && (github.event.action == 'closed' || (needs.lint.result == 'success' && needs.test.result == 'success')) needs: [lint, test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - if: github.event.action != 'closed' uses: actions/setup-node@v4 with: node-version: 22 cache: npm - if: github.event.action != 'closed' run: npm ci - if: github.event.action != 'closed' name: Build env: NEXT_PUBLIC_BASE_PATH: /leetcode-patterns/pr-preview/pr-${{ github.event.number }} NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} run: npx next build - if: github.event.action != 'closed' name: Remove service worker from preview run: rm -f out/sw.js - uses: rossjrw/pr-preview-action@v1 with: source-dir: out ================================================ FILE: .github/workflows/deploy.yml ================================================ name: Deploy to GitHub Pages on: push: branches: [main] workflow_run: workflows: [Update Questions] types: - completed workflow_dispatch: permissions: contents: write jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - name: Build env: NEXT_PUBLIC_BASE_PATH: /leetcode-patterns NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} run: npm run build - uses: JamesIves/github-pages-deploy-action@v4 with: folder: out branch: gh-pages clean-exclude: pr-preview force: false ================================================ FILE: .github/workflows/update-questions.yml ================================================ name: Update Questions on: schedule: # Every Sunday at 8am EST (1pm UTC) - cron: "0 13 * * 0" workflow_dispatch: permissions: contents: write jobs: update: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Update question metadata working-directory: cron env: LEETCODE_SESSION_TOKEN: ${{ secrets.LEETCODE_SESSION_TOKEN }} LEETCODE_CSRF_TOKEN: ${{ secrets.LEETCODE_CSRF_TOKEN }} LEETCODE_CF_CLEARANCE: ${{ secrets.LEETCODE_CF_CLEARANCE }} run: python update_questions.py - name: Commit and push changes run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add src/data/questions.json git diff --cached --quiet || git commit --author="Sean Prashad <13009507+seanprashad@users.noreply.github.com>" -m "chore: update question metadata" && git push ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.* .yarn/* !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/versions # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) .env* # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts # python __pycache__ ================================================ FILE: .husky/pre-push ================================================ npm run lint -- --fix npm test ================================================ FILE: .npmrc ================================================ registry=https://registry.npmjs.org ================================================ FILE: LICENSE ================================================ Creative Commons Attribution-NonCommercial 4.0 International Copyright (c) 2026 Sean Prashad This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. You are free to: Share — copy and redistribute the material in any medium or format Adapt — remix, transform, and build upon the material Under the following terms: Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. NonCommercial — You may not use the material for commercial purposes. No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. Notices: You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. Full license text: https://creativecommons.org/licenses/by-nc/4.0/legalcode ================================================ FILE: README.md ================================================

Leetcode Patterns

Deploy to GitHub Pages Update Questions

## Table of Contents - [Background](#background) - [Fundamentals](#fundamentals) - [Notes](#notes) - [Question List](#question-list) - [Solutions](#solutions) - [Contributing](#contributing) - [Suggestions](#suggestions) - [Acknowledgements](#acknowledgements) ## Background This repo is intended for any individual wanting to improve their problem solving skills for software engineering interviews. Problems are grouped under their respective subtopic, in order to focus on repeatedly applying common patterns rather than randomly tackling questions. All questions are available on [leetcode.com] with some requiring [leetcode premium]. ## Fundamentals To find the greatest amount of success when practicing, it is highly recommended to know the methods and runtimes of the following data structures and their operations: - Arrays - Maps - Linked Lists - Queues - Heaps - Stacks - Trees - Graphs In addition, you should have a good grasp on common algorithms such as: - Breadth-first search - Depth-first search - Binary search - Recursion ## Notes [This pdf] contains information for the main data structures in Java. Other useful methods to know include [`substring()`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-int-), [`toCharArray()`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#toCharArray--), [`Math.max()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#max-int-int-), [`Math.min()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#min-int-int-), and [`Arrays.fill()`](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#fill-int:A-int-). ## Question List The entire question list can be found here: https://seanprashad.com/leetcode-patterns/. ## Solutions Solutions written in Java can be found in the [solutions] branch. ## Contributing The app is built with [Next.js] (App Router), [React] 19, [TypeScript], [Tailwind CSS] v4, [TanStack Table] v8, [Lucide React] for icons, and Google Analytics via `@next/third-parties`. Tests use [Vitest] + [React Testing Library]. ```bash npm install npm run dev # http://localhost:3000 npm test # single run npm run test:watch # watch mode ``` A [Husky] `pre-push` hook runs `npm test` automatically before every push. This is set up for every clone via the `prepare` script. ## Acknowledgements This list is heavily inspired from [Grokking the Coding Interview] with additional problems extracted from the [Blind 75 list] and this hackernoon article on [14 patterns to ace any coding interview question]. [leetcode.com]: https://leetcode.com [leetcode premium]: https://leetcode.com/subscribe/ [next.js]: https://nextjs.org [react]: https://react.dev [typescript]: https://www.typescriptlang.org [tailwind css]: https://tailwindcss.com [tanstack table]: https://tanstack.com/table [lucide react]: https://lucide.dev [vitest]: https://vitest.dev [react testing library]: https://testing-library.com/docs/react-testing-library/intro [husky]: https://typicode.github.io/husky [this pdf]: https://drive.google.com/open?id=1ao4ZA28zzBttDkuS6MLQI52gDs_CJZEm [solutions]: https://github.com/SeanPrashad/leetcode-patterns/tree/solutions [grokking the coding interview]: https://www.educative.io/courses/grokking-the-coding-interview [issue]: https://github.com/SeanPrashad/leetcode-patterns/issues/new [blind 75 list]: https://www.teamblind.com/article/New-Year-Gift---Curated-List-of-Top-100-LeetCode-Questions-to-Save-Your-Time-OaM1orEU?utm_source=share&utm_medium=ios_app [14 patterns to ace any coding interview question]: https://hackernoon.com/14-patterns-to-ace-any-coding-interview-question-c5bb3357f6ed ================================================ FILE: cron/leetcode/__init__.py ================================================ from leetcode.models import ( Configuration, ApiClient, DefaultApi, GraphqlQuery, GraphqlQueryGetQuestionDetailVariables, GraphqlQuestionDetail, GraphqlData, GraphqlResponse, ) from leetcode import auth from leetcode import rest __all__ = [ "Configuration", "ApiClient", "DefaultApi", "GraphqlQuery", "GraphqlQueryGetQuestionDetailVariables", "GraphqlQuestionDetail", "GraphqlData", "GraphqlResponse", "auth", "rest", ] ================================================ FILE: cron/leetcode/auth.py ================================================ # Placeholder for leetcode.auth compatibility. # Authentication is handled via Configuration.api_key in the main module. ================================================ FILE: cron/leetcode/models.py ================================================ import json import urllib.request from leetcode.rest import ApiException class Configuration: def __init__(self): self.host = "https://leetcode.com" self.api_key = {} self.debug = False class GraphqlQuery: def __init__(self, query=None, variables=None, operation_name=None): self.query = query self.variables = variables self.operation_name = operation_name class GraphqlQueryGetQuestionDetailVariables: def __init__(self, title_slug=None): self.title_slug = title_slug class GraphqlQuestionDetail: def __init__(self, question_id=None, title=None, difficulty=None, company_tag_stats_v2=None, is_paid_only=None, topic_tags=None): self.question_id = question_id self.title = title self.difficulty = difficulty self.company_tag_stats_v2 = company_tag_stats_v2 self.is_paid_only = is_paid_only self.topic_tags = topic_tags class GraphqlData: def __init__(self, question=None): self.question = question class GraphqlResponse: def __init__(self, data=None): self.data = data class ApiClient: def __init__(self, configuration=None): self.configuration = configuration or Configuration() class DefaultApi: def __init__(self, api_client=None): self.api_client = api_client or ApiClient() def graphql_post(self, body=None): config = self.api_client.configuration variables = {} if body.variables and hasattr(body.variables, "title_slug"): variables["titleSlug"] = body.variables.title_slug payload = {"query": body.query, "variables": variables} if body.operation_name: payload["operationName"] = body.operation_name data = json.dumps(payload).encode("utf-8") cookie = ( f"csrftoken={config.api_key.get('csrftoken') or ''}; " f"LEETCODE_SESSION={config.api_key.get('LEETCODE_SESSION') or ''}; " f"cf_clearance={config.api_key.get('cf_clearance') or ''}" ) url = config.host + "/graphql" req = urllib.request.Request(url, data=data, method="POST") req.add_header("Content-Type", "application/json") req.add_header("Accept", "application/json") req.add_header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36") req.add_header("x-csrftoken", config.api_key.get("x-csrftoken") or "") req.add_header("Referer", config.api_key.get("Referer") or "https://leetcode.com") req.add_header("Cookie", cookie) try: with urllib.request.urlopen(req) as resp: resp_data = json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: raise ApiException( status=e.code, reason=e.reason, body=e.read().decode("utf-8"), ) question_data = resp_data.get("data", {}).get("question", {}) topic_tags_raw = question_data.get("topicTags") topic_tags = [type("Tag", (), {"name": t["name"]}) for t in topic_tags_raw] question = GraphqlQuestionDetail( question_id=question_data.get("questionId"), title=question_data.get("title"), difficulty=question_data.get("difficulty"), company_tag_stats_v2=question_data.get("companyTagStatsV2"), is_paid_only=question_data.get("isPaidOnly"), topic_tags=topic_tags, ) return GraphqlResponse(data=GraphqlData(question=question)) ================================================ FILE: cron/leetcode/rest.py ================================================ class ApiException(Exception): def __init__(self, status=None, reason=None, body=None): self.status = status self.reason = reason self.body = body def __str__(self): msg = f"({self.status})\nReason: {self.reason}\n" if self.body: msg += f"HTTP response body: {self.body}\n" return msg ================================================ FILE: cron/update_questions.py ================================================ import os import json import leetcode import leetcode.auth from datetime import datetime from leetcode.rest import ApiException def create_leetcode_api(): leetcode_session_token = os.environ.get("LEETCODE_SESSION_TOKEN") csrf_token = os.environ.get("LEETCODE_CSRF_TOKEN") cf_clearance = os.environ.get("LEETCODE_CF_CLEARANCE") if not leetcode_session_token: print("❌ LEETCODE_SESSION_TOKEN environment variable is required") exit(1) if not csrf_token: print("❌ LEETCODE_CSRF_TOKEN environment variable is required") exit(1) if not cf_clearance: print("❌ LEETCODE_CF_CLEARANCE environment variable is required") exit(1) configuration = leetcode.Configuration() configuration.api_key["x-csrftoken"] = csrf_token configuration.api_key["csrftoken"] = csrf_token configuration.api_key["LEETCODE_SESSION"] = leetcode_session_token configuration.api_key["cf_clearance"] = cf_clearance configuration.api_key["Referer"] = "https://leetcode.com" configuration.debug = False return leetcode.DefaultApi(leetcode.ApiClient(configuration)) def get_question_metadata(api, title_slug): graphql_request = leetcode.GraphqlQuery( query='''query questionData($titleSlug: String!) { question(titleSlug: $titleSlug) { questionId title difficulty companyTagStatsV2 isPaidOnly topicTags { name } } } ''', variables=leetcode.GraphqlQueryGetQuestionDetailVariables( title_slug=title_slug) ) try: response = api.graphql_post(body=graphql_request) if not response.data.question: print(f'❌ Empty response body for question: {title_slug}') exit(1) return response except ApiException as e: print( f'Exception occurred when contacting the Leetcode GraphQL API: ${e}') exit() def construct_company_tag_list(company_tag_stats_v2): companies = [] tag_stats = json.loads(company_tag_stats_v2) # "three_months" = 0-3 months, "six_months" = 3-6 months, "more_than_six_months" = 6+ months for tag in tag_stats["three_months"] + tag_stats["six_months"]: companies.append({ "name": tag["name"], "slug": tag["slug"], "frequency": tag["timesEncountered"] }) return sorted(companies, key=lambda d: d['frequency'], reverse=True) def update_question_metadata(question, response): print(f'''🔄 Updating question metadata for {question["title"]}''') question_id = response.data.question.question_id question_title = response.data.question.title question_difficulty = response.data.question.difficulty question_company_tag_stats_v2 = response.data.question.company_tag_stats_v2 question_is_premium = response.data.question.is_paid_only question_topic_tags = response.data.question.topic_tags companies = construct_company_tag_list(question_company_tag_stats_v2) patterns = [tag.name for tag in question_topic_tags] question["id"] = int(question_id) question["title"] = question_title question["difficulty"] = question_difficulty question["pattern"] = patterns question["companies"] = companies question["premium"] = question_is_premium def read_questions(file_name): print(f"💾 Loading {file_name}") try: with open(file_name, "r") as file: questions = json.load(file) print(f"✅ Finished loading {file_name}") return questions except Exception as e: print( f"❌ Exception occurred when reading {file_name}: {e}") exit() def write_questions(file_name, questions): print(f"💾 Updating {file_name}") try: with open(file_name, "w") as file: questions["updated"] = str(datetime.now().isoformat()) json.dump(questions, file, indent=2) print(f"✅ Finished updating {file_name}") except Exception as e: print( f"❌ Exception occurred when writing {file_name}: {e}") exit() def main(file_name): api = create_leetcode_api() questions = read_questions(file_name) for question in questions["data"]: title_slug = question["slug"] response = get_question_metadata(api, title_slug) update_question_metadata(question, response) write_questions(file_name, questions) if __name__ == "__main__": file_name = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src", "data", "questions.json") startTime = datetime.now() main(file_name) print(f"⏱️ Data updated in {datetime.now() - startTime} seconds") ================================================ FILE: eslint.config.mjs ================================================ import { defineConfig, globalIgnores } from "eslint/config"; import nextVitals from "eslint-config-next/core-web-vitals"; import nextTs from "eslint-config-next/typescript"; const eslintConfig = defineConfig([ ...nextVitals, ...nextTs, // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: ".next/**", "out/**", "build/**", "next-env.d.ts", ]), ]); export default eslintConfig; ================================================ FILE: next.config.ts ================================================ import type { NextConfig } from "next"; const nextConfig: NextConfig = { reactStrictMode: false, output: "export", trailingSlash: false, basePath: process.env.NEXT_PUBLIC_BASE_PATH ?? "", images: { unoptimized: true }, }; export default nextConfig; ================================================ FILE: package.json ================================================ { "name": "leetcode-patterns-v2", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build && node scripts/generate-sw-precache.mjs", "start": "next start", "lint": "eslint", "test": "vitest run", "test:watch": "vitest", "prepare": "husky" }, "dependencies": { "@next/third-parties": "^16.1.6", "@supabase/supabase-js": "^2.99.0", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.13.21", "lucide-react": "^0.577.0", "next": "16.1.7", "react": "19.2.3", "react-dom": "19.2.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "@vitejs/plugin-react": "^5.1.4", "@vitest/coverage-v8": "^4.0.18", "eslint": "^9", "eslint-config-next": "16.1.6", "happy-dom": "^20.8.3", "husky": "^9.1.7", "jsdom": "^28.1.0", "tailwindcss": "^4", "typescript": "^5", "vitest": "^4.0.18" } } ================================================ FILE: postcss.config.mjs ================================================ const config = { plugins: { "@tailwindcss/postcss": {}, }, }; export default config; ================================================ FILE: public/.nojekyll ================================================ ================================================ FILE: public/manifest.json ================================================ { "short_name": "leetcode-patterns", "name": "Leetcode Patterns", "description": "A curated list of LeetCode questions grouped by patterns.", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" } ], "start_url": ".", "display": "standalone", "theme_color": "#09090b", "background_color": "#ffffff" } ================================================ FILE: public/robots.txt ================================================ User-agent: * Allow: / ================================================ FILE: public/sw.js ================================================ // Service Worker — cache-first for static assets, network-first for navigation const CACHE_NAME = "lc-patterns-v2"; const PRECACHE_URLS = [ "./", "./manifest.json", ]; self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)) ); self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))) ) ); self.clients.claim(); }); self.addEventListener("fetch", (event) => { const { request } = event; // Skip non-GET and chrome-extension requests if (request.method !== "GET" || !request.url.startsWith("http")) return; // Skip analytics and external API calls if ( request.url.includes("google-analytics.com") || request.url.includes("googletagmanager.com") || request.url.includes("s2/favicons") ) return; // Navigation requests: network-first with cache fallback if (request.mode === "navigate") { event.respondWith( fetch(request) .then((response) => { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); return response; }) .catch(() => caches.match(request).then((cached) => cached || caches.match("./"))) ); return; } // Static assets: cache-first with network fallback event.respondWith( caches.match(request).then( (cached) => cached || fetch(request).then((response) => { // Only cache successful same-origin responses if (response.ok && request.url.startsWith(self.location.origin)) { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); } return response; }) ) ); }); ================================================ FILE: scripts/generate-sw-precache.mjs ================================================ import { readFileSync, writeFileSync, readdirSync, statSync } from "fs"; import { join, relative } from "path"; const OUT_DIR = "out"; const SW_PATH = join(OUT_DIR, "sw.js"); const PRECACHE_EXTENSIONS = new Set([ ".html", ".js", ".css", ".json", ".txt", ".woff", ".woff2", ]); function collectFiles(dir, base = dir) { const entries = []; for (const entry of readdirSync(dir)) { const full = join(dir, entry); if (statSync(full).isDirectory()) { entries.push(...collectFiles(full, base)); } else { const rel = "./" + relative(base, full); if (rel === "./sw.js") continue; if (rel.endsWith(".map")) continue; const ext = rel.slice(rel.lastIndexOf(".")); if (!PRECACHE_EXTENSIONS.has(ext)) continue; entries.push(rel); } } return entries; } const files = collectFiles(OUT_DIR); const sw = readFileSync(SW_PATH, "utf-8"); // Replace the placeholder PRECACHE_URLS array with the full file list const updated = sw.replace( /const PRECACHE_URLS = \[[\s\S]*?\];/, `const PRECACHE_URLS = ${JSON.stringify(files, null, 2)};` ); writeFileSync(SW_PATH, updated); console.log(`Precache manifest: ${files.length} files injected into sw.js`); ================================================ FILE: src/app/globals.css ================================================ @import "tailwindcss"; @custom-variant dark (&:where(.dark, .dark *)); :root { --background: #ffffff; --foreground: #171717; } .dark { --background: #0a0a0a; --foreground: #ededed; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } body { background: var(--background); color: var(--foreground); font-family: Arial, Helvetica, sans-serif; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(1rem); } to { opacity: 1; transform: translateY(0); } } ================================================ FILE: src/app/layout.tsx ================================================ import type { Metadata } from "next"; import { GoogleAnalytics } from "@next/third-parties/google"; import { Geist, Geist_Mono, Dancing_Script } from "next/font/google"; import "./globals.css"; import ServiceWorkerRegistrar from "@/components/layout/ServiceWorkerRegistrar"; import { AuthProvider } from "@/components/layout/AuthContext"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); const dancingScript = Dancing_Script({ variable: "--font-dancing-script", subsets: ["latin"], }); const siteUrl = "https://seanprashad.com/leetcode-patterns"; const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; export const metadata: Metadata = { title: "Leetcode Patterns", description: "A curated list of LeetCode questions grouped by pattern to help you ace coding interviews. Filter by difficulty, company, and topic.", manifest: `${basePath}/manifest.json`, metadataBase: new URL(siteUrl), alternates: { canonical: "/" }, openGraph: { title: "Leetcode Patterns", description: "A curated list of LeetCode questions grouped by pattern to help you ace coding interviews.", url: siteUrl, siteName: "Leetcode Patterns", images: [ { url: `${basePath}/images/og-image.png`, width: 1200, height: 630, alt: "Leetcode Patterns – A curated list of LeetCode questions grouped by pattern", }, ], type: "website", }, twitter: { card: "summary_large_image", title: "Leetcode Patterns", description: "A curated list of LeetCode questions grouped by pattern to help you ace coding interviews.", images: [`${basePath}/images/og-image.png`], }, keywords: [ "leetcode", "coding interview", "data structures", "algorithms", "interview prep", "blind 75", "leetcode patterns", ], authors: [{ name: "Sean Prashad", url: "https://github.com/SeanPrashad" }], }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return (