fix: resolve multi-page PDF crashes and Gemini API ingestion errors (#2781)

* fix: resolve multi-page PDF crashes and Gemini API ingestion errors

* fix type errors

* refactor: address PR review feedback and prevent call stack recursion in regex payload scanner

---------

Co-authored-by: Ofir <ofir@example.com>
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
This commit is contained in:
obitton
2026-03-16 18:08:40 -04:00
committed by GitHub
parent ea8fd838d9
commit fb61bb4a63
8 changed files with 818 additions and 278 deletions
+22 -5
View File
@@ -3,6 +3,7 @@ import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { DownloadSimpleIcon, FileIcon, UploadSimpleIcon } from "@phosphor-icons/react";
import { useMutation } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { useEffect, useRef, useState } from "react";
import { useForm, useWatch } from "react-hook-form";
import { toast } from "sonner";
@@ -63,7 +64,21 @@ const formSchema = z.discriminatedUnion("type", [
type FormValues = z.infer<typeof formSchema>;
function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result as string;
// remove data URL prefix (e.g., "data:application/pdf;base64," or "data:application/vnd...;base64,")
resolve(result.split(",")[1]);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
export function ImportResumeDialog(_: DialogProps<"resume.import">) {
const navigate = useNavigate();
const { enabled: isAIEnabled, provider, model, apiKey, baseURL } = useAIStore();
const closeDialog = useDialogStore((state) => state.closeDialog);
@@ -135,8 +150,8 @@ export function ImportResumeDialog(_: DialogProps<"resume.import">) {
if (!isAIEnabled)
throw new Error(t`This feature requires AI Integration to be enabled. Please enable it in the settings.`);
const arrayBuffer = await values.file.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
// const arrayBuffer = await values.file.arrayBuffer();
const base64 = await fileToBase64(values.file);
data = await client.ai.parsePdf({
provider,
@@ -151,8 +166,9 @@ export function ImportResumeDialog(_: DialogProps<"resume.import">) {
if (!isAIEnabled)
throw new Error(t`This feature requires AI Integration to be enabled. Please enable it in the settings.`);
const arrayBuffer = await values.file.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
// const arrayBuffer = await values.file.arrayBuffer();
const base64 = await fileToBase64(values.file);
const mediaType =
values.file.type === "application/msword"
? ("application/msword" as const)
@@ -170,9 +186,10 @@ export function ImportResumeDialog(_: DialogProps<"resume.import">) {
if (!data) throw new Error("No data was returned from the AI provider.");
await importResume({ data });
const id = await importResume({ data });
toast.success(t`Your resume has been imported successfully.`, { id: toastId, description: null });
closeDialog();
navigate({ to: `/builder/$resumeId`, params: { resumeId: id } });
} catch (error: unknown) {
if (error instanceof Error) {
toast.error(error.message, { id: toastId, description: null });