feat: refine portfolio content, contact, and supporting media
This commit is contained in:
9
src/content/contact-email.ts
Normal file
9
src/content/contact-email.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ContactContent } from './types';
|
||||
|
||||
// The page carries separate parts; a complete address is assembled only when
|
||||
// someone interacts. This deters simple harvesting, not a determined crawler.
|
||||
export const contactAddress = (email: ContactContent['email']): string =>
|
||||
`${email.localPart}@${email.domain}`;
|
||||
|
||||
export const readableContactAddress = (email: ContactContent['email']): string =>
|
||||
`${email.localPart} [at] ${email.domain.replaceAll('.', ' [dot] ')}`;
|
||||
@@ -22,7 +22,6 @@ interface PersonJsonLd {
|
||||
jobTitle: string;
|
||||
description: string;
|
||||
url: string;
|
||||
email: string;
|
||||
homeLocation: PlaceJsonLd;
|
||||
sameAs?: string[];
|
||||
}
|
||||
@@ -104,7 +103,6 @@ export const createPageMetadata = (
|
||||
jobTitle: content.identity.role,
|
||||
description: content.metadata.description,
|
||||
url: site.canonicalUrl,
|
||||
email: `mailto:${content.contact.email}`,
|
||||
homeLocation: {
|
||||
'@type': 'Place',
|
||||
address: {
|
||||
|
||||
@@ -201,7 +201,12 @@ const portfolioContentSchema = z.object({
|
||||
}).strict()).min(1),
|
||||
contact: z.object({
|
||||
introduction: visibleString,
|
||||
email: z.email(),
|
||||
email: z.object({ localPart: visibleString, domain: visibleString }).strict().refine(
|
||||
({ localPart, domain }) => !localPart.includes('@') && !domain.includes('@') &&
|
||||
z.email().safeParse(`${localPart}@${domain}`).success,
|
||||
'Contact email parts must form a valid address.',
|
||||
),
|
||||
avatarAssetId: opaqueId.optional(),
|
||||
social: z.array(z.object({
|
||||
service: z.enum(['GitHub', 'GitLab', 'LinkedIn']),
|
||||
url: httpUrl,
|
||||
@@ -334,6 +339,7 @@ export const parseProductionInputSnapshot = (input: unknown): ProductionInputs =
|
||||
const { content, manifest, site } = productionInputsSchema.parse(input);
|
||||
const manifestIds = new Set(manifest.assets.map((asset) => asset.id));
|
||||
const referencedAssetIds = [
|
||||
...(content.contact.avatarAssetId === undefined ? [] : [content.contact.avatarAssetId]),
|
||||
...content.projects.selected.flatMap((project) => project.assetIds),
|
||||
...content.projects.additional.flatMap((project) => project.assetIds),
|
||||
...content.toolkit.flatMap((group) => group.technologies.flatMap((technology) =>
|
||||
|
||||
@@ -133,7 +133,8 @@ export interface ContactSocialLink {
|
||||
|
||||
export interface ContactContent {
|
||||
introduction: string;
|
||||
email: string;
|
||||
email: { localPart: string; domain: string };
|
||||
avatarAssetId?: string;
|
||||
social: readonly ContactSocialLink[];
|
||||
cvMessage: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user