thread-master/frontend/src/lib/productContext.ts

87 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export type CtaType = 'none' | 'link' | 'dm' | 'follow'
export type ProductContextFields = {
brand: string
product: string
features: string
placementTone?: string
ctaType?: CtaType
ctaUrl?: string
}
export const CTA_TYPE_OPTIONS: Array<{ value: CtaType; label: string }> = [
{ value: 'none', label: '(無)不主動帶 CTA' },
{ value: 'link', label: '連結(可填網址)' },
{ value: 'dm', label: '私訊' },
{ value: 'follow', label: '追蹤' },
]
export const EMPTY_PRODUCT_CONTEXT: ProductContextFields = {
brand: '',
product: '',
features: '',
placementTone: '',
ctaType: 'none',
ctaUrl: '',
}
function normalizeCtaType(value: unknown): CtaType {
return value === 'link' || value === 'dm' || value === 'follow' ? value : 'none'
}
export function parseProductContext(raw: string | null | undefined): ProductContextFields {
if (!raw?.trim()) return { ...EMPTY_PRODUCT_CONTEXT }
try {
const parsed = JSON.parse(raw) as Partial<ProductContextFields>
if (parsed && typeof parsed === 'object') {
return {
brand: parsed.brand?.trim() ?? '',
product: parsed.product?.trim() ?? '',
features: parsed.features?.trim() ?? '',
placementTone: parsed.placementTone?.trim() ?? '',
ctaType: normalizeCtaType(parsed.ctaType),
ctaUrl: parsed.ctaUrl?.trim() ?? '',
}
}
} catch {
return { ...EMPTY_PRODUCT_CONTEXT, features: raw.trim() }
}
return { ...EMPTY_PRODUCT_CONTEXT }
}
export function serializeProductContext(fields: ProductContextFields): string {
const brand = fields.brand.trim()
const product = fields.product.trim()
const features = fields.features.trim()
const placementTone = fields.placementTone?.trim() ?? ''
const ctaType = normalizeCtaType(fields.ctaType)
const ctaUrl = fields.ctaUrl?.trim() ?? ''
if (!brand && !product && !features && !placementTone && ctaType === 'none' && !ctaUrl) {
return ''
}
return JSON.stringify({ brand, product, features, placementTone, ctaType, ctaUrl })
}
export function hasProductContext(raw: string | null | undefined): boolean {
const fields = parseProductContext(raw)
return !!(fields.brand || fields.product || fields.features)
}
export function summarizeProductContext(raw: string | null | undefined): string | null {
const fields = parseProductContext(raw)
const parts = [fields.brand, fields.product, fields.features].filter(Boolean)
return parts.length > 0 ? parts.join(' · ') : null
}
export function formatProductContextForPrompt(raw: string | null | undefined): string {
const fields = parseProductContext(raw)
const lines: string[] = []
if (fields.brand) lines.push(`品牌:${fields.brand}`)
if (fields.product) lines.push(`產品:${fields.product}`)
if (fields.features) lines.push(`特色/能幫上忙的地方:${fields.features}`)
if (fields.placementTone) lines.push(`置入語氣偏好:${fields.placementTone}`)
if (fields.ctaType === 'link' && fields.ctaUrl) lines.push(`留言 CTA 連結:${fields.ctaUrl}`)
else if (fields.ctaType === 'dm') lines.push('留言 CTA引導私訊')
else if (fields.ctaType === 'follow') lines.push('留言 CTA引導追蹤')
return lines.join('\n')
}