Skip to content

feat(pyqs): add diagram attachments with AI context support#64

Merged
imuniqueshiv merged 1 commit into
mainfrom
feature/question-diagram-support
Jul 7, 2026
Merged

feat(pyqs): add diagram attachments with AI context support#64
imuniqueshiv merged 1 commit into
mainfrom
feature/question-diagram-support

Conversation

@imuniqueshiv

@imuniqueshiv imuniqueshiv commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Pull Request

Summary

Add production-ready diagram attachment support for Previous Year Questions (PYQs), enabling circuit diagrams to be rendered alongside questions and automatically included as structured context for AI-generated answers.


Related Issue

Closes #


Type of Change

  • Feature
  • Bug Fix
  • Documentation
  • Refactor
  • Performance Improvement
  • CI / Build
  • Other

What Changed?

  • Added reusable attachment support for PYQ sub-questions through the new attachments data model.
  • Implemented secure serving of diagram images stored under the content/ directory using a dedicated API route.
  • Created reusable attachment rendering components with responsive layout, lazy loading, accessibility support, and graceful fallback handling.
  • Integrated attachment aiContext into the AI prompt builder so Gemini receives complete circuit information without OCR or Vision API.
  • Added shared TypeScript models for attachments and updated question mapping to support the new schema.
  • Added attachment path utilities and validation helpers.
  • Updated the PYQ page layout to display diagrams between the question text and AI answer section.
  • Added complete diagram metadata (title, caption, alt text, AI context) for BT-104 questions and included all available circuit diagrams.

Screenshots (UI Changes Only)

NA


Testing

  • Verified responsive rendering on desktop and mobile.
  • Tested questions with and without attachments.
  • Confirmed missing image fallback behavior.
  • Verified AI prompt includes attachment context.
  • Confirmed successful local build and production build.
  • Tested light mode and dark mode rendering.

Checklist

  • My branch is up to date with the latest main.
  • My code follows the project's coding standards.
  • I have formatted the modified files (npx prettier --write <file>).
  • npm run format:check passes.
  • npm run lint passes.
  • npm run typecheck passes.
  • npm run build passes.
  • I have updated documentation if required.
  • I have tested my changes locally.
  • This Pull Request focuses on a single feature or fix.

Additional Notes

Architecture Highlights

  • Introduced extensible attachments support for PYQs.
  • Diagram assets remain inside the content/ directory instead of public/.
  • Images are served securely through a dedicated API route.
  • AI answers now leverage structured aiContext rather than OCR or image recognition.
  • Existing questions without attachments continue to work without modification.

Future Ready

The attachment architecture is designed to support additional resource types without changing the data model, including:

  • Tables
  • Graphs
  • Mathematical equations
  • Chemical structures
  • PDF snippets
  • Animations
  • Other educational resources

This implementation is fully backward compatible with the existing PYQ system while providing a scalable foundation for richer educational content.

Summary by CodeRabbit

  • New Features

    • Added support for displaying question attachments, including images and captions, on paper pages.
    • Attachment images now load through a dedicated viewer with a clear fallback when unavailable.
    • Exam questions can now include attachment context when generating AI-assisted answers.
  • Bug Fixes

    • Improved handling for missing or invalid attachment paths with safer fallbacks and clearer availability checks.
    • Updated image handling so local diagrams and attachment URLs display correctly.

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyper-learning-tech Ready Ready Preview, Comment Jul 7, 2026 9:36am

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a question-attachment feature for PYQ pages: new types, a filesystem-backed attachment API route, path/validation utilities, image rendering components, AI prompt context injection, page wiring, Next.js image config updates, and enriched BT-104 content JSON with attachment metadata. An unrelated inline theme script is also removed from the root layout.

Changes

Question Attachment Feature

