@heripo/research-radar 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +455 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +124 -16
- package/dist/index.js +455 -42
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -3,6 +3,10 @@ import { createOpenAI } from '@ai-sdk/openai';
|
|
|
3
3
|
import { DateType, GenerateNewsletter } from '@llm-newsletter-kit/core';
|
|
4
4
|
import * as cheerio from 'cheerio';
|
|
5
5
|
import TurndownService from 'turndown';
|
|
6
|
+
import { JSDOM } from 'jsdom';
|
|
7
|
+
import safeMarkdown2Html from 'safe-markdown2html';
|
|
8
|
+
import DOMPurify from 'dompurify';
|
|
9
|
+
import juice from 'juice';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* Formats a date string by replacing dots with dashes.
|
|
@@ -1706,6 +1710,74 @@ class AnalysisProvider {
|
|
|
1706
1710
|
}
|
|
1707
1711
|
}
|
|
1708
1712
|
|
|
1713
|
+
/**
|
|
1714
|
+
* Shared HTML fragments used by both newsletter and welcome email templates.
|
|
1715
|
+
*
|
|
1716
|
+
* These helpers extract identical HTML blocks to avoid duplication
|
|
1717
|
+
* while keeping template-specific styling separate.
|
|
1718
|
+
*/
|
|
1719
|
+
const purify = DOMPurify(new JSDOM('').window);
|
|
1720
|
+
/**
|
|
1721
|
+
* Sanitize user-supplied strings for safe HTML insertion.
|
|
1722
|
+
* Strips all HTML tags, leaving only plain text.
|
|
1723
|
+
*/
|
|
1724
|
+
const sanitizeText = (str) => purify.sanitize(str, { ALLOWED_TAGS: [] });
|
|
1725
|
+
/**
|
|
1726
|
+
* Heripo light/dark logo block.
|
|
1727
|
+
* @param imgMarginBottom - Margin below the logo image (e.g., '8px', '12px')
|
|
1728
|
+
*/
|
|
1729
|
+
const heripoLogoHtml = (imgMarginBottom) => `
|
|
1730
|
+
<div style="margin-bottom: 32px;">
|
|
1731
|
+
<div style="text-align: left; display: block;" class="light-logo">
|
|
1732
|
+
<img src="https://heripo.com/heripo-logo.png" width="150" alt="로고" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; margin-bottom: ${imgMarginBottom};" height="auto">
|
|
1733
|
+
</div>
|
|
1734
|
+
<!--[if !mso]><!-->
|
|
1735
|
+
<div style="text-align: left; display: none;" class="dark-logo">
|
|
1736
|
+
<img src="https://heripo.com/heripo-logo-dark.png" width="150" alt="다크모드 로고" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; margin-bottom: ${imgMarginBottom};" height="auto">
|
|
1737
|
+
</div>
|
|
1738
|
+
<!--<![endif]-->
|
|
1739
|
+
</div>`;
|
|
1740
|
+
/**
|
|
1741
|
+
* KRAS dual-logo header block.
|
|
1742
|
+
* Left: KRAS logo, Right: heripo lab logo (light/dark) + "제공" text.
|
|
1743
|
+
*/
|
|
1744
|
+
const krasHeaderHtml = () => `
|
|
1745
|
+
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="width: 100%; border-collapse: collapse; margin: 0 0 18px 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; margin-bottom: 20px; border: none;">
|
|
1746
|
+
<tr>
|
|
1747
|
+
<td align="left" valign="middle" width="50%" style="text-align: left; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; padding: 0; border: none;">
|
|
1748
|
+
<img src="https://heripo.com/kras-logo.jpeg" width="200" alt="한국고고학회" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; display: block;" height="auto">
|
|
1749
|
+
</td>
|
|
1750
|
+
<td align="right" valign="middle" width="50%" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; padding: 0; border: none; text-align: right; font-size: 0; line-height: 0; white-space: nowrap;">
|
|
1751
|
+
<div style="text-align: left; display: inline-block; vertical-align: middle; line-height: 0;" class="light-logo">
|
|
1752
|
+
<img src="https://heripo.com/heripolab-logo.png" width="120" alt="heripo lab" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; outline: none; text-decoration: none; display: inline-block; vertical-align: middle;" height="auto">
|
|
1753
|
+
</div><!--[if !mso]><!--><div style="text-align: left; display: none; vertical-align: middle; line-height: 0;" class="dark-logo dark-logo-inline">
|
|
1754
|
+
<img src="https://heripo.com/heripolab-logo-dark.png" width="120" alt="heripo lab" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; outline: none; text-decoration: none; display: inline-block; vertical-align: middle;" height="auto">
|
|
1755
|
+
</div><!--<![endif]--><span class="header-dark-text" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 20px; font-weight: normal; color: #666666; line-height: 1; vertical-align: middle; padding-left: 4px;">제공</span>
|
|
1756
|
+
</td>
|
|
1757
|
+
</tr>
|
|
1758
|
+
</table>`;
|
|
1759
|
+
/**
|
|
1760
|
+
* Heripo platform introduction section.
|
|
1761
|
+
* Shared between newsletter and welcome email templates.
|
|
1762
|
+
*
|
|
1763
|
+
* Note: Each template may append its own additional paragraph after this block
|
|
1764
|
+
* (e.g., newsletter adds a line about source requests via GitHub Issues).
|
|
1765
|
+
*/
|
|
1766
|
+
const platformIntroHtml = () => `
|
|
1767
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">heripo는 고고학 연구 환경의 실질적인 디지털 전환을 지향하는 연구 플랫폼입니다.</p>
|
|
1768
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">발굴조사보고서(PDF) 속에 갇힌 텍스트와 도면을 분석 가능한 구조화된 데이터로 전환하여, 연구자가 자료를 보다 체계적으로 탐색하고 재사용할 수 있는 인프라를 구축하고 있습니다.</p>
|
|
1769
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7;
|
|
1770
|
+
color: #444444; margin: 0 0 18px 0;">현재는 소프트웨어 엔지니어와 고고학 연구자가 함께하는 <strong><a href="https://github.com/heripo-lab" target="_blank">heripo lab</a></strong>으로 운영 중이며, 2026년 1월 28일 핵심 엔진을 <strong><a href="https://github.com/heripo-lab/heripo-engine" target="_blank">오픈소스로 공개</a></strong>했습니다.</p>
|
|
1771
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">오픈소스로 공개된 핵심 기능은 <strong><a href="https://engine-demo.heripo.com" target="_blank">데모 사이트</a></strong>에서 직접 체험해 보실 수 있으며, 플랫폼 프로토타입 출시 시 구독자분들께 우선 안내해 드리겠습니다.</p>`;
|
|
1772
|
+
/**
|
|
1773
|
+
* "Powered by LLM Newsletter Kit · View Source" footer line.
|
|
1774
|
+
*/
|
|
1775
|
+
const poweredByFooterHtml = () => `
|
|
1776
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.5; color: #999999; margin: 0 0 12px 0;" class="footer-text">
|
|
1777
|
+
Powered by <a href="https://github.com/heripo-lab/llm-newsletter-kit-core" target="_blank" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; transition: color 0.2s; color: #999999; text-decoration: underline;" class="footer-link">LLM Newsletter Kit</a> ·
|
|
1778
|
+
<a href="https://github.com/heripo-lab/heripo-research-radar" target="_blank" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; transition: color 0.2s; color: #999999; text-decoration: underline;" class="footer-link">View Source</a>
|
|
1779
|
+
</p>`;
|
|
1780
|
+
|
|
1709
1781
|
/**
|
|
1710
1782
|
* Creates an HTML template for the newsletter email
|
|
1711
1783
|
*
|
|
@@ -1718,16 +1790,18 @@ class AnalysisProvider {
|
|
|
1718
1790
|
* - Platform introduction
|
|
1719
1791
|
*
|
|
1720
1792
|
* @param targets - Array of crawling targets to be listed in the newsletter footer
|
|
1793
|
+
* @param options - Optional template customization options
|
|
1721
1794
|
* @returns Complete HTML string for the newsletter email
|
|
1722
1795
|
*
|
|
1723
1796
|
* @example
|
|
1724
1797
|
* ```typescript
|
|
1725
|
-
* const html = createNewsletterHtmlTemplate(
|
|
1726
|
-
* { id: '1', name: 'Source 1', url: 'https://example.com', ... }
|
|
1727
|
-
*
|
|
1798
|
+
* const html = createNewsletterHtmlTemplate(
|
|
1799
|
+
* [{ id: '1', name: 'Source 1', url: 'https://example.com', ... }],
|
|
1800
|
+
* { isKrasNewsletter: true, krasNewsMarkdown: '## News...' },
|
|
1801
|
+
* );
|
|
1728
1802
|
* ```
|
|
1729
1803
|
*/
|
|
1730
|
-
const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
1804
|
+
const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
1731
1805
|
<html lang="ko" style="color-scheme: light dark; supported-color-schemes: light dark;">
|
|
1732
1806
|
<head>
|
|
1733
1807
|
<meta charset="UTF-8">
|
|
@@ -2105,6 +2179,18 @@ const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
|
2105
2179
|
background-color: #23201c !important;
|
|
2106
2180
|
}
|
|
2107
2181
|
|
|
2182
|
+
|
|
2183
|
+
.header-dark-text {
|
|
2184
|
+
color: #eeeeee !important;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
.header-title-border {
|
|
2188
|
+
border-bottom-color: #E59866 !important;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
.dark-logo-inline {
|
|
2192
|
+
display: inline-block !important;
|
|
2193
|
+
}
|
|
2108
2194
|
}
|
|
2109
2195
|
</style>
|
|
2110
2196
|
</head>
|
|
@@ -2120,16 +2206,50 @@ const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
|
2120
2206
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 800px;" class="container" role="presentation">
|
|
2121
2207
|
<tr>
|
|
2122
2208
|
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 44px 44px 36px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2209
|
+
${options?.isKrasNewsletter
|
|
2210
|
+
? `${krasHeaderHtml()}
|
|
2211
|
+
<!-- 헤더: 제목/날짜 행 -->
|
|
2212
|
+
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" class="header-title-border" style="width: 100%; border-collapse: collapse; margin: 0 0 18px 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; margin-bottom: 32px; border: none; border-bottom: 3px solid #D2691E;">
|
|
2213
|
+
<tr>
|
|
2214
|
+
<td align="left" valign="baseline" class="header-dark-text" style="text-align: left; padding: 0 0 14px 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 26px; font-weight: bold; color: #111111; line-height: 1.2; border: none;">
|
|
2215
|
+
한국고고학회 뉴스레터
|
|
2216
|
+
</td>
|
|
2217
|
+
<td align="left" valign="baseline" class="header-dark-text" style="text-align: left; padding: 0 0 14px 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #333333; white-space: nowrap; border: none; width: 1px;" width="1">
|
|
2218
|
+
${options?.displayDate ?? ''}
|
|
2219
|
+
</td>
|
|
2220
|
+
</tr>
|
|
2221
|
+
</table>
|
|
2222
|
+
`
|
|
2223
|
+
: `${heripoLogoHtml('12px')}
|
|
2224
|
+
`}
|
|
2225
|
+
|
|
2226
|
+
${options?.krasNewsMarkdown
|
|
2227
|
+
? safeMarkdown2Html(`## 학회 소식
|
|
2228
|
+
${options.krasNewsMarkdown}
|
|
2229
|
+
|
|
2230
|
+
---
|
|
2231
|
+
`, {
|
|
2232
|
+
window: new JSDOM('').window,
|
|
2233
|
+
linkTargetBlank: true,
|
|
2234
|
+
fixMalformedUrls: true,
|
|
2235
|
+
fixBoldSyntax: true,
|
|
2236
|
+
convertStrikethrough: true,
|
|
2237
|
+
})
|
|
2238
|
+
: ''}
|
|
2239
|
+
|
|
2240
|
+
${options?.heripolabNewsMarkdown
|
|
2241
|
+
? safeMarkdown2Html(`## heripo lab 소식
|
|
2242
|
+
${options.heripolabNewsMarkdown}
|
|
2243
|
+
|
|
2244
|
+
---
|
|
2245
|
+
`, {
|
|
2246
|
+
window: new JSDOM('').window,
|
|
2247
|
+
linkTargetBlank: true,
|
|
2248
|
+
fixMalformedUrls: true,
|
|
2249
|
+
fixBoldSyntax: true,
|
|
2250
|
+
convertStrikethrough: true,
|
|
2251
|
+
})
|
|
2252
|
+
: ''}
|
|
2133
2253
|
|
|
2134
2254
|
{{NEWSLETTER_CONTENT}}
|
|
2135
2255
|
|
|
@@ -2145,7 +2265,7 @@ const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
|
2145
2265
|
</ul>
|
|
2146
2266
|
<hr style="border: 0; border-top: 2px solid #D2691E; margin: 32px 0;">
|
|
2147
2267
|
<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 16px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: none;">📅 발행 정책</h2>
|
|
2148
|
-
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;"><strong
|
|
2268
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;"><strong>${options?.isKrasNewsletter ? '한국고고학회' : 'heripo 리서치 레이더'}</strong>는 매일 발행을 원칙으로 하되, 독자분들께 의미 있는 정보를 제공하기 위해 다음과 같은 발행 기준을 적용합니다:</p>
|
|
2149
2269
|
<ul style="padding-left: 24px; margin: 0 0 18px 0;">
|
|
2150
2270
|
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0; margin-bottom: 8px;"><strong>정상 발행</strong>: 새로운 소식이 ${newsletterConfig.publicationCriteria.minimumArticleCountForIssue + 1}개 이상이거나, ${newsletterConfig.publicationCriteria.minimumArticleCountForIssue}개 이하여도 중요도 ${newsletterConfig.publicationCriteria.priorityArticleScoreThreshold}점 이상의 핵심 소식이 포함된 경우</li>
|
|
2151
2271
|
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0; margin-bottom: 8px;"><strong>이월 발행</strong>: 새로운 소식이 ${newsletterConfig.publicationCriteria.minimumArticleCountForIssue}개 이하이면서 중요한 내용(${newsletterConfig.publicationCriteria.priorityArticleScoreThreshold}점 이상)이 없을 경우, 다음 호로 이월하여 더 풍성한 내용으로 제공</li>
|
|
@@ -2154,11 +2274,7 @@ const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
|
2154
2274
|
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">이러한 정책을 통해 매일 의미 없는 소식으로 독자분들의 시간을 낭비하지 않고, 정말 중요한 정보를 적절한 타이밍에 제공하고자 합니다.</p>
|
|
2155
2275
|
<hr style="border: 0; border-top: 2px solid #D2691E; margin: 32px 0;">
|
|
2156
2276
|
<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 16px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: none;">🔍 heripo(헤리포) 플랫폼 소개</h2>
|
|
2157
|
-
|
|
2158
|
-
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">발굴조사보고서(PDF) 속에 갇힌 텍스트와 도면을 분석 가능한 구조화된 데이터로 전환하여, 연구자가 자료를 보다 체계적으로 탐색하고 재사용할 수 있는 인프라를 구축하고 있습니다.</p>
|
|
2159
|
-
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7;
|
|
2160
|
-
color: #444444; margin: 0 0 18px 0;">현재는 소프트웨어 엔지니어와 고고학 연구자가 함께하는 <strong><a href="https://github.com/heripo-lab" target="_blank">heripo lab</a></strong>으로 운영 중이며, 2026년 1월 28일 핵심 엔진을 <strong><a href="https://github.com/heripo-lab/heripo-engine" target="_blank">오픈소스로 공개</a></strong>했습니다.</p>
|
|
2161
|
-
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">오픈소스로 공개된 핵심 기능은 <strong><a href="https://engine-demo.heripo.com" target="_blank">데모 사이트</a></strong>에서 직접 체험해 보실 수 있으며, 플랫폼 프로토타입 출시 시 구독자분들께 우선 안내해 드리겠습니다.</p>
|
|
2277
|
+
${platformIntroHtml()}
|
|
2162
2278
|
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7;
|
|
2163
2279
|
color: #444444; margin: 0 0 18px 0;">보고 계신 뉴스레터(리서치 레이더)는 heripo의 초기 선행 기능 중 하나입니다. 뉴스레터 소스 추가 요청은 <a href="https://github.com/heripo-lab/heripo-research-radar/issues" target="_blank">GitHub 이슈</a>를 통해 언제든 환영합니다.</p>
|
|
2164
2280
|
<hr style="border: 0; border-top: 2px solid #D2691E; margin: 32px 0;">
|
|
@@ -2171,12 +2287,9 @@ const createNewsletterHtmlTemplate = (targets) => `<!DOCTYPE html>
|
|
|
2171
2287
|
<tr>
|
|
2172
2288
|
<td align="center" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 30px 20px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 1.5; color: #888888;">
|
|
2173
2289
|
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 10px 0;" class="footer-text">heripo lab | newsletter@heripo.com</p>
|
|
2174
|
-
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 10px 0;" class="footer-text"
|
|
2290
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 10px 0;" class="footer-text">${options?.isKrasNewsletter ? '이 메일은 heripo.com에서 뉴스레터를 구독하신 분들과 한국고고학회 회원에게 발송됩니다.' : '이 메일은 heripo.com에서 리서치 레이더를 구독하신 분들에게 발송됩니다.'}<br>
|
|
2175
2291
|
더 이상 이메일을 받고 싶지 않으시면 <a href="{{{RESEND_UNSUBSCRIBE_URL}}}" target="_blank" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-weight: bold; transition: color 0.2s; color: #888888; text-decoration: underline;" class="footer-link">여기에서 수신 거부</a>하세요.</p>
|
|
2176
|
-
|
|
2177
|
-
Powered by <a href="https://github.com/heripo-lab/llm-newsletter-kit-core" target="_blank" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; transition: color 0.2s; color: #999999; text-decoration: underline;" class="footer-link">LLM Newsletter Kit</a> ·
|
|
2178
|
-
<a href="https://github.com/heripo-lab/heripo-research-radar" target="_blank" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; transition: color 0.2s; color: #999999; text-decoration: underline;" class="footer-link">View Source</a>
|
|
2179
|
-
</p>
|
|
2292
|
+
${poweredByFooterHtml()}
|
|
2180
2293
|
</td>
|
|
2181
2294
|
</tr>
|
|
2182
2295
|
</table>
|
|
@@ -2203,16 +2316,26 @@ class ContentGenerateProvider {
|
|
|
2203
2316
|
newsletterRepository;
|
|
2204
2317
|
_issueOrder = null;
|
|
2205
2318
|
model;
|
|
2206
|
-
|
|
2319
|
+
/** HTML template with markers for title and content injection */
|
|
2320
|
+
htmlTemplate;
|
|
2321
|
+
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
2322
|
+
newsletterBrandName;
|
|
2323
|
+
constructor(google, articleRepository, newsletterRepository, templateOptions, brandName) {
|
|
2207
2324
|
this.google = google;
|
|
2208
2325
|
this.articleRepository = articleRepository;
|
|
2209
2326
|
this.newsletterRepository = newsletterRepository;
|
|
2210
2327
|
this.model = this.google('gemini-3-pro-preview');
|
|
2328
|
+
this.newsletterBrandName = brandName ?? newsletterConfig.brandName;
|
|
2329
|
+
this.htmlTemplate = {
|
|
2330
|
+
html: createNewsletterHtmlTemplate(crawlingTargetGroups.flatMap((group) => group.targets), templateOptions),
|
|
2331
|
+
markers: {
|
|
2332
|
+
title: 'NEWSLETTER_TITLE',
|
|
2333
|
+
content: 'NEWSLETTER_CONTENT',
|
|
2334
|
+
},
|
|
2335
|
+
};
|
|
2211
2336
|
}
|
|
2212
2337
|
/** LLM temperature setting for content generation */
|
|
2213
2338
|
temperature = llmConfig.generation.temperature;
|
|
2214
|
-
/** Newsletter brand name */
|
|
2215
|
-
newsletterBrandName = newsletterConfig.brandName;
|
|
2216
2339
|
/** Subscribe page URL */
|
|
2217
2340
|
subscribePageUrl = newsletterConfig.subscribePageUrl;
|
|
2218
2341
|
/** Publication criteria (minimum article count, priority score threshold) */
|
|
@@ -2240,14 +2363,6 @@ class ContentGenerateProvider {
|
|
|
2240
2363
|
async fetchArticleCandidates() {
|
|
2241
2364
|
return this.articleRepository.findCandidatesForNewsletter();
|
|
2242
2365
|
}
|
|
2243
|
-
/** HTML template with markers for title and content injection */
|
|
2244
|
-
htmlTemplate = {
|
|
2245
|
-
html: createNewsletterHtmlTemplate(crawlingTargetGroups.flatMap((group) => group.targets)),
|
|
2246
|
-
markers: {
|
|
2247
|
-
title: 'NEWSLETTER_TITLE',
|
|
2248
|
-
content: 'NEWSLETTER_CONTENT',
|
|
2249
|
-
},
|
|
2250
|
-
};
|
|
2251
2366
|
/**
|
|
2252
2367
|
* Save generated newsletter to the repository
|
|
2253
2368
|
* @param input - Newsletter data and used articles
|
|
@@ -2296,8 +2411,35 @@ class CrawlingProvider {
|
|
|
2296
2411
|
* Date service implementation
|
|
2297
2412
|
* - Provides current date and display date strings
|
|
2298
2413
|
* - Always returns Korea Standard Time (KST, Asia/Seoul) regardless of server timezone
|
|
2414
|
+
* - Accepts optional publishDate to override the current date (e.g., for next-day publishing)
|
|
2299
2415
|
*/
|
|
2300
2416
|
class DateService {
|
|
2417
|
+
targetDate;
|
|
2418
|
+
/**
|
|
2419
|
+
* @param publishDate - Optional ISO date string (YYYY-MM-DD) to use instead of current date.
|
|
2420
|
+
* When provided, the newsletter will use this date as its publication date.
|
|
2421
|
+
* @throws {Error} If publishDate is not in YYYY-MM-DD format or is not a real calendar date.
|
|
2422
|
+
*/
|
|
2423
|
+
constructor(publishDate) {
|
|
2424
|
+
if (publishDate !== undefined) {
|
|
2425
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(publishDate)) {
|
|
2426
|
+
throw new Error(`Invalid publishDate format: "${publishDate}". Expected YYYY-MM-DD (e.g., "2025-02-12").`);
|
|
2427
|
+
}
|
|
2428
|
+
const date = new Date(publishDate + 'T00:00:00+09:00');
|
|
2429
|
+
// Round-trip check: format back to YYYY-MM-DD in KST and compare.
|
|
2430
|
+
// Catches invalid dates like "2025-02-30" that JS silently normalizes to "2025-03-02".
|
|
2431
|
+
const roundTrip = date.toLocaleDateString('en-CA', {
|
|
2432
|
+
timeZone: 'Asia/Seoul',
|
|
2433
|
+
});
|
|
2434
|
+
if (roundTrip !== publishDate) {
|
|
2435
|
+
throw new Error(`Invalid publishDate: "${publishDate}" is not a real calendar date.`);
|
|
2436
|
+
}
|
|
2437
|
+
this.targetDate = date;
|
|
2438
|
+
}
|
|
2439
|
+
else {
|
|
2440
|
+
this.targetDate = new Date();
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2301
2443
|
/**
|
|
2302
2444
|
* Get current date in ISO format (YYYY-MM-DD)
|
|
2303
2445
|
* - Always returns date in Korea Standard Time (UTC+9)
|
|
@@ -2306,7 +2448,7 @@ class DateService {
|
|
|
2306
2448
|
getCurrentISODateString() {
|
|
2307
2449
|
// Use Intl.DateTimeFormat to get date in Korea timezone
|
|
2308
2450
|
// 'en-CA' locale returns YYYY-MM-DD format by default
|
|
2309
|
-
const kstDate =
|
|
2451
|
+
const kstDate = this.targetDate.toLocaleDateString('en-CA', {
|
|
2310
2452
|
timeZone: 'Asia/Seoul',
|
|
2311
2453
|
});
|
|
2312
2454
|
return kstDate;
|
|
@@ -2323,7 +2465,7 @@ class DateService {
|
|
|
2323
2465
|
month: 'long',
|
|
2324
2466
|
day: 'numeric',
|
|
2325
2467
|
});
|
|
2326
|
-
return formatter.format(
|
|
2468
|
+
return formatter.format(this.targetDate);
|
|
2327
2469
|
}
|
|
2328
2470
|
}
|
|
2329
2471
|
|
|
@@ -2397,13 +2539,31 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2397
2539
|
const google = createGoogleGenerativeAI({
|
|
2398
2540
|
apiKey: dependencies.googleGenerativeAIApiKey,
|
|
2399
2541
|
});
|
|
2400
|
-
const dateService = new DateService();
|
|
2542
|
+
const dateService = new DateService(dependencies.publishDate);
|
|
2401
2543
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
2402
2544
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository);
|
|
2403
2545
|
const analysisProvider = new AnalysisProvider(openai, dependencies.articleRepository, dependencies.tagRepository);
|
|
2404
|
-
|
|
2546
|
+
// Inject display date from DateService into template options
|
|
2547
|
+
const templateOptions = dependencies.templateOptions
|
|
2548
|
+
? {
|
|
2549
|
+
...dependencies.templateOptions,
|
|
2550
|
+
displayDate: dateService.getDisplayDateString(),
|
|
2551
|
+
}
|
|
2552
|
+
: undefined;
|
|
2553
|
+
let resolvedContentOptions = { ...contentOptions };
|
|
2554
|
+
let resolvedBrandName = newsletterConfig.brandName;
|
|
2555
|
+
if (templateOptions?.isKrasNewsletter) {
|
|
2556
|
+
resolvedContentOptions = {
|
|
2557
|
+
...resolvedContentOptions,
|
|
2558
|
+
expertField: ['고고학 우선적 문화유산'],
|
|
2559
|
+
freeFormIntro: true,
|
|
2560
|
+
titleContext: templateOptions.titleContext || undefined,
|
|
2561
|
+
};
|
|
2562
|
+
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
2563
|
+
}
|
|
2564
|
+
const contentGenerateProvider = new ContentGenerateProvider(google, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
2405
2565
|
return new GenerateNewsletter({
|
|
2406
|
-
contentOptions,
|
|
2566
|
+
contentOptions: resolvedContentOptions,
|
|
2407
2567
|
dateService,
|
|
2408
2568
|
taskService,
|
|
2409
2569
|
crawlingProvider,
|
|
@@ -2446,5 +2606,258 @@ async function generateNewsletter(dependencies) {
|
|
|
2446
2606
|
return generator.generate();
|
|
2447
2607
|
}
|
|
2448
2608
|
|
|
2449
|
-
|
|
2609
|
+
/**
|
|
2610
|
+
* Generates a welcome email HTML string with CSS inlined via juice.
|
|
2611
|
+
*
|
|
2612
|
+
* API is designed to match the original heripo-web `generateWelcomeHTML(id, name)` usage,
|
|
2613
|
+
* with an optional third parameter for KRAS mode and site URL override.
|
|
2614
|
+
*
|
|
2615
|
+
* @param id - Subscriber ID (used for unsubscribe links in default mode)
|
|
2616
|
+
* @param name - Subscriber display name
|
|
2617
|
+
* @param options - Optional configuration for KRAS mode and site URL
|
|
2618
|
+
* @returns Complete HTML string with CSS inlined (ready to send as email)
|
|
2619
|
+
*
|
|
2620
|
+
* @example
|
|
2621
|
+
* ```typescript
|
|
2622
|
+
* // Default heripo branding (same as original heripo-web usage):
|
|
2623
|
+
* const html = generateWelcomeHTML('subscriber-123', '홍길동');
|
|
2624
|
+
*
|
|
2625
|
+
* // KRAS mode:
|
|
2626
|
+
* const krasHtml = generateWelcomeHTML('subscriber-123', '홍길동', {
|
|
2627
|
+
* isKrasNewsletter: true,
|
|
2628
|
+
* });
|
|
2629
|
+
* ```
|
|
2630
|
+
*/
|
|
2631
|
+
function generateWelcomeHTML(id, name, options) {
|
|
2632
|
+
const isKras = options?.isKrasNewsletter ?? false;
|
|
2633
|
+
const siteUrl = options?.siteUrl ?? 'https://heripo.com';
|
|
2634
|
+
const safeName = sanitizeText(name);
|
|
2635
|
+
const unsubscribeUrl = isKras
|
|
2636
|
+
? '{{{RESEND_UNSUBSCRIBE_URL}}}'
|
|
2637
|
+
: `${siteUrl}/research-radar/unsubscribe?id=${id}`;
|
|
2638
|
+
return juice(createWelcomeHtmlRaw(safeName, isKras, siteUrl, unsubscribeUrl));
|
|
2639
|
+
}
|
|
2640
|
+
function createWelcomeHtmlRaw(name, isKras, siteUrl, unsubscribeUrl) {
|
|
2641
|
+
const title = isKras
|
|
2642
|
+
? '한국고고학회 뉴스레터 구독 완료'
|
|
2643
|
+
: 'heripo 리서치 레이더 구독 완료';
|
|
2644
|
+
const headerHtml = isKras
|
|
2645
|
+
? `${krasHeaderHtml()}
|
|
2646
|
+
<h1 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.2; margin: 0 0
|
|
2647
|
+
18px 0; letter-spacing: -0.5px; margin-top: 0; font-size: 32px; font-weight: bold; color: #111111; border-bottom: 3px solid #D2691E; padding-bottom: 8px;">${name}님, 한국고고학회 뉴스레터를 구독해주셔서 감사합니다.</h1>`
|
|
2648
|
+
: `${heripoLogoHtml('8px')}
|
|
2649
|
+
|
|
2650
|
+
<h1 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.2; margin: 0 0
|
|
2651
|
+
18px 0; letter-spacing: -0.5px; margin-top: 0; font-size: 32px; font-weight: bold; color: #111111; border-bottom: 3px solid #D2691E; padding-bottom: 8px;">${name}님, heripo 리서치 레이더에 오신 것을 환영합니다!</h1>`;
|
|
2652
|
+
const feedbackHeading = `${name}님의 목소리가 heripo의 미래를 만듭니다`;
|
|
2653
|
+
const feedbackText = 'heripo';
|
|
2654
|
+
const newsletterLine = isKras
|
|
2655
|
+
? `<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">뉴스레터(리서치 레이더)는 heripo의 초기 선행 기능 중 하나입니다. 뉴스레터 소스 추가 요청은 <a href="https://github.com/heripo-lab/heripo-research-radar/issues" target="_blank">GitHub 이슈</a>를 통해 언제든 환영합니다.</p>`
|
|
2656
|
+
: `<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">뉴스레터(리서치 레이더)는 heripo의 초기 선행 기능 중 하나입니다. 뉴스레터 소스 추가 요청은 <a href="https://github.com/heripo-lab/heripo-research-radar/issues" target="_blank">GitHub 이슈</a>를 통해 언제든 환영합니다.</p>`;
|
|
2657
|
+
const warningHtml = isKras
|
|
2658
|
+
? `
|
|
2659
|
+
<blockquote style="background-color: #fef2f2; border-left: 5px solid #dc2626; margin: 24px 0; padding: 20px; border-radius: 4px;">
|
|
2660
|
+
<h3 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: bold; line-height: 1.3; color: #dc2626; margin: 0 0 10px 0; letter-spacing: -0.1px;">⚠️ 본인이 신청하지 않으셨다면</h3>
|
|
2661
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0; margin-bottom: 10px;">만약 본인이 직접 구독 신청을 하지 않으셨다면, 다른 분이 실수로 이메일 주소를 입력했을 가능성이 있습니다. 이 경우 아래 링크를 통해 즉시 수신을 거부하실 수 있습니다.</p>
|
|
2662
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0;"><a href="${unsubscribeUrl}" style="color: #dc2626; font-weight: bold;">🚫 수신 거부하기</a></p>
|
|
2663
|
+
</blockquote>`
|
|
2664
|
+
: `
|
|
2665
|
+
<blockquote style="background-color: #fef2f2; border-left: 5px solid #dc2626; margin: 24px 0; padding: 20px; border-radius: 4px;">
|
|
2666
|
+
<h3 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: bold; line-height: 1.3; color: #dc2626; margin: 0 0 10px 0; letter-spacing: -0.1px;">⚠️ 본인이 신청하지 않으셨다면</h3>
|
|
2667
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0; margin-bottom: 10px;">만약 본인이 직접 구독 신청을 하지 않으셨다면, 다른 분이 실수로 이메일 주소를 입력했을 가능성이 있습니다. 이 경우 아래 링크를 통해 즉시 수신을 거부하실 수 있습니다.</p>
|
|
2668
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0;"><a href="${unsubscribeUrl}" style="color: #dc2626; font-weight: bold;">🚫 수신 거부하기</a></p>
|
|
2669
|
+
</blockquote>`;
|
|
2670
|
+
const footerDisclaimerText = isKras
|
|
2671
|
+
? '이 이메일은 heripo.com에서 한국고고학회 뉴스레터를 구독하신 분들에게 발송됩니다.'
|
|
2672
|
+
: '이 이메일은 heripo.com에서 리서치 레이더를 구독하신 분들에게 발송됩니다.';
|
|
2673
|
+
const footerUnsubscribeHtml = isKras
|
|
2674
|
+
? `<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.7; color: #6b7280; margin: 0 0 18px 0; margin-bottom: 8px;">📱 구독 관리: <a href="${unsubscribeUrl}" class="footer-link">구독 해지</a></p>`
|
|
2675
|
+
: `<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.7; color: #6b7280; margin: 0 0 18px 0; margin-bottom: 8px;">📱 구독 관리: <a href="${unsubscribeUrl}" class="footer-link">구독 해지</a></p>`;
|
|
2676
|
+
return `<!DOCTYPE html>
|
|
2677
|
+
<html lang="ko" style="color-scheme: light dark; supported-color-schemes: light dark;">
|
|
2678
|
+
<head>
|
|
2679
|
+
<meta charset="UTF-8">
|
|
2680
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2681
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
2682
|
+
<meta name="color-scheme" content="light dark">
|
|
2683
|
+
<meta name="supported-color-schemes" content="light dark">
|
|
2684
|
+
<title>${title}</title>
|
|
2685
|
+
<style type="text/css">
|
|
2686
|
+
a:hover {
|
|
2687
|
+
color: #D2691E;
|
|
2688
|
+
}
|
|
2689
|
+
.button-link {
|
|
2690
|
+
color: #fff !important;
|
|
2691
|
+
text-decoration: none !important;
|
|
2692
|
+
font-weight: bold;
|
|
2693
|
+
font-size: 16px;
|
|
2694
|
+
display: inline-block;
|
|
2695
|
+
padding: 10px 28px;
|
|
2696
|
+
border-radius: 4px;
|
|
2697
|
+
background: #D2691E;
|
|
2698
|
+
border: none;
|
|
2699
|
+
}
|
|
2700
|
+
.button-link:hover {
|
|
2701
|
+
background: #b85a1a;
|
|
2702
|
+
}
|
|
2703
|
+
@media screen and (max-width: 800px) {
|
|
2704
|
+
.container {
|
|
2705
|
+
width: 100% !important;
|
|
2706
|
+
max-width: 100% !important;
|
|
2707
|
+
padding: 0 !important;
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
.content-cell {
|
|
2711
|
+
padding: 20px !important;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
@media screen and (max-width: 600px) {
|
|
2715
|
+
h1 {
|
|
2716
|
+
font-size: 24px !important;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
h2 {
|
|
2720
|
+
font-size: 20px !important;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
@media (prefers-color-scheme: dark) {
|
|
2724
|
+
body,
|
|
2725
|
+
.dark-mode-bg {
|
|
2726
|
+
background-color: #121212 !important;
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
.dark-mode-content-bg {
|
|
2730
|
+
background-color: #1e1e1e !important;
|
|
2731
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.25) !important;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
h1,
|
|
2735
|
+
h2,
|
|
2736
|
+
h3,
|
|
2737
|
+
h4,
|
|
2738
|
+
h5,
|
|
2739
|
+
h6 {
|
|
2740
|
+
color: #FFFFFF !important;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
h2,
|
|
2744
|
+
h3,
|
|
2745
|
+
h4 {
|
|
2746
|
+
background: #1e1e1e !important;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
p,
|
|
2750
|
+
li {
|
|
2751
|
+
color: #FFFFFF !important;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
a:not(.button-link) {
|
|
2755
|
+
color: #4da6ff !important;
|
|
2756
|
+
text-decoration: underline !important;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
a.button-link {
|
|
2760
|
+
color: #fff !important;
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
blockquote {
|
|
2764
|
+
background-color: #2b2b2b !important;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
blockquote p {
|
|
2768
|
+
color: #bbbbbb !important;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
.footer-text {
|
|
2772
|
+
color: #999999 !important;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
.footer-link {
|
|
2776
|
+
color: #999999 !important;
|
|
2777
|
+
text-decoration: underline !important;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
.dark-logo {
|
|
2781
|
+
display: block !important;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
.light-logo {
|
|
2785
|
+
display: none !important;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
.welcome-notice {
|
|
2789
|
+
background-color: #2b2b2b !important;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
.header-dark-text {
|
|
2793
|
+
color: #eeeeee !important;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
.dark-logo-inline {
|
|
2797
|
+
display: inline-block !important;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
</style>
|
|
2801
|
+
</head>
|
|
2802
|
+
<body style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #f4f4f4; font-size: 16px; line-height: 1.7; letter-spacing: 0.01em; height: 100%; width: 100%; margin: 0; padding: 0;">
|
|
2803
|
+
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt;">
|
|
2804
|
+
<tr>
|
|
2805
|
+
<td bgcolor="#f4f4f4" align="center" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 20px 0;" class="dark-mode-bg">
|
|
2806
|
+
<!--[if (gte mso 9)|(IE)]>
|
|
2807
|
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="800">
|
|
2808
|
+
<tr>
|
|
2809
|
+
<td align="center" valign="top" width="800">
|
|
2810
|
+
<![endif]-->
|
|
2811
|
+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 800px;" class="container" role="presentation">
|
|
2812
|
+
<tr>
|
|
2813
|
+
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 48px 44px 44px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2814
|
+
${headerHtml}
|
|
2815
|
+
|
|
2816
|
+
<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 15px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: #fff7f2;">💬 ${feedbackHeading}</h2>
|
|
2817
|
+
|
|
2818
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">가장 큰 응원은 ${feedbackText}를 직접 사용해보시고, 솔직한 피드백을 주시는 것입니다.</p>
|
|
2819
|
+
|
|
2820
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;"><strong style="color: #D2691E; font-weight: bold;">"이런 기능이 있다면 좋겠다"</strong> 혹은 <strong style="color: #D2691E; font-weight: bold;">"이런 점은 불편하다"</strong>와 같은 의견을 언제든 보내주세요.</p>
|
|
2821
|
+
|
|
2822
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;">여러분의 피드백 하나하나가 ${feedbackText}의 다음 발걸음을 결정합니다.</p>
|
|
2823
|
+
${isKras
|
|
2824
|
+
? `
|
|
2825
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.7; color: #444444; margin: 0 0 18px 0;"><strong><a href="https://github.com/heripo-lab" target="_blank">heripo lab</a></strong>은 한국고고학회와 함께 뉴스레터 발행 및 고고학의 디지털 전환을 추진하고 있습니다. 앞으로도 연구 현장에 실질적으로 도움이 되는 정보와 기술을 제공해 드리겠습니다.</p>
|
|
2826
|
+
`
|
|
2827
|
+
: ''}
|
|
2828
|
+
<hr style="border: 0; border-top: 1px solid #e5e7eb; margin: 28px 0 20px;">
|
|
2829
|
+
|
|
2830
|
+
<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 15px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: #fff7f2;">🔍 heripo(헤리포) 플랫폼 소개</h2>
|
|
2831
|
+
${platformIntroHtml()}
|
|
2832
|
+
${newsletterLine}
|
|
2833
|
+
${warningHtml}
|
|
2834
|
+
|
|
2835
|
+
<hr style="border: 0; border-top: 1px solid #e5e7eb; margin: 32px 0;">
|
|
2836
|
+
|
|
2837
|
+
<div style="color: #6b7280; font-size: 14px;" class="footer-text">
|
|
2838
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.7; color: #6b7280; margin: 0 0 18px 0; margin-bottom: 8px;">📧 피드백 및 문의: <a href="${siteUrl}/contact" class="footer-link">문의하기</a></p>
|
|
2839
|
+
${footerUnsubscribeHtml}
|
|
2840
|
+
|
|
2841
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.7; color: #6b7280; margin: 0 0 18px 0; margin-bottom: 15px;"><em>정보 검색에 쏟던 시간을 연구와 창의적 기획에 집중하는 시간으로 바꿔보세요.</em></p>
|
|
2842
|
+
|
|
2843
|
+
${poweredByFooterHtml()}
|
|
2844
|
+
|
|
2845
|
+
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 1.7; color: #9ca3af; margin: 0;">${footerDisclaimerText}<br>더 이상 이메일을 받고 싶지 않으시면 <a href="${unsubscribeUrl}" target="_blank" style="color: #888888; text-decoration: underline;" class="footer-link">여기에서 수신 거부</a>하세요.</p>
|
|
2846
|
+
</div>
|
|
2847
|
+
</td>
|
|
2848
|
+
</tr>
|
|
2849
|
+
</table>
|
|
2850
|
+
<!--[if (gte mso 9)|(IE)]>
|
|
2851
|
+
</td>
|
|
2852
|
+
</tr>
|
|
2853
|
+
</table>
|
|
2854
|
+
<![endif]-->
|
|
2855
|
+
</td>
|
|
2856
|
+
</tr>
|
|
2857
|
+
</table>
|
|
2858
|
+
</body>
|
|
2859
|
+
</html>`;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
export { AnalysisProvider, ContentGenerateProvider, CrawlingProvider, DateService, TaskService, contentOptions, crawlingTargetGroups, generateNewsletter, generateWelcomeHTML, llmConfig, newsletterConfig };
|
|
2450
2863
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|