Feature: Implement Embedding Links in Titles of all Section Items (#2662)

* feat: add options.showLinkInTitle to baseItemSchema

Add itemOptionsSchema with showLinkInTitle boolean property to control
whether the website URL is rendered as a hyperlink on the title instead
of a separate link at the bottom. The field is optional for backwards
compatibility with existing resumes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add hideLabelButton prop to URLInput

When hideLabelButton is true, the tag/label button is hidden from the
URL input. This is used when showLinkInTitle is enabled since the label
is not needed when the link is shown in the title.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: create LinkedTitle component for title-as-link rendering

Add a reusable component that conditionally renders the title as a
hyperlink when showLinkInTitle is true and a website URL is provided.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add showLinkInTitle option to experience section

- Add Switch toggle in experience dialog for showLinkInTitle option
- Update URLInput to hide label button when showLinkInTitle is enabled
- Use LinkedTitle component in experience-item for conditional link rendering
- Hide bottom website link when showLinkInTitle is enabled

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add showLinkInTitle option to all section items

- Update education, projects, awards, certifications, publications,
  volunteer, references, and profiles dialogs with Switch toggle
- Add LinkedTitle component usage in all corresponding item components
- Conditionally hide bottom website link when showLinkInTitle is enabled
- Add hideLabelButton prop to URLInput when showLinkInTitle is enabled

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: extract i18n strings for showLinkInTitle feature

Add "Show link in title" translation string to all locale catalogs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* update dependencies, fix an issue with glalie template and non-clickable links, fix better-auth type error

* remove unused export

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Amruth Pillai
2026-01-31 12:16:23 +01:00
committed by GitHub
parent 4d368ab6f6
commit 8d347f5162
82 changed files with 1085 additions and 193 deletions
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateAwardDialog({ data }: DialogProps<"resume.sections.awards.
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
awarder: data?.item?.awarder ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdateAwardDialog({ data }: DialogProps<"resume.sections.awards.
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
awarder: data.item.awarder,
date: data.item.date,
@@ -205,13 +208,33 @@ function AwardForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateCertificationDialog({ data }: DialogProps<"resume.sections
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
issuer: data?.item?.issuer ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdateCertificationDialog({ data }: DialogProps<"resume.sections
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
issuer: data.item.issuer,
date: data.item.date,
@@ -205,13 +208,33 @@ function CertificationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateEducationDialog({ data }: DialogProps<"resume.sections.edu
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
school: data?.item?.school ?? "",
degree: data?.item?.degree ?? "",
area: data?.item?.area ?? "",
@@ -92,6 +94,7 @@ export function UpdateEducationDialog({ data }: DialogProps<"resume.sections.edu
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
school: data.item.school,
degree: data.item.degree,
area: data.item.area,
@@ -259,13 +262,33 @@ function EducationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateExperienceDialog({ data }: DialogProps<"resume.sections.ex
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
company: data?.item?.company ?? "",
position: data?.item?.position ?? "",
location: data?.item?.location ?? "",
@@ -90,6 +92,7 @@ export function UpdateExperienceDialog({ data }: DialogProps<"resume.sections.ex
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
company: data.item.company,
position: data.item.position,
location: data.item.location,
@@ -223,13 +226,33 @@ function ExperienceForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -12,6 +12,7 @@ import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTit
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group";
import { Switch } from "@/components/ui/switch";
import { type DialogProps, useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
import { profileItemSchema } from "@/schema/resume/data";
@@ -31,6 +32,7 @@ export function CreateProfileDialog({ data }: DialogProps<"resume.sections.profi
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
icon: data?.item?.icon ?? "acorn",
network: data?.item?.network ?? "",
username: data?.item?.username ?? "",
@@ -90,6 +92,7 @@ export function UpdateProfileDialog({ data }: DialogProps<"resume.sections.profi
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
icon: data.item.icon,
network: data.item.network,
username: data.item.username,
@@ -216,12 +219,32 @@ function ProfileForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
</>
);
}
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateProjectDialog({ data }: DialogProps<"resume.sections.proje
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
name: data?.item?.name ?? "",
period: data?.item?.period ?? "",
website: data?.item?.website ?? { url: "", label: "" },
@@ -88,6 +90,7 @@ export function UpdateProjectDialog({ data }: DialogProps<"resume.sections.proje
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
name: data.item.name,
period: data.item.period,
website: data.item.website,
@@ -187,13 +190,33 @@ function ProjectForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2 sm:col-span-full">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreatePublicationDialog({ data }: DialogProps<"resume.sections.p
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
title: data?.item?.title ?? "",
publisher: data?.item?.publisher ?? "",
date: data?.item?.date ?? "",
@@ -89,6 +91,7 @@ export function UpdatePublicationDialog({ data }: DialogProps<"resume.sections.p
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
title: data.item.title,
publisher: data.item.publisher,
date: data.item.date,
@@ -205,13 +208,33 @@ function PublicationForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateReferenceDialog({ data }: DialogProps<"resume.sections.ref
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
name: data?.item?.name ?? "",
position: data?.item?.position ?? "",
website: data?.item?.website ?? { url: "", label: "" },
@@ -89,6 +91,7 @@ export function UpdateReferenceDialog({ data }: DialogProps<"resume.sections.ref
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
name: data.item.name,
position: data.item.position,
website: data.item.website,
@@ -205,13 +208,33 @@ function ReferenceForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
+24 -1
View File
@@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import type { DialogProps } from "@/dialogs/store";
import { useDialogStore } from "@/dialogs/store";
import { useFormBlocker } from "@/hooks/use-form-blocker";
@@ -29,6 +30,7 @@ export function CreateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
defaultValues: {
id: generateId(),
hidden: data?.item?.hidden ?? false,
options: data?.item?.options ?? { showLinkInTitle: false },
organization: data?.item?.organization ?? "",
location: data?.item?.location ?? "",
period: data?.item?.period ?? "",
@@ -89,6 +91,7 @@ export function UpdateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
defaultValues: {
id: data.item.id,
hidden: data.item.hidden,
options: data.item.options ?? { showLinkInTitle: false },
organization: data.item.organization,
location: data.item.location,
period: data.item.period,
@@ -205,13 +208,33 @@ function VolunteerForm() {
<Trans>Website</Trans>
</FormLabel>
<FormControl>
<URLInput {...field} value={field.value} onChange={field.onChange} />
<URLInput
{...field}
value={field.value}
onChange={field.onChange}
hideLabelButton={form.watch("options.showLinkInTitle")}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="options.showLinkInTitle"
render={({ field }) => (
<FormItem className="flex items-center gap-x-2">
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel className="!mt-0">
<Trans>Show link in title</Trans>
</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"