Layer / File(s) Summary
Shared PYQ types
types/pyq.ts, lib/content/question-mapper.ts
Defines QuestionAttachment, SubQuestion, Question, Paper, PYQData types; question-mapper now imports/re-exports PYQData instead of declaring its own.
Attachment validation and path resolution
lib/content/attachment-utils.ts, lib/content/attachment-path.ts
Adds runtime validation/display helpers for attachments and functions to sanitize paths, resolve safe filesystem paths within a diagrams directory, build browser API URLs, and check file existence.
Attachment serving API route
app/api/content/attachments/[branch]/[semester]/[subject]/[...path]/route.ts
New GET handler resolves the requested file, serves it with MIME/cache headers, or returns 400/404 JSON on invalid/missing paths.
Attachment image UI components
components/pyqs/attachment-image.tsx, components/pyqs/question-attachments.tsx
Adds AttachmentImage with error-fallback UI and QuestionAttachments which validates, resolves, and renders attachment images.
AI prompt attachment context
lib/ai/prompt-builder.ts
Adds buildQuestionWithAttachments, appending collected aiContext strings to the question text.
PYQ page integration
app/rgpv/[branch]/[semester]/[subject]/pyqs/[paper]/page.tsx
Renders QuestionAttachments per sub-question, updates subQuestions typing to SubQuestion, and passes attachment-aware question text to GenerateAnswerButton.
BT-104 content enrichment
content/rgpv/common/semester-2/bt-104/pyqs.json
Adds attachments and aiContext metadata to multiple sub-questions across several exam sessions.
Image config and layout cleanup
next.config.ts, app/layout.tsx
Adds images.localPatterns allowlist for attachment URLs; removes the inline localStorage theme script from RootLayout.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PaperPage
  participant QuestionAttachments
  participant AttachmentPathUtils
  participant AttachmentImage
  participant AttachmentApiRoute

  PaperPage->>QuestionAttachments: render(attachments, branch, semester, subject)
  QuestionAttachments->>AttachmentPathUtils: getAttachmentPath / attachmentFileExists
  AttachmentPathUtils-->>QuestionAttachments: src url / exists boolean
  QuestionAttachments->>AttachmentImage: render(src, invalidPath)
  AttachmentImage->>AttachmentApiRoute: GET /api/content/attachments/...
  AttachmentApiRoute-->>AttachmentImage: file bytes or 404 JSON
Loading

Possibly related PRs

  • imuniqueshiv/HyperLearningTech#22: Both PRs touch content/rgpv/common/semester-2/bt-104/pyqs.json—the earlier PR adds the BT-104 PYQ dataset that this PR later enriches with attachments and aiContext.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main feature: PYQ diagram attachments with AI context support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/question-diagram-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
types/pyq.ts (1)

10-18: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

title/alt/caption/aiContext typed as required but treated as optional downstream.

QuestionAttachment declares title, alt, caption, and aiContext as required string fields, but lib/content/attachment-utils.ts (getAttachmentAlt, getAttachmentTitle, getAttachmentCaption) accesses them with optional chaining (attachment.alt?.trim()), and isQuestionAttachment never validates these fields at runtime. Since attachment data ultimately comes from JSON content files (not statically type-checked), these fields can legitimately be missing at runtime, and the type signature gives false confidence to any consumer that reads them directly without the helper functions.

🔧 Suggested fix
 export interface QuestionAttachment {
   id: string;
   type: QuestionAttachmentType;
   path: string;
-  title: string;
-  alt: string;
-  caption: string;
-  aiContext: string;
+  title?: string;
+  alt?: string;
+  caption?: string;
+  aiContext?: string;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@types/pyq.ts` around lines 10 - 18, QuestionAttachment currently marks title,
alt, caption, and aiContext as required even though attachment-utils helpers
treat them as optional and runtime JSON may omit them. Update the
QuestionAttachment type in pyq.ts to make these fields optional (or otherwise
align the interface with the actual runtime shape), and keep
isQuestionAttachment and the
getAttachmentAlt/getAttachmentTitle/getAttachmentCaption helpers consistent with
that contract so consumers do not assume guaranteed string values.
next.config.ts (1)

6-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant localPatterns entry.

The "/**" pattern already matches all local paths, including /api/content/attachments/**, so the second entry is dead config. Harmless as-is, but simplify if this list expands later.

♻️ Simplify
     localPatterns: [
       {
         pathname: "/**", // allow existing local images such as /hl-logo.png
       },
-      {
-        pathname: "/api/content/attachments/**",
-      },
     ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@next.config.ts` around lines 6 - 13, The local image pattern list is
redundant because the broad pathname pattern in the config already covers the
attachments path. Update the image config to keep only the needed unique entry
in the localPatterns array, using the existing localPatterns setup in
next.config.ts as the place to simplify and avoid overlapping patterns.
app/api/content/attachments/[branch]/[semester]/[subject]/[...path]/route.ts (1)

51-53: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Whole file buffered into memory via fs.readFile.

For larger attachments (e.g. scanned PDFs), buffering the entire file for every request adds memory pressure. Consider streaming the file (e.g. fs.createReadStream piped into the response) if attachment sizes are expected to grow beyond small diagram images.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/content/attachments/`[branch]/[semester]/[subject]/[...path]/route.ts
around lines 51 - 53, The attachment handler in route.ts is buffering the entire
file with fs.readFile, which can create unnecessary memory pressure for larger
uploads. Update the file-serving logic in the request handler to stream the
attachment instead of loading it all at once, using the existing filePath-based
flow and preserving the current content-type/response handling in the route.
components/pyqs/question-attachments.tsx (1)

58-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent logging level between attachment warning paths.

Invalid-path and missing-file warnings (lines 59-61, 78-80) log unconditionally via console.warn, while malformed/unsupported-type warnings use warnInDevelopment (dev-only). If this is intentional (ops visibility for missing content in production), consider a brief comment; otherwise align them for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/pyqs/question-attachments.tsx` around lines 58 - 81, The
attachment warning paths in question-attachments.tsx use different logging
behaviors: the invalid-path and missing-file branches in the attachment
rendering flow log with console.warn while the malformed/unsupported-type paths
use warnInDevelopment. Update the logging in the AttachmentImage/attachment
existence checks so the behavior is consistent with the intended policy, or add
a brief inline comment in the relevant branches explaining why these warnings
are emitted unconditionally.
components/pyqs/attachment-image.tsx (1)

66-75: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Fixed 960×720 intrinsic size can cause layout shift for non-4:3 diagrams.

Hardcoding width/height regardless of each diagram's real aspect ratio means the browser reserves a 4:3 box before load; once a differently-shaped image loads, the box resizes, producing CLS. Consider storing actual dimensions per attachment or wrapping in an aspect-ratio-agnostic container (e.g. fill with a CSS aspect-ratio derived from metadata).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/pyqs/attachment-image.tsx` around lines 66 - 75, The fixed 960×720
intrinsic size in AttachmentImage can cause layout shift for images that are not
4:3. Update the Image usage in attachment-image.tsx so it uses the real
dimensions for each attachment or a metadata-driven, aspect-ratio-aware layout
instead of hardcoded width and height. Locate the logic around AttachmentImage
and its Image props, and adjust the component to reserve space based on the
actual image aspect ratio while preserving lazy loading and error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/api/content/attachments/`[branch]/[semester]/[subject]/[...path]/route.ts:
- Around line 25-32: The GET handler in the route’s attachment path parsing is
double-decoding the catch-all segments, which can break valid filenames
containing percent characters. Update the logic in GET to stop calling
decodeURIComponent on pathSegments and build attachmentPath directly from
pathSegments.join("/"), keeping the rest of the RouteContext params handling
unchanged.

In `@components/pyqs/attachment-image.tsx`:
- Around line 61-77: The image wrapper in AttachmentImage is hiding meaningful
content from assistive technology because the container is marked
aria-hidden="true" while the Image has a useful alt. Remove aria-hidden from the
wrapper in attachment-image so the diagram and its alt text remain exposed to
the accessibility tree, keeping the existing Image rendering and error handling
intact.

In `@lib/content/attachment-path.ts`:
- Around line 14-16: The path handling in normalizeSegment() and the attachment
path builder is only lowercasing inputs, so unsafe branch/semester/subject
values like path traversal segments can escape the intended content root. Add
validation in the code path that builds diagramsDir and any path.join usage to
reject traversal/absolute segments before combining them, or alternatively
resolve the final path and ensure it is anchored under CONTENT_BASE_DIR. Use the
unique symbols normalizeSegment, diagramsDir, and CONTENT_BASE_DIR to locate the
fix.

---

Nitpick comments:
In
`@app/api/content/attachments/`[branch]/[semester]/[subject]/[...path]/route.ts:
- Around line 51-53: The attachment handler in route.ts is buffering the entire
file with fs.readFile, which can create unnecessary memory pressure for larger
uploads. Update the file-serving logic in the request handler to stream the
attachment instead of loading it all at once, using the existing filePath-based
flow and preserving the current content-type/response handling in the route.

In `@components/pyqs/attachment-image.tsx`:
- Around line 66-75: The fixed 960×720 intrinsic size in AttachmentImage can
cause layout shift for images that are not 4:3. Update the Image usage in
attachment-image.tsx so it uses the real dimensions for each attachment or a
metadata-driven, aspect-ratio-aware layout instead of hardcoded width and
height. Locate the logic around AttachmentImage and its Image props, and adjust
the component to reserve space based on the actual image aspect ratio while
preserving lazy loading and error handling.

In `@components/pyqs/question-attachments.tsx`:
- Around line 58-81: The attachment warning paths in question-attachments.tsx
use different logging behaviors: the invalid-path and missing-file branches in
the attachment rendering flow log with console.warn while the
malformed/unsupported-type paths use warnInDevelopment. Update the logging in
the AttachmentImage/attachment existence checks so the behavior is consistent
with the intended policy, or add a brief inline comment in the relevant branches
explaining why these warnings are emitted unconditionally.

In `@next.config.ts`:
- Around line 6-13: The local image pattern list is redundant because the broad
pathname pattern in the config already covers the attachments path. Update the
image config to keep only the needed unique entry in the localPatterns array,
using the existing localPatterns setup in next.config.ts as the place to
simplify and avoid overlapping patterns.

In `@types/pyq.ts`:
- Around line 10-18: QuestionAttachment currently marks title, alt, caption, and
aiContext as required even though attachment-utils helpers treat them as
optional and runtime JSON may omit them. Update the QuestionAttachment type in
pyq.ts to make these fields optional (or otherwise align the interface with the
actual runtime shape), and keep isQuestionAttachment and the
getAttachmentAlt/getAttachmentTitle/getAttachmentCaption helpers consistent with
that contract so consumers do not assume guaranteed string values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1dc1b1e-ab94-4bb1-a7f2-b32dcf35b268

📥 Commits

Reviewing files that changed from the base of the PR and between 245249e and 150c2e0.

📒 Files selected for processing (25)
  • app/api/content/attachments/[branch]/[semester]/[subject]/[...path]/route.ts
  • app/layout.tsx
  • app/rgpv/[branch]/[semester]/[subject]/pyqs/[paper]/page.tsx
  • components/pyqs/attachment-image.tsx
  • components/pyqs/question-attachments.tsx
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2023/Q.1-a-dec-2023.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2023/Q.1-b-dec-2023.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2024/Q.1-b-dec-2024.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2024/Q.2-a-dec-2024.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2025/Q.1-b-dec-2025.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/december-2025/Q.6-a-dec-2025.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/june-2023/Q.1-a-june-2023.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/june-2023/Q.1-b-june-2023.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/june-2024/Q.2-a-june-2024.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/june-2025/Q.1-b-june-2025.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/june-2025/Q.2-b-june-2025.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/november-2022/Q.1-a-dec-2022.webp
  • content/rgpv/common/semester-2/bt-104/diagrams/november-2022/Q.1-b-dec-2022.webp
  • content/rgpv/common/semester-2/bt-104/pyqs.json
  • lib/ai/prompt-builder.ts
  • lib/content/attachment-path.ts
  • lib/content/attachment-utils.ts
  • lib/content/question-mapper.ts
  • next.config.ts
  • types/pyq.ts
💤 Files with no reviewable changes (1)
  • app/layout.tsx

Comment thread components/pyqs/attachment-image.tsx
Comment thread lib/content/attachment-path.ts
@imuniqueshiv imuniqueshiv merged commit d066c13 into main Jul 7, 2026
5 checks passed
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🎉 Congratulations @imuniqueshiv!

Thank you for contributing to HyperLearningTech.

Your pull request has been successfully merged into main.

📦 Merge Summary

🚀 Keep Contributing

  • Follow the CONTRIBUTING.md guidelines.
  • Keep each Pull Request focused on a single feature or fix.
  • Run formatting, linting, type checking, and a production build before opening a PR.

Thank you for helping make HyperLearningTech better.

Happy Coding! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant