const { TagChip, Card, Button, Badge } = window.TaglyDesignSystem_1a793a; const FREE_LIMITS = { hashtags: 15, keywords: 10, longTail: 8, related: 6 }; const FAVORITES_KEY = 'tagly_favorites_v1'; function downloadBlob(filename, text, type) { const blob = new Blob([text], { type }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function loadFavorites() { try { return JSON.parse(localStorage.getItem(FAVORITES_KEY)) || []; } catch (e) { return []; } } function saveFavorites(list) { try { localStorage.setItem(FAVORITES_KEY, JSON.stringify(list.slice(0, 30))); } catch (e) {} } function CategoryTabs({ categories, active, onChange }) { return (
{categories.map(c => ( ))}
); } function AdSlot() { return (
Advertisement 336×280 · loads after results render
); } const PURPOSE_LABEL = { awareness: 'Awareness', engagement: 'Engagement', 'niche discovery': 'Niche discovery', community: 'Community', conversion: 'Conversion' }; const PURPOSE_DESC = { awareness: 'Maximize how many new people see your content.', engagement: 'Drive likes, comments, shares, and replies.', 'niche discovery': 'Reach a smaller, highly relevant audience.', community: 'Signal you into the platform’s own active communities.', conversion: 'Match intent-driven searches right before action.', }; const TIER_LABEL = { broad: 'Broad reach', medium: 'Medium reach', niche: 'Niche reach' }; const TIER_COLOR = { broad: 'var(--tg-primary)', medium: 'var(--tg-success, #1a8a4a)', niche: 'var(--text-muted)' }; function Gauge({ label, value, color }) { return (
{label}
); } function StrategyPanel({ strategy }) { if (!strategy) return null; return (
AI strategy Estimated guidance, not measured platform data
Suggested tag mix
{strategy.mix}
Posting guidance
{strategy.postingGuidance}
Content angle
{strategy.contentAngle}
Complementary keywords
{strategy.complementaryKeywords.map(k => {k})}
); } function ExpandedTopics({ topics, onPick }) { if (!topics?.length) return null; return (
Related angles — click to broaden your search
{topics.map((t, i) => ( ))}
); } function CopyFormatMenu({ items, label }) { const [open, setOpen] = React.useState(false); const [copied, setCopied] = React.useState(null); function doCopy(fmt) { let text; if (fmt === 'full') text = items.map(i => i.tag).join(' '); else if (fmt === 'comma') text = items.map(i => i.tag).join(', '); else if (fmt === 'nohash') text = items.map(i => i.tag.replace(/^#/, '')).join(' '); else if (fmt === 'markdown') text = items.map(i => `- ${i.tag}`).join('\n'); else if (fmt === 'category') { const groups = {}; items.forEach(i => { const k = i.purpose || 'other'; (groups[k] = groups[k] || []).push(i.tag); }); text = Object.entries(groups).map(([k, v]) => `${PURPOSE_LABEL[k] || k}:\n${v.join(' ')}`).join('\n\n'); } navigator.clipboard.writeText(text).catch(() => {}); setCopied(fmt); setOpen(false); setTimeout(() => setCopied(c => (c === fmt ? null : c)), 1400); } return (
{open && (
{[['full', 'Full set (with #)'], ['category', 'Grouped by category'], ['comma', 'Comma-separated'], ['nohash', 'Without # symbols'], ['markdown', 'Markdown list']].map(([fmt, txt]) => ( ))}
)}
); } function PublishPackageView({ pkg, platform, topic }) { const [copiedField, setCopiedField] = React.useState(null); if (!pkg) return null; function copy(field, text) { navigator.clipboard.writeText(text).catch(() => {}); setCopiedField(field); setTimeout(() => setCopiedField(f => (f === field ? null : f)), 1200); } function copyFullPackage() { const parts = [ `Title: ${pkg.title}`, `Caption: ${pkg.caption}`, `Hashtags: ${pkg.hashtags.map(h => h.tag).join(' ')}`, `Alt text: ${pkg.altText}`, `CTA: ${pkg.cta}`, pkg.firstComment ? pkg.firstComment : null, `Suggested posting time (estimated): ${pkg.postingTime.text}`, ].filter(Boolean); copy('full', parts.join('\n\n')); } const rows = [ ['Title', pkg.title], ['Caption', pkg.caption], ['Hashtags', pkg.hashtags.map(h => h.tag).join(' ')], ['Alt text', pkg.altText], ['Call to action', pkg.cta], ]; return (
A complete, ready-to-publish package for "{topic}" on {platform}.
{rows.map(([label, value]) => (
{label}
{value}
))} {pkg.firstComment && (
First comment
{pkg.firstComment}
)}
Suggested posting time (estimated, not measured)
{pkg.postingTime.text}
); } function ResultsPanel({ results, isPro, onGoPro, onExpandTopic }) { const [active, setActive] = React.useState('hashtags'); const [copiedId, setCopiedId] = React.useState(null); const [selected, setSelected] = React.useState(() => new Set()); const [query, setQuery] = React.useState(''); const [tierFilter, setTierFilter] = React.useState('all'); const [groupBy, setGroupBy] = React.useState('purpose'); const [isFav, setIsFav] = React.useState(false); const [toastMsg, setToastMsg] = React.useState(null); React.useEffect(() => { setActive('hashtags'); setSelected(new Set()); setQuery(''); setTierFilter('all'); const favs = loadFavorites(); setIsFav(favs.some(f => f.topic === results.topic && f.platform === results.platform)); }, [results.topic, results.platform]); const categories = [ { key: 'hashtags', label: 'Hashtags', count: results.hashtags.length, items: results.hashtags }, { key: 'keywords', label: 'Keywords', count: results.keywords.length, items: results.keywords }, { key: 'longTail', label: 'Long-tail', count: results.longTail.length, items: results.longTail }, { key: 'related', label: 'Related searches', count: results.related.length, items: results.related }, { key: 'captions', label: 'Captions & titles', count: (results.captions?.length || 0) + (results.titles?.length || 0), items: [...(results.captions || []), ...(results.titles || [])] }, { key: 'publish', label: 'Publish package', count: null, items: [] }, ]; const current = categories.find(c => c.key === active); const limit = FREE_LIMITS[active] || 999; const proItems = isPro ? current.items : current.items.slice(0, limit); const visibleItems = proItems.filter(item => { if (tierFilter !== 'all' && item.tier !== tierFilter) return false; if (query && !item.tag.toLowerCase().includes(query.toLowerCase())) return false; return true; }); const lockedCount = current.items.length - proItems.length; function copyOne(tag) { navigator.clipboard.writeText(tag).catch(() => {}); setCopiedId(tag); setTimeout(() => setCopiedId(id => (id === tag ? null : id)), 1100); } function toggle(tag) { setSelected(prev => { const n = new Set(prev); n.has(tag) ? n.delete(tag) : n.add(tag); return n; }); } function exportTxt() { downloadBlob(`tagly-${results.topic.replace(/\s+/g, '-')}-${current.key}.txt`, visibleItems.map(i => i.tag).join(' '), 'text/plain'); } function exportCsv() { if (!isPro) { onGoPro(); return; } const rows = ['tag,score,competition,trend,purpose,confidence', ...visibleItems.map(i => `"${i.tag}",${i.score},${i.competition},${i.trend},${i.purpose || ''},${i.confidence || ''}`)]; downloadBlob(`tagly-${results.topic.replace(/\s+/g, '-')}-${current.key}.csv`, rows.join('\n'), 'text/csv'); } function exportMarkdown() { const md = `# ${results.topic} \u2014 ${current.label} (${results.platform})\n\n` + visibleItems.map(i => `- ${i.tag}`).join('\n'); downloadBlob(`tagly-${results.topic.replace(/\s+/g, '-')}-${current.key}.md`, md, 'text/markdown'); } function toggleFavorite() { const favs = loadFavorites(); const exists = favs.some(f => f.topic === results.topic && f.platform === results.platform); const next = exists ? favs.filter(f => !(f.topic === results.topic && f.platform === results.platform)) : [{ topic: results.topic, platform: results.platform, savedAt: Date.now() }, ...favs]; saveFavorites(next); setIsFav(!exists); setToastMsg(exists ? 'Removed from favorites' : 'Saved to favorites'); setTimeout(() => setToastMsg(null), 1600); } function shareLink() { const url = new URL(window.location.href.split('?')[0].split('#')[0]); url.searchParams.set('topic', results.topic); url.searchParams.set('platform', results.platform); url.hash = 'generate'; navigator.clipboard.writeText(url.toString()).catch(() => {}); setToastMsg('Shareable link copied \u2014 anyone can open it to see this result'); setTimeout(() => setToastMsg(null), 2200); } const clusterKeys = groupBy === 'topic' ? [...new Set(visibleItems.map(i => i.topic).filter(Boolean))] : ['awareness', 'engagement', 'niche discovery', 'community', 'conversion']; return (
{isPro ? results.total : Object.values(FREE_LIMITS).reduce((a, b) => a + b, 0)}
tags ready for "{results.topic}" on {results.platform} {!isPro && <> · }
{active === 'hashtags' && (
Recommended for {results.platform}: use about {results.recommendedCount} {results.tagWord} — the top-ranked ones below are marked for you.
)}
{active !== 'publish' && } {toastMsg && {toastMsg}}
{active === 'hashtags' && } {active === 'hashtags' && } {active === 'publish' && } {active === 'hashtags' && (
setQuery(e.target.value)} placeholder="Search tags…" aria-label="Search within results" style={{ height: 34, borderRadius: 'var(--radius-full)', border: '1px solid var(--border-default)', padding: '0 14px', fontSize: 13, fontFamily: 'var(--font-sans)', minWidth: 140 }} /> {['all', 'broad', 'medium', 'niche'].map(t => ( ))}
{['purpose', 'topic'].map(g => ( ))}
)} {active === 'hashtags' ? ( {clusterKeys.map(key => { const group = visibleItems.filter(i => (groupBy === 'topic' ? i.topic === key : i.purpose === key)); if (!group.length) return null; const avgConf = Math.round(group.reduce((s, i) => s + (i.confidence === 'High' ? 90 : i.confidence === 'Medium' ? 60 : 30), 0) / group.length); const avgSpec = Math.round(group.reduce((s, i) => s + (i.specificity || 50), 0) / group.length); const avgReach = Math.round(group.reduce((s, i) => s + (i.reach || 50), 0) / group.length); const heading = groupBy === 'topic' ? (key === 'platform' ? 'Platform culture' : key === 'general' ? 'General' : key.charAt(0).toUpperCase() + key.slice(1)) : PURPOSE_LABEL[key]; return (
{heading} {groupBy === 'purpose' && {PURPOSE_DESC[key]}}
{group.map(item => (
{ copyOne(item.tag); toggle(item.tag); }} title={`${item.reason || ''} \u00b7 confidence ${item.confidence} \u00b7 specificity ${item.specificity} \u00b7 reach ${item.reach} \u00b7 score ${item.score} \u00b7 ${item.competition} competition`} aria-label={`Copy ${item.tag}, ${PURPOSE_LABEL[item.purpose]}, confidence ${item.confidence}`} /> {item.tier && {TIER_LABEL[item.tier]}}
))}
); })}
) : active !== 'publish' && (
{visibleItems.map(item => ( { copyOne(item.tag); toggle(item.tag); }} title={item.reason || `score ${item.score}`} aria-label={`Copy ${item.tag}`} /> ))}
)} {active === 'hashtags' && visibleItems.length === 0 && (

No tags match your search or filter.

)} {!isPro && active !== 'publish' && lockedCount > 0 && (
Pro
{lockedCount} more {current.label.toLowerCase()} ready
Unlock the full ranked set, CSV export, and saved history for $3/mo.
)} {active !== 'publish' && (

Click any tag to copy it individually. Scores are a relative popularity proxy (0–99); competition, specificity, and reach gauges are illustrative in this prototype.

)} {!isPro && active !== 'publish' && }
); } window.TaglyResults = { ResultsPanel };