mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-25 01:15:26 +10:00
Merge pull request #2299 from Moamal-2000/improve-accessibility
fix(homepage): resolve missing button labels and incorrect heading order
This commit is contained in:
@@ -1,81 +0,0 @@
|
|||||||
# Environment
|
|
||||||
NODE_ENV=development
|
|
||||||
|
|
||||||
# Ports
|
|
||||||
PORT=3000
|
|
||||||
|
|
||||||
# URLs
|
|
||||||
# These URLs must reference a publicly accessible domain or IP address, not a docker container ID (depending on your compose setup)
|
|
||||||
PUBLIC_URL=http://localhost:3000
|
|
||||||
STORAGE_URL=http://localhost:9000/default # default is the bucket name specified in the STORAGE_BUCKET variable
|
|
||||||
|
|
||||||
# Database (Prisma/PostgreSQL)
|
|
||||||
# This can be swapped out to use any other database, like MySQL
|
|
||||||
# Note: This is used only in the compose.yml file
|
|
||||||
POSTGRES_PORT=5432
|
|
||||||
POSTGRES_DB=postgres
|
|
||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_PASSWORD=postgres
|
|
||||||
|
|
||||||
# Database (Prisma/PostgreSQL)
|
|
||||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres?schema=public
|
|
||||||
|
|
||||||
# Authentication Secrets
|
|
||||||
# generated with `openssl rand -base64 64`
|
|
||||||
ACCESS_TOKEN_SECRET=access_token_secret
|
|
||||||
REFRESH_TOKEN_SECRET=refresh_token_secret
|
|
||||||
|
|
||||||
# Chrome Browser (for printing)
|
|
||||||
# generated with `openssl rand -hex 32`
|
|
||||||
CHROME_PORT=8080
|
|
||||||
CHROME_TOKEN=chrome_token
|
|
||||||
CHROME_URL=ws://localhost:8080
|
|
||||||
# Launch puppeteer with flag to ignore https errors
|
|
||||||
# CHROME_IGNORE_HTTPS_ERRORS=true
|
|
||||||
|
|
||||||
# Mail Server (for e-mails)
|
|
||||||
# For testing, you can use https://ethereal.email/create
|
|
||||||
MAIL_FROM=noreply@localhost
|
|
||||||
# SMTP_URL=smtp://username:password@smtp.ethereal.email:587
|
|
||||||
|
|
||||||
# Storage
|
|
||||||
STORAGE_ENDPOINT=localhost
|
|
||||||
STORAGE_PORT=9000
|
|
||||||
STORAGE_REGION=us-east-1
|
|
||||||
STORAGE_BUCKET=default
|
|
||||||
STORAGE_ACCESS_KEY=minioadmin
|
|
||||||
STORAGE_SECRET_KEY=minioadmin
|
|
||||||
STORAGE_USE_SSL=false
|
|
||||||
STORAGE_SKIP_BUCKET_CHECK=false
|
|
||||||
|
|
||||||
# Nx Cloud (Optional)
|
|
||||||
# NX_CLOUD_ACCESS_TOKEN=
|
|
||||||
|
|
||||||
# Crowdin (Optional)
|
|
||||||
# CROWDIN_PROJECT_ID=
|
|
||||||
# CROWDIN_PERSONAL_TOKEN=
|
|
||||||
|
|
||||||
# Feature Flags (Optional)
|
|
||||||
# DISABLE_SIGNUPS=false
|
|
||||||
# DISABLE_EMAIL_AUTH=false
|
|
||||||
|
|
||||||
# GitHub (OAuth, Optional)
|
|
||||||
# GITHUB_CLIENT_ID=
|
|
||||||
# GITHUB_CLIENT_SECRET=
|
|
||||||
# GITHUB_CALLBACK_URL=http://localhost:5173/api/auth/github/callback
|
|
||||||
|
|
||||||
# Google (OAuth, Optional)
|
|
||||||
# GOOGLE_CLIENT_ID=
|
|
||||||
# GOOGLE_CLIENT_SECRET=
|
|
||||||
# GOOGLE_CALLBACK_URL=http://localhost:5173/api/auth/google/callback
|
|
||||||
|
|
||||||
# OpenID (Optional)
|
|
||||||
# VITE_OPENID_NAME=
|
|
||||||
# OPENID_AUTHORIZATION_URL=
|
|
||||||
# OPENID_CALLBACK_URL=http://localhost:5173/api/auth/openid/callback
|
|
||||||
# OPENID_CLIENT_ID=
|
|
||||||
# OPENID_CLIENT_SECRET=
|
|
||||||
# OPENID_ISSUER=
|
|
||||||
# OPENID_SCOPE=openid profile email
|
|
||||||
# OPENID_TOKEN_URL=
|
|
||||||
# OPENID_USER_INFO_URL=
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { t } from "@lingui/macro";
|
||||||
import { useLingui } from "@lingui/react";
|
import { useLingui } from "@lingui/react";
|
||||||
import { TranslateIcon } from "@phosphor-icons/react";
|
import { TranslateIcon } from "@phosphor-icons/react";
|
||||||
import { Button, Popover, PopoverContent, PopoverTrigger } from "@reactive-resume/ui";
|
import { Button, Popover, PopoverContent, PopoverTrigger } from "@reactive-resume/ui";
|
||||||
@@ -13,7 +14,7 @@ export const LocaleSwitch = () => {
|
|||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button size="icon" variant="ghost">
|
<Button size="icon" variant="ghost" aria-label={t`Change Language`}>
|
||||||
<TranslateIcon size={20} />
|
<TranslateIcon size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { t } from "@lingui/macro";
|
||||||
import { CloudSunIcon, MoonIcon, SunIcon } from "@phosphor-icons/react";
|
import { CloudSunIcon, MoonIcon, SunIcon } from "@phosphor-icons/react";
|
||||||
import { useTheme } from "@reactive-resume/hooks";
|
import { useTheme } from "@reactive-resume/hooks";
|
||||||
import { Button } from "@reactive-resume/ui";
|
import { Button } from "@reactive-resume/ui";
|
||||||
@@ -25,9 +26,9 @@ export const ThemeSwitch = ({ size = 20, className }: Props) => {
|
|||||||
<Button size="icon" variant="ghost" className={className} onClick={toggleTheme}>
|
<Button size="icon" variant="ghost" className={className} onClick={toggleTheme}>
|
||||||
<div className="cursor-pointer overflow-hidden" style={{ width: size, height: size }}>
|
<div className="cursor-pointer overflow-hidden" style={{ width: size, height: size }}>
|
||||||
<motion.div animate={theme} variants={variants} className="flex">
|
<motion.div animate={theme} variants={variants} className="flex">
|
||||||
<SunIcon size={size} className="shrink-0" />
|
<SunIcon size={size} className="shrink-0" aria-label={t`Switch to Light Mode`} />
|
||||||
<CloudSunIcon size={size} className="shrink-0" />
|
<CloudSunIcon size={size} className="shrink-0" aria-label={t`Use System Theme`} />
|
||||||
<MoonIcon size={size} className="shrink-0" />
|
<MoonIcon size={size} className="shrink-0" aria-label={t`Switch to Dark Mode`} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { FloppyDiskIcon, TrashSimpleIcon } from "@phosphor-icons/react";
|
|||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@@ -11,12 +12,15 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
Input,
|
Input,
|
||||||
Checkbox,
|
|
||||||
} from "@reactive-resume/ui";
|
} from "@reactive-resume/ui";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DEFAULT_MAX_TOKENS, DEFAULT_MODEL, DEFAULT_AZURE_API_VERSION } from "@/client/constants/llm";
|
import {
|
||||||
|
DEFAULT_AZURE_API_VERSION,
|
||||||
|
DEFAULT_MAX_TOKENS,
|
||||||
|
DEFAULT_MODEL,
|
||||||
|
} from "@/client/constants/llm";
|
||||||
import { useOpenAiStore } from "@/client/stores/openai";
|
import { useOpenAiStore } from "@/client/stores/openai";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
@@ -41,12 +45,18 @@ type FormValues = z.infer<typeof formSchema>;
|
|||||||
|
|
||||||
export const OpenAISettings = () => {
|
export const OpenAISettings = () => {
|
||||||
const {
|
const {
|
||||||
apiKey, setApiKey,
|
apiKey,
|
||||||
baseURL, setBaseURL,
|
setApiKey,
|
||||||
model, setModel,
|
baseURL,
|
||||||
maxTokens, setMaxTokens,
|
setBaseURL,
|
||||||
isAzure, setIsAzure,
|
model,
|
||||||
azureApiVersion, setAzureApiVersion
|
setModel,
|
||||||
|
maxTokens,
|
||||||
|
setMaxTokens,
|
||||||
|
isAzure,
|
||||||
|
setIsAzure,
|
||||||
|
azureApiVersion,
|
||||||
|
setAzureApiVersion,
|
||||||
} = useOpenAiStore();
|
} = useOpenAiStore();
|
||||||
|
|
||||||
const isEnabled = !!apiKey;
|
const isEnabled = !!apiKey;
|
||||||
@@ -58,12 +68,19 @@ export const OpenAISettings = () => {
|
|||||||
baseURL: baseURL ?? "",
|
baseURL: baseURL ?? "",
|
||||||
model: model ?? DEFAULT_MODEL,
|
model: model ?? DEFAULT_MODEL,
|
||||||
maxTokens: maxTokens ?? DEFAULT_MAX_TOKENS,
|
maxTokens: maxTokens ?? DEFAULT_MAX_TOKENS,
|
||||||
isAzure: isAzure ?? false,
|
isAzure,
|
||||||
azureApiVersion: azureApiVersion ?? DEFAULT_AZURE_API_VERSION,
|
azureApiVersion: azureApiVersion ?? DEFAULT_AZURE_API_VERSION,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = ({ apiKey, baseURL, model, maxTokens, isAzure, azureApiVersion }: FormValues) => {
|
const onSubmit = ({
|
||||||
|
apiKey,
|
||||||
|
baseURL,
|
||||||
|
model,
|
||||||
|
maxTokens,
|
||||||
|
isAzure,
|
||||||
|
azureApiVersion,
|
||||||
|
}: FormValues) => {
|
||||||
setApiKey(apiKey);
|
setApiKey(apiKey);
|
||||||
setIsAzure(isAzure);
|
setIsAzure(isAzure);
|
||||||
if (baseURL) {
|
if (baseURL) {
|
||||||
@@ -93,7 +110,7 @@ export const OpenAISettings = () => {
|
|||||||
model: DEFAULT_MODEL,
|
model: DEFAULT_MODEL,
|
||||||
maxTokens: DEFAULT_MAX_TOKENS,
|
maxTokens: DEFAULT_MAX_TOKENS,
|
||||||
isAzure: false,
|
isAzure: false,
|
||||||
azureApiVersion: DEFAULT_AZURE_API_VERSION
|
azureApiVersion: DEFAULT_AZURE_API_VERSION,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,10 +142,10 @@ export const OpenAISettings = () => {
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<Trans>
|
<Trans>
|
||||||
You can also integrate with Azure OpenAI by enabling the "Use Azure OpenAI" checkbox
|
You can also integrate with Azure OpenAI by enabling the <code>Use Azure OpenAI</code>{" "}
|
||||||
and setting the Resource URL to your Azure OpenAI resource (e.g.,
|
checkbox and setting the Resource URL to your Azure OpenAI resource:{" "}
|
||||||
<code>https://your-resource.openai.azure.com</code>). Set the deployment name in the Model field
|
<code>https://your-resource.openai.azure.com</code>. Set the deployment name in the
|
||||||
and specify the appropriate API version for your Azure deployment.
|
Model field and specify the appropriate API version for your Azure deployment.
|
||||||
</Trans>
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -136,8 +153,8 @@ export const OpenAISettings = () => {
|
|||||||
<Trans>
|
<Trans>
|
||||||
You can also integrate with Ollama simply by setting the API key to
|
You can also integrate with Ollama simply by setting the API key to
|
||||||
<code>sk-1234567890abcdef</code> and the Base URL to your Ollama URL, i.e.
|
<code>sk-1234567890abcdef</code> and the Base URL to your Ollama URL, i.e.
|
||||||
<code>http://localhost:11434/v1</code>. You can also pick and choose models and set the max tokens
|
<code>http://localhost:11434/v1</code>. You can also pick and choose models and set the
|
||||||
as per your preference.
|
max tokens as per your preference.
|
||||||
</Trans>
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,10 +180,7 @@ export const OpenAISettings = () => {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{form.watch("isAzure")
|
{form.watch("isAzure") ? t`Azure OpenAI Resource URL` : t`Base URL`}
|
||||||
? t`Azure OpenAI Resource URL`
|
|
||||||
: t`Base URL`
|
|
||||||
}
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@@ -188,12 +202,7 @@ export const OpenAISettings = () => {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>{form.watch("isAzure") ? t`Deployment Name` : t`Model`}</FormLabel>
|
||||||
{form.watch("isAzure")
|
|
||||||
? t`Deployment Name`
|
|
||||||
: t`Model`
|
|
||||||
}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="text" placeholder={DEFAULT_MODEL} {...field} />
|
<Input type="text" placeholder={DEFAULT_MODEL} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -228,7 +237,7 @@ export const OpenAISettings = () => {
|
|||||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={!!field.value}
|
checked={field.value}
|
||||||
onCheckedChange={(value) => {
|
onCheckedChange={(value) => {
|
||||||
field.onChange(Boolean(value));
|
field.onChange(Boolean(value));
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export const FeaturesSection = () => {
|
|||||||
whileInView={{ opacity: 1, x: 0, transition: { delay: index * 0.1 } }}
|
whileInView={{ opacity: 1, x: 0, transition: { delay: index * 0.1 } }}
|
||||||
>
|
>
|
||||||
{feature.icon}
|
{feature.icon}
|
||||||
<h4>{feature.title}</h4>
|
<h3>{feature.title}</h3>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|||||||
@@ -13,40 +13,25 @@ export const openai = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isAzure) {
|
if (isAzure) {
|
||||||
if (!baseURL) {
|
if (!baseURL || !model || !azureApiVersion) {
|
||||||
throw new Error(t`Azure OpenAI Base URL is required when using Azure OpenAI.`);
|
throw new Error(
|
||||||
|
t`Azure OpenAI Base URL, deployment name (model), and API version are required when using Azure OpenAI.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!model) {
|
const azureBaseURL = baseURL.replace(/\/$/, "");
|
||||||
throw new Error(t`Azure OpenAI deployment name (model) is required when using Azure OpenAI.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!azureApiVersion) {
|
|
||||||
throw new Error(t`Azure OpenAI API version is required when using Azure OpenAI.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct Azure OpenAI URL: https://your-resource.openai.azure.com/openai/deployments/your-deployment
|
|
||||||
const azureBaseURL = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
|
|
||||||
const constructedURL = `${azureBaseURL}/openai/deployments/${model}`;
|
|
||||||
|
|
||||||
return new OpenAI({
|
return new OpenAI({
|
||||||
apiKey,
|
apiKey,
|
||||||
baseURL: constructedURL,
|
baseURL: `${azureBaseURL}/openai/deployments/${model}`,
|
||||||
defaultQuery: { "api-version": azureApiVersion ?? undefined },
|
defaultQuery: { "api-version": azureApiVersion },
|
||||||
dangerouslyAllowBrowser: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseURL) {
|
|
||||||
return new OpenAI({
|
|
||||||
apiKey,
|
|
||||||
baseURL,
|
|
||||||
dangerouslyAllowBrowser: true,
|
dangerouslyAllowBrowser: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OpenAI({
|
return new OpenAI({
|
||||||
apiKey,
|
apiKey,
|
||||||
|
baseURL,
|
||||||
dangerouslyAllowBrowser: true,
|
dangerouslyAllowBrowser: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
import { DEFAULT_MAX_TOKENS, DEFAULT_MODEL, DEFAULT_AZURE_API_VERSION } from "../constants/llm";
|
import { DEFAULT_AZURE_API_VERSION, DEFAULT_MAX_TOKENS, DEFAULT_MODEL } from "../constants/llm";
|
||||||
|
|
||||||
type OpenAIStore = {
|
type OpenAIStore = {
|
||||||
baseURL: string | null;
|
baseURL: string | null;
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export class PrinterService {
|
|||||||
Promise.all(
|
Promise.all(
|
||||||
// eslint-disable-next-line unicorn/prefer-spread
|
// eslint-disable-next-line unicorn/prefer-spread
|
||||||
Array.from(document.images).map((img) => {
|
Array.from(document.images).map((img) => {
|
||||||
if (img.complete) return;
|
if (img.complete) return Promise.resolve();
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
||||||
img.onload = img.onerror = resolve;
|
img.onload = img.onerror = resolve;
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@reactive-resume/source",
|
"name": "@reactive-resume/source",
|
||||||
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||||
"version": "4.4.7",
|
"version": "4.4.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.17.1+sha512.17c560fca4867ae9473a3899ad84a88334914f379be46d455cbf92e5cf4b39d34985d452d2583baf19967fa76cb5c17bc9e245529d0b98745721aa7200ecaf7a",
|
"packageManager": "pnpm@10.17.1+sha512.17c560fca4867ae9473a3899ad84a88334914f379be46d455cbf92e5cf4b39d34985d452d2583baf19967fa76cb5c17bc9e245529d0b98745721aa7200ecaf7a",
|
||||||
|
|||||||
Reference in New Issue
Block a user