style: complete redo of the email layouts (#20)
This commit is contained in:
@@ -63,7 +63,11 @@ function ConfirmationPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Just a second...';
|
return (
|
||||||
|
<CardDescription className='text-center'>
|
||||||
|
Just a second...
|
||||||
|
</CardDescription>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import React from 'react';
|
||||||
import Note from './components/Note';
|
import Note from './components/Note';
|
||||||
import Template from './Template';
|
import Template from './Template';
|
||||||
|
|
||||||
@@ -8,21 +9,32 @@ export default function ConfirmationTemplate(code: string) {
|
|||||||
<Template
|
<Template
|
||||||
title='Welcome!'
|
title='Welcome!'
|
||||||
body={
|
body={
|
||||||
<div style={{ fontSize: '1rem', color: '#4a5568' }}>
|
<div style={{ color: '#4A5568' }}>
|
||||||
<p>Dear subscriber,</p>
|
<p>Dear subscriber,</p>
|
||||||
<p style={{ marginTop: '0.5rem' }}>
|
<p style={{ marginTop: '16px' }}>
|
||||||
thank you for subscribing to our newsletter! Please click the link
|
Thank you for subscribing to our newsletter! Please click the link
|
||||||
below to confirm your subscription.
|
below to confirm your subscription.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
margin: '2rem 0',
|
margin: '32px 0',
|
||||||
display: 'flex',
|
textAlign: 'center'
|
||||||
justifyContent: 'center'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a href={`${process.env.HOME_URL}/confirmation?code=${code}`}>
|
<a
|
||||||
{'Confirm subscription'}
|
href={`${process.env.HOME_URL}/confirmation?code=${code}`}
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: '12px 24px',
|
||||||
|
backgroundColor: '#9333EA',
|
||||||
|
color: 'white',
|
||||||
|
textDecoration: 'none',
|
||||||
|
borderRadius: '6px',
|
||||||
|
fontWeight: '500',
|
||||||
|
border: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm subscription
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Note>
|
<Note>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { getSayings } from '@utils/getSayings';
|
import React from 'react';
|
||||||
import { summirize } from '@utils/summarize';
|
import { summirize } from '@utils/summarize';
|
||||||
import { NewsType } from '@utils/validationSchemas';
|
import { NewsType } from '@utils/validationSchemas';
|
||||||
import createDOMPurify from 'isomorphic-dompurify';
|
import createDOMPurify from 'isomorphic-dompurify';
|
||||||
import Template from './Template';
|
import Template from './Template';
|
||||||
|
import extractMainTopic from '@utils/extractMainTopic';
|
||||||
|
|
||||||
export default async function NewsletterTemplate(stories: NewsType[]) {
|
export default async function NewsletterTemplate(stories: NewsType[]) {
|
||||||
const summary = await summirize(stories);
|
const summary = await summirize(stories);
|
||||||
const sanitizedSummary = createDOMPurify.sanitize(summary, {
|
const sanitizedSummary = createDOMPurify.sanitize(summary, {
|
||||||
USE_PROFILES: { html: true }
|
USE_PROFILES: { html: true },
|
||||||
|
ADD_ATTR: ['target', 'rel', 'style']
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sanitizedSummary) {
|
if (!sanitizedSummary) {
|
||||||
@@ -15,17 +17,51 @@ export default async function NewsletterTemplate(stories: NewsType[]) {
|
|||||||
throw new Error('Failed to sanitize summary');
|
throw new Error('Failed to sanitize summary');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const topic = extractMainTopic(sanitizedSummary);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subject: `What's new from the Hackernews forum?`,
|
subject: topic,
|
||||||
template: (
|
template: (
|
||||||
<Template
|
<Template
|
||||||
title={`Here is something
|
variant='newsletter'
|
||||||
${getSayings[Math.floor(Math.random() * getSayings.length)]}!`}
|
title='Your Daily Tech Updates'
|
||||||
body={
|
body={
|
||||||
<div
|
<div
|
||||||
style={{ fontSize: '1rem', color: '#4a5568' }}
|
style={{
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizedSummary }}
|
color: '#374151',
|
||||||
/>
|
fontSize: '16px',
|
||||||
|
lineHeight: 1.6
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
.what-to-watch {
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 24px;
|
||||||
|
background: linear-gradient(135deg, rgba(186,27,88,0.03) 0%, rgba(245,161,98,0.03) 100%);
|
||||||
|
border-left: 3px solid #BA1B58;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.what-to-watch h3 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: #BA1B58;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
.what-to-watch p {
|
||||||
|
margin: 0;
|
||||||
|
color: #4A5568;
|
||||||
|
}
|
||||||
|
.what-to-watch a {
|
||||||
|
color: #9333EA !important;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: sanitizedSummary }} />
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,31 +1,76 @@
|
|||||||
|
import React from 'react';
|
||||||
import Footer from './components/Footer';
|
import Footer from './components/Footer';
|
||||||
|
|
||||||
interface TemplateProps {
|
interface TemplateProps {
|
||||||
title: string;
|
title: string;
|
||||||
body: JSX.Element;
|
body: JSX.Element;
|
||||||
|
variant?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Template({ title, body }: TemplateProps) {
|
export default function Template({
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
variant = 'default'
|
||||||
|
}: TemplateProps) {
|
||||||
|
const isNewsletter = variant === 'newsletter';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
maxWidth: '720px',
|
maxWidth: '720px',
|
||||||
alignContent: 'center',
|
margin: '0 auto',
|
||||||
alignItems: 'center',
|
fontFamily: 'system-ui, -apple-system, sans-serif',
|
||||||
backgroundColor: '#F9FBFB'
|
backgroundColor: '#ffffff'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h2
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: '20px',
|
background: 'linear-gradient(135deg, #9333EA 0%, #F5A162 100%)',
|
||||||
textAlign: 'center',
|
padding: '28px 24px',
|
||||||
color: 'white',
|
borderTopLeftRadius: '8px',
|
||||||
backgroundColor: `#8230CC`
|
borderTopRightRadius: '8px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title}
|
<p
|
||||||
</h2>
|
style={{
|
||||||
<div style={{ margin: '20px', padding: '20px' }}>{body}</div>
|
margin: '0 0 8px 0',
|
||||||
|
fontSize: '12px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Hackernews + newsletter
|
||||||
|
</p>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
fontSize: '24px',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'white',
|
||||||
|
fontWeight: '600',
|
||||||
|
margin: '0',
|
||||||
|
letterSpacing: '-0.02em'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
padding: '32px 24px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: '#374151',
|
||||||
|
fontSize: isNewsletter ? '16px' : '14px',
|
||||||
|
lineHeight: '1.6'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import React from 'react';
|
||||||
import Note from './components/Note';
|
import Note from './components/Note';
|
||||||
import Template from './Template';
|
import Template from './Template';
|
||||||
|
|
||||||
@@ -6,27 +7,43 @@ export default function UnsubscribeTemplate() {
|
|||||||
subject: 'Unsubscribe confirmation',
|
subject: 'Unsubscribe confirmation',
|
||||||
template: (
|
template: (
|
||||||
<Template
|
<Template
|
||||||
title="We're sad to see you go :("
|
title="We're sad to see you go"
|
||||||
body={
|
body={
|
||||||
<div style={{ fontSize: '1rem', color: '#4a5568' }}>
|
<div style={{ color: '#4A5568' }}>
|
||||||
<p style={{ marginTop: '0.5rem' }}>
|
<p style={{ marginTop: '16px' }}>
|
||||||
You have been successfully unsubscribed from our newsletter. You
|
You have been successfully unsubscribed from our newsletter. You
|
||||||
won't receive any further communications from us unless you
|
won't receive any further communications from us unless you
|
||||||
explicitly opt-in again.
|
explicitly opt-in again.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
margin: '2rem 0',
|
margin: '32px 0',
|
||||||
display: 'flex',
|
textAlign: 'center'
|
||||||
justifyContent: 'center'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a href={`${process.env.HOME_URL}/`}>{'Re-subscribe'}</a>
|
<a
|
||||||
|
href={`${process.env.HOME_URL}/`}
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: '12px 24px',
|
||||||
|
backgroundColor: '#9333EA',
|
||||||
|
color: 'white',
|
||||||
|
textDecoration: 'none',
|
||||||
|
borderRadius: '6px',
|
||||||
|
fontWeight: '500',
|
||||||
|
border: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Re-subscribe
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Note>
|
<Note>
|
||||||
If you have any questions or concerns, please feel free to{' '}
|
If you have any questions or concerns, please feel free to{' '}
|
||||||
<a
|
<a
|
||||||
style={{ color: '#3b82f6', textDecoration: 'underline' }}
|
style={{
|
||||||
|
color: '#386FA4',
|
||||||
|
textDecoration: 'none'
|
||||||
|
}}
|
||||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
||||||
>
|
>
|
||||||
contact us
|
contact us
|
||||||
|
|||||||
@@ -1,35 +1,167 @@
|
|||||||
|
import {
|
||||||
|
User,
|
||||||
|
Building2,
|
||||||
|
Mail,
|
||||||
|
LogOut,
|
||||||
|
LayoutGrid,
|
||||||
|
Shield,
|
||||||
|
Home
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer
|
||||||
style={{
|
style={{
|
||||||
|
background: 'linear-gradient(180deg, #F8FAFC 0%, #F1F4F9 100%)',
|
||||||
paddingTop: '1.5rem',
|
paddingTop: '1.5rem',
|
||||||
backgroundColor: `#E6F2F2`,
|
borderBottomLeftRadius: '8px',
|
||||||
color: 'black'
|
borderBottomRightRadius: '8px',
|
||||||
|
borderLeft: '1px solid #E5E9F0',
|
||||||
|
borderRight: '1px solid #E5E9F0',
|
||||||
|
borderBottom: '1px solid #E5E9F0'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginLeft: '2rem',
|
margin: '0 2rem',
|
||||||
|
paddingBottom: '2rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
justifyContent: 'space-between'
|
||||||
justifyContent: 'space-between',
|
|
||||||
paddingBottom: '2rem'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h4 style={{ fontSize: '1.125rem', fontWeight: '600' }}>
|
<h4
|
||||||
|
style={{
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: '600',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
marginTop: '0',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#2F4858',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.05em'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<User size={16} color='#386FA4' />
|
||||||
Contact Us
|
Contact Us
|
||||||
</h4>
|
</h4>
|
||||||
<p>{process.env.NEXT_PUBLIC_BRAND_NAME}</p>
|
<p
|
||||||
<p>
|
style={{
|
||||||
Email:{' '}
|
margin: '0.5rem 0',
|
||||||
<a href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}>
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#4A5568'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Building2 size={14} color='#386FA4' />
|
||||||
|
{process.env.NEXT_PUBLIC_BRAND_NAME}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#4A5568'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Mail size={14} color='#386FA4' />
|
||||||
|
<a
|
||||||
|
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
||||||
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
{process.env.NEXT_PUBLIC_BRAND_EMAIL}
|
{process.env.NEXT_PUBLIC_BRAND_EMAIL}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p
|
||||||
Click <a href={`${process.env.HOME_URL}/unsubscribe`}>here</a> to
|
style={{
|
||||||
unsubscribe.
|
margin: '0.5rem 0',
|
||||||
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#4A5568'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogOut size={14} color='#386FA4' />
|
||||||
|
<span>
|
||||||
|
Click{' '}
|
||||||
|
<a
|
||||||
|
href={`${process.env.HOME_URL}/unsubscribe`}
|
||||||
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>{' '}
|
||||||
|
to unsubscribe
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '1px',
|
||||||
|
background: 'linear-gradient(180deg, #E5E9F0 0%, transparent 100%)',
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
height: 'auto'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h4
|
||||||
|
style={{
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: '600',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
marginTop: '0',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#2F4858',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.05em'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LayoutGrid size={16} color='#386FA4' />
|
||||||
|
Quick Links
|
||||||
|
</h4>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#4A5568'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Shield size={14} color='#386FA4' />
|
||||||
|
<a
|
||||||
|
href={`${process.env.HOME_URL}/privacy`}
|
||||||
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
Privacy Policy
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
fontSize: '13px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
color: '#4A5568'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Home size={14} color='#386FA4' />
|
||||||
|
<a
|
||||||
|
href={process.env.HOME_URL}
|
||||||
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
Visit Website
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,15 +4,8 @@ interface NoteProps {
|
|||||||
|
|
||||||
export default function Note({ children }: NoteProps) {
|
export default function Note({ children }: NoteProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className='mt-6 rounded-md bg-gray-50 p-4 text-sm text-gray-600'>
|
||||||
style={{
|
{children}
|
||||||
marginTop: '2rem',
|
|
||||||
padding: '1.5rem 1.5rem',
|
|
||||||
backgroundColor: '#EBF1F5',
|
|
||||||
color: '#718096'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ fontSize: '0.875rem' }}>{children}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"build": "prisma generate && next build",
|
"build": "prisma generate && next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"format": "prettier --config .prettierrc 'app/' --write",
|
"format": "prettier --config .prettierrc '**/*.{js,jsx,ts,tsx,json,css,scss,md}' --write",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"analyze": "BUNDLE_ANALYZE=true next build",
|
"analyze": "BUNDLE_ANALYZE=true next build",
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"isomorphic-dompurify": "^2.15.0",
|
"isomorphic-dompurify": "^2.15.0",
|
||||||
|
"lucide-react": "^0.460.0",
|
||||||
"next": "^14.2.10",
|
"next": "^14.2.10",
|
||||||
"postcss-nesting": "^12.0.2",
|
"postcss-nesting": "^12.0.2",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|||||||
65
utils/extractMainTopic.ts
Normal file
65
utils/extractMainTopic.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
export default function extractMainTopic(summary: string): string {
|
||||||
|
const firstParaMatch = summary.match(/<p>(.*?)<\/p>/);
|
||||||
|
if (!firstParaMatch) return 'tech updates';
|
||||||
|
|
||||||
|
const firstPara = firstParaMatch[1];
|
||||||
|
|
||||||
|
const cleanText = firstPara
|
||||||
|
.replace(/<[^>]*>/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.replace(/[,.]+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const techAreas = [
|
||||||
|
{
|
||||||
|
name: 'AI',
|
||||||
|
pattern: /\b(AI|artificial intelligence|machine learning|ML|GPT|LLM)\b/i,
|
||||||
|
preserveCase: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'security',
|
||||||
|
pattern: /\b(security|cyber|hack|privacy|breach)\b/i
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Web3',
|
||||||
|
pattern: /\b(blockchain|crypto|web3|NFT|token|ethereum|bitcoin)\b/i,
|
||||||
|
preserveCase: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cloud',
|
||||||
|
pattern: /\b(cloud|AWS|Azure|serverless|containerization|kubernetes)\b/i
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mobile',
|
||||||
|
pattern: /\b(mobile|iOS|Android|app|smartphone)\b/i
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'development',
|
||||||
|
pattern: /\b(developer|programming|code|software|development)\b/i
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const area of techAreas) {
|
||||||
|
if (area.pattern.test(cleanText)) {
|
||||||
|
return area.preserveCase ? area.name : area.name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = cleanText
|
||||||
|
.split(' ')
|
||||||
|
.filter(
|
||||||
|
word =>
|
||||||
|
word.length > 2 &&
|
||||||
|
!/^(and|the|in|of|at|by|for)$/i.test(word) &&
|
||||||
|
!/[,.]/.test(word)
|
||||||
|
)
|
||||||
|
.slice(0, 2)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
const topic = words.toLowerCase() || 'tech updates';
|
||||||
|
|
||||||
|
const titleSuffix =
|
||||||
|
topic === 'tech updates' ? topic : `tech: ${topic} in focus`;
|
||||||
|
|
||||||
|
return `Today in ${titleSuffix}`;
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
export const getSayings = [
|
|
||||||
'hot off the press',
|
|
||||||
'straight from the oven',
|
|
||||||
"straight from the horse's mouth",
|
|
||||||
'brand spanking new',
|
|
||||||
'fresh as a daisy',
|
|
||||||
'straight out of the box',
|
|
||||||
'straight off the assembly line',
|
|
||||||
'hot out of the kitchen',
|
|
||||||
'just minted',
|
|
||||||
'freshly brewed',
|
|
||||||
'just off the production line'
|
|
||||||
];
|
|
||||||
@@ -7,7 +7,7 @@ export async function summirize(news: NewsType[]) {
|
|||||||
.join('\n\n');
|
.join('\n\n');
|
||||||
|
|
||||||
const promptSetup =
|
const promptSetup =
|
||||||
"You are a tech journalist with a technology degree and background. Summarize the following list of posts from an online forum as a TL;DR (Too Long; Didn't Read) summary. Your summary should:\n\n1. Be 300-400 words long (not counting the urls).\n\n2. Consist multiple phrases in one single paragraph, combining related news items where possible.\n\n3. Highlight the 2-3 most significant or impactful news items.\n\n4. Provide context within broader tech trends or developments.\n\n5. Use a tone that is informative and slightly enthusiastic, aimed at tech-savvy general readers, careful at not repeating the same phrasing and do not sound too proseic.\n\n6. Group news items by themes or technology areas if applicable.\n\n7. Be formatted for HTML use, with links incorporated as follows and including at most 3-4 words: <a href='[LINK]'>[linked text]</a>.\n\n8. Each news that is mentioned needs to incorporare its own url link, if it exists.\n\nFocus on conveying the key points and their potential impact on the tech landscape. Your response should consist of the summary only.\n\nThe news items are structured as follows:\n\nTITLE: <title>\n\nCONTENT: <content>\n\nLINK: <link>\n\nPlease summarize the following news:";
|
'You are a tech journalist with a technology degree and background. Summarize the following list of posts from an online forum as a TL;DR (Too Long; Didn't Read) summary. Your summary should:\n\n1. Be 300-400 words long (not counting the urls).\n\n2. Structure the content in 2-3 short paragraphs, with each paragraph focusing on a specific theme or technology area.\n\n3. Start with the 2-3 most significant or impactful news items in the first paragraph.\n\n4. Use HTML paragraph tags (<p>) to separate paragraphs for better readability.\n\n5. Use a tone that is informative and slightly enthusiastic, aimed at tech-savvy general readers.\n\n6. Incorporate links as follows, including at most 3-4 words: <a href='[LINK]' target='_blank' rel='noopener noreferrer'>[linked text]</a>.\n\n7. Each mentioned news item must include its own url link.\n\n8. End with a section wrapped in a div with inline styles: <div style='margin-top: 24px; padding: 20px; background: #F8FAFC; border-left: 3px solid #386FA4; border-radius: 4px;'>. Inside this div, start with an <h3 style='margin: 0 0 12px 0; color: #386FA4; font-size: 18px; font-weight: 600;'>What to Watch</h3> followed by a paragraph highlighting emerging trends or developments to follow.</div>\n\nFocus on conveying the key points and their potential impact on the tech landscape. Your response should consist of the summary only.\n\nThe news items are structured as follows:\n\nTITLE: <title>\nCONTENT: <content>\nLINK: <link>\n\nPlease summarize the following news:';
|
||||||
try {
|
try {
|
||||||
const response = await message(promptSetup + newsInput);
|
const response = await message(promptSetup + newsInput);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user