Generate engaging post ideas based on trending questions from your connections
Size
104.7 KB
Version
2.1.17
Created
Nov 3, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name LinkedIn Post Idea Generator
3// @description Generate engaging post ideas based on trending questions from your connections
4// @version 2.1.17
5// @match https://*.linkedin.com/*
6// @icon https://static.licdn.com/aero-v1/sc/h/3loy7tajf3n0cho89wgg0fjre
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('LinkedIn Post Idea Generator v2.0 initialized');
12
13 // Debounce function to prevent excessive calls
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Function to create the post suggestions button
27 function createSuggestionsButton() {
28 // Check if button already exists
29 if (document.getElementById('post-suggestions-btn')) {
30 console.log('Post suggestions button already exists');
31 return;
32 }
33
34 // Find the share box toolbar
35 const toolbar = document.querySelector('.share-box-feed-entry-toolbar__wrapper');
36 if (!toolbar) {
37 console.log('Toolbar not found, will retry...');
38 return;
39 }
40
41 console.log('Creating post suggestions button');
42
43 // Create the button with prominent styling
44 const button = document.createElement('button');
45 button.id = 'post-suggestions-btn';
46 button.className = 'artdeco-button artdeco-button--4 share-box-feed-entry-toolbar__item';
47 button.setAttribute('aria-label', 'Get AI-powered post suggestions');
48 button.setAttribute('type', 'button');
49
50 // Prominent gradient styling
51 button.style.cssText = `
52 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
53 color: white !important;
54 border: none !important;
55 padding: 10px 20px !important;
56 border-radius: 24px !important;
57 cursor: pointer !important;
58 font-size: 14px !important;
59 font-weight: 700 !important;
60 transition: all 0.3s ease !important;
61 box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
62 position: relative !important;
63 overflow: hidden !important;
64 display: flex !important;
65 align-items: center !important;
66 gap: 8px !important;
67 margin-left: 8px !important;
68 `;
69
70 button.innerHTML = `
71 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" style="filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));">
72 <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"/>
73 </svg>
74 <span style="text-shadow: 0 1px 2px rgba(0,0,0,0.2);">✨ AI Post Ideas</span>
75 `;
76
77 // Add shimmer effect element
78 const shimmer = document.createElement('div');
79 shimmer.style.cssText = `
80 position: absolute;
81 top: 0;
82 left: -100%;
83 width: 100%;
84 height: 100%;
85 background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
86 animation: shimmer 3s infinite;
87 `;
88 button.appendChild(shimmer);
89
90 // Add hover effects
91 button.addEventListener('mouseenter', () => {
92 button.style.transform = 'translateY(-2px) scale(1.05)';
93 button.style.boxShadow = '0 6px 25px rgba(102, 126, 234, 0.6)';
94 });
95
96 button.addEventListener('mouseleave', () => {
97 button.style.transform = 'translateY(0) scale(1)';
98 button.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.4)';
99 });
100
101 // Add click animation
102 button.addEventListener('mousedown', () => {
103 button.style.transform = 'translateY(0) scale(0.98)';
104 });
105
106 button.addEventListener('mouseup', () => {
107 button.style.transform = 'translateY(-2px) scale(1.05)';
108 });
109
110 // Add click event listener
111 button.addEventListener('click', handleGetSuggestions);
112
113 // Insert the button into the toolbar
114 toolbar.appendChild(button);
115 console.log('Post suggestions button created successfully');
116 }
117
118 // Function to show loading state
119 function showLoadingState(button) {
120 button.disabled = true;
121 button.innerHTML = `
122 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" data-supported-dps="24x24" fill="currentColor" class="mercado-match" width="24" height="24" focusable="false">
123 <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2" opacity="0.3"></circle>
124 <path d="M12 2 A10 10 0 0 1 22 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
125 <animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/>
126 </path>
127 </svg>
128 <span class="artdeco-button__text">Generating...</span>
129 `;
130 }
131
132 // Function to reset button state
133 function resetButtonState(button) {
134 button.disabled = false;
135 button.innerHTML = `
136 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" data-supported-dps="24x24" fill="currentColor" class="mercado-match" width="24" height="24" focusable="false">
137 <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path>
138 </svg>
139 <span class="artdeco-button__text">Post Ideas</span>
140 `;
141 }
142
143 // Function to collect recent posts from feed
144 function collectFeedContent() {
145 console.log('Collecting feed content...');
146 const posts = document.querySelectorAll('.feed-shared-update-v2');
147 const feedContent = [];
148
149 // Limit to first 15 posts for better analysis
150 const postsToAnalyze = Array.from(posts).slice(0, 15);
151
152 postsToAnalyze.forEach((post) => {
153 try {
154 // Try multiple selectors for post text content
155 const textElement = post.querySelector('.feed-shared-inline-show-more-text') ||
156 post.querySelector('.update-components-text') ||
157 post.querySelector('.feed-shared-text__text-view');
158
159 if (textElement) {
160 const text = textElement.textContent.trim();
161 if (text.length > 20) {
162 feedContent.push(text);
163 console.log('Collected post text:', text.substring(0, 100) + '...');
164 }
165 }
166 } catch (error) {
167 console.error('Error extracting post content:', error);
168 }
169 });
170
171 console.log(`Collected ${feedContent.length} posts from feed`);
172 return feedContent;
173 }
174
175 // Function to get user's writing style from their past posts
176 async function getUserWritingStyle() {
177 const savedStyle = await GM.getValue('userWritingStyle', null);
178 if (savedStyle) {
179 return JSON.parse(savedStyle);
180 }
181 return null;
182 }
183
184 // ============ PREMIUM FEATURES & USAGE TRACKING ============
185
186 // Function to check if user is premium
187 async function isPremiumUser() {
188 const premiumStatus = await GM.getValue('isPremium', false);
189 return premiumStatus;
190 }
191
192 // Function to set premium status (for testing or after purchase)
193 async function setPremiumStatus(status) {
194 await GM.setValue('isPremium', status);
195 await GM.setValue('premiumActivatedAt', Date.now());
196 }
197
198 // Function to get usage data for current week
199 async function getWeeklyUsage() {
200 const usage = await GM.getValue('weeklyUsage', null);
201
202 if (!usage) {
203 return {
204 count: 0,
205 weekStart: getWeekStart(),
206 lastReset: Date.now()
207 };
208 }
209
210 const parsedUsage = JSON.parse(usage);
211 const currentWeekStart = getWeekStart();
212
213 // Reset if new week
214 if (parsedUsage.weekStart !== currentWeekStart) {
215 return {
216 count: 0,
217 weekStart: currentWeekStart,
218 lastReset: Date.now()
219 };
220 }
221
222 return parsedUsage;
223 }
224
225 // Function to get week start timestamp (Monday 00:00)
226 function getWeekStart() {
227 const now = new Date();
228 const day = now.getDay();
229 const diff = now.getDate() - day + (day === 0 ? -6 : 1); // Adjust to Monday
230 const monday = new Date(now.setDate(diff));
231 monday.setHours(0, 0, 0, 0);
232 return monday.getTime();
233 }
234
235 // Function to increment usage
236 async function incrementUsage() {
237 const usage = await getWeeklyUsage();
238 usage.count += 1;
239 await GM.setValue('weeklyUsage', JSON.stringify(usage));
240 return usage;
241 }
242
243 // Function to check if user can generate posts
244 async function canGeneratePosts() {
245 const isPremium = await isPremiumUser();
246 if (isPremium) {
247 return { allowed: true, remaining: 'unlimited' };
248 }
249
250 // Always allow unlimited generations (removed free tier limit)
251 return { allowed: true, remaining: 'unlimited' };
252 }
253
254 // Function to show upgrade modal
255 function showUpgradeModal(usageInfo) {
256 const modal = document.createElement('div');
257 modal.style.cssText = `
258 position: fixed;
259 top: 0;
260 left: 0;
261 width: 100%;
262 height: 100%;
263 background: rgba(0, 0, 0, 0.7);
264 display: flex;
265 justify-content: center;
266 align-items: center;
267 z-index: 10002;
268 animation: fadeIn 0.3s ease-in-out;
269 `;
270
271 const content = document.createElement('div');
272 content.style.cssText = `
273 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
274 border-radius: 16px;
275 max-width: 500px;
276 padding: 40px;
277 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
278 text-align: center;
279 color: white;
280 `;
281
282 content.innerHTML = `
283 <div style="font-size: 64px; margin-bottom: 20px;">🚀</div>
284 <h2 style="margin: 0 0 16px 0; font-size: 32px; font-weight: 700;">Upgrade to Pro</h2>
285 <p style="font-size: 18px; margin-bottom: 24px; opacity: 0.95;">You've used all ${usageInfo.limit} free post generations this week!</p>
286
287 <div style="background: rgba(255, 255, 255, 0.15); border-radius: 12px; padding: 24px; margin-bottom: 24px; backdrop-filter: blur(10px);">
288 <h3 style="margin: 0 0 16px 0; font-size: 20px; font-weight: 600;">Pro Features:</h3>
289 <ul style="list-style: none; padding: 0; margin: 0; text-align: left;">
290 <li style="padding: 8px 0; font-size: 16px;">✅ Unlimited post generations</li>
291 <li style="padding: 8px 0; font-size: 16px;">✅ Generate variations (3 styles per post)</li>
292 <li style="padding: 8px 0; font-size: 16px;">✅ Article outline generator</li>
293 <li style="padding: 8px 0; font-size: 16px;">✅ Save unlimited ideas</li>
294 <li style="padding: 8px 0; font-size: 16px;">✅ Advanced analytics dashboard</li>
295 <li style="padding: 8px 0; font-size: 16px;">✅ Priority support</li>
296 </ul>
297 </div>
298
299 <div style="font-size: 48px; font-weight: 700; margin-bottom: 8px;">$14.99<span style="font-size: 20px; font-weight: 400;">/month</span></div>
300 <p style="font-size: 14px; margin-bottom: 24px; opacity: 0.9;">Cancel anytime. No questions asked.</p>
301
302 <button id="upgrade-now-btn" style="background: white; color: #667eea; border: none; padding: 16px 48px; border-radius: 30px; cursor: pointer; font-size: 18px; font-weight: 700; margin-bottom: 16px; width: 100%; transition: transform 0.2s, box-shadow 0.2s;">
303 Upgrade Now
304 </button>
305
306 <button id="close-upgrade-modal" style="background: transparent; color: white; border: 2px solid rgba(255, 255, 255, 0.5); padding: 12px 32px; border-radius: 30px; cursor: pointer; font-size: 16px; font-weight: 600; width: 100%; transition: all 0.2s;">
307 Maybe Later
308 </button>
309 `;
310
311 modal.appendChild(content);
312 document.body.appendChild(modal);
313
314 // Add hover effects
315 const upgradeBtn = content.querySelector('#upgrade-now-btn');
316 upgradeBtn.addEventListener('mouseenter', () => {
317 upgradeBtn.style.transform = 'scale(1.05)';
318 upgradeBtn.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.2)';
319 });
320 upgradeBtn.addEventListener('mouseleave', () => {
321 upgradeBtn.style.transform = 'scale(1)';
322 upgradeBtn.style.boxShadow = 'none';
323 });
324
325 // Event listeners
326 upgradeBtn.addEventListener('click', () => {
327 // Open upgrade page (replace with your actual payment link)
328 GM.openInTab('https://your-payment-page.com/upgrade', false);
329 });
330
331 content.querySelector('#close-upgrade-modal').addEventListener('click', () => {
332 modal.remove();
333 });
334
335 modal.addEventListener('click', (e) => {
336 if (e.target === modal) {
337 modal.remove();
338 }
339 });
340 }
341
342 // Function to show premium feature lock
343 function showPremiumLock(featureName) {
344 showNotification(`${featureName} is a Pro feature. Upgrade to unlock!`, 'info');
345 setTimeout(() => {
346 showUpgradeModal({ limit: 3, used: 3 });
347 }, 500);
348 }
349
350 // Function to check for premium activation from URL
351 async function checkPremiumActivation() {
352 const urlParams = new URLSearchParams(window.location.search);
353 const sessionId = urlParams.get('session_id');
354 const premiumActivated = urlParams.get('premium');
355
356 // Check if returning from successful Stripe payment
357 if (premiumActivated === 'success' && sessionId) {
358 console.log('Premium activation detected from Stripe success URL');
359
360 // Activate premium
361 await setPremiumStatus(true);
362
363 // Show success notification
364 showNotification('🎉 Welcome to Pro! You now have unlimited access to all features!', 'success');
365
366 // Clean up URL
367 const cleanUrl = window.location.origin + window.location.pathname;
368 window.history.replaceState({}, document.title, cleanUrl);
369
370 console.log('Premium activated successfully');
371 }
372 }
373
374 // ============ END PREMIUM FEATURES ============
375
376 // Function to get saved post ideas
377 async function getSavedIdeas() {
378 const saved = await GM.getValue('savedPostIdeas', '[]');
379 return JSON.parse(saved);
380 }
381
382 // Function to save a post idea
383 async function savePostIdea(idea) {
384 const saved = await getSavedIdeas();
385 saved.unshift({
386 ...idea,
387 savedAt: Date.now(),
388 id: Date.now()
389 });
390 await GM.setValue('savedPostIdeas', JSON.stringify(saved.slice(0, 50))); // Keep last 50
391 }
392
393 // Function to get used post ideas
394 async function getUsedIdeas() {
395 const used = await GM.getValue('usedPostIdeas', '[]');
396 return JSON.parse(used);
397 }
398
399 // Function to mark idea as used
400 async function markIdeaAsUsed(idea) {
401 const used = await getUsedIdeas();
402 used.unshift({
403 ...idea,
404 usedAt: Date.now()
405 });
406 await GM.setValue('usedPostIdeas', JSON.stringify(used.slice(0, 100)));
407 }
408
409 // Function to get trending topics history
410 async function getTrendingHistory() {
411 const history = await GM.getValue('trendingHistory', '[]');
412 return JSON.parse(history);
413 }
414
415 // Function to save trending topics
416 async function saveTrendingTopics(topics) {
417 const history = await getTrendingHistory();
418 history.unshift({
419 topics: topics,
420 timestamp: Date.now()
421 });
422 await GM.setValue('trendingHistory', JSON.stringify(history.slice(0, 30))); // Keep last 30 days
423 }
424
425 // Function to handle getting suggestions
426 async function handleGetSuggestions(event) {
427 const button = event.currentTarget;
428 console.log('Get suggestions button clicked');
429
430 try {
431 // Check if user can generate posts
432 const usageCheck = await canGeneratePosts();
433
434 if (!usageCheck.allowed) {
435 console.log('Usage limit reached');
436 resetButtonState(button);
437 showUpgradeModal(usageCheck);
438 return;
439 }
440
441 showLoadingState(button);
442
443 // Show remaining uses for free users
444 const isPremium = await isPremiumUser();
445 if (!isPremium && usageCheck.remaining !== 'unlimited') {
446 showNotification(`Generating... (${usageCheck.remaining - 1} uses left this week)`, 'info');
447 }
448
449 // Collect user profile information
450 const userProfile = await collectUserProfile();
451 console.log('User profile collected:', userProfile);
452
453 // Collect feed content
454 const feedContent = collectFeedContent();
455
456 if (feedContent.length === 0) {
457 showNotification('No posts found in your feed. Please scroll down to load more posts.', 'warning');
458 resetButtonState(button);
459 return;
460 }
461
462 // Get user's writing style if available
463 await getUserWritingStyle();
464
465 // Create comprehensive prompt for AI with user profile context
466 const prompt = `You are a LinkedIn content strategist helping ${userProfile.name || 'a professional'} create authentic, high-performing posts.
467
468USER PROFILE:
469- Name: ${userProfile.name || 'Unknown'}
470- Role: ${userProfile.headline || 'Professional'}
471- Business: ${userProfile.business || 'their company'}
472- Industry Focus: ${userProfile.industry || 'their field'}
473
474WHAT THEIR NETWORK IS TALKING ABOUT:
475${feedContent.join('\n\n---\n\n')}
476
477YOUR MISSION: Generate 5 LinkedIn posts that sound like they were written by a REAL PERSON sharing genuine experiences, not a marketing robot.
478
479🚫 WHAT TO ABSOLUTELY AVOID:
480- Corporate jargon ("synergy", "leverage", "circle back", "touch base")
481- Motivational poster language ("hustle", "grind", "rise and grind")
482- Fake vulnerability ("I'll be honest...", "Can I be real for a second?")
483- Generic advice that could apply to anyone
484- Clickbait hooks that don't deliver
485- Overly polished, perfect stories with no real struggle
486- Lists that sound like they came from a template
487- Ending every post with "Agree?" or "Thoughts?"
488
489✅ WHAT MAKES POSTS FEEL HUMAN:
490
4911. REAL STORIES WITH MESSY DETAILS:
492 - Include specific moments: "Tuesday morning, 6:47 AM, staring at my laptop"
493 - Name real emotions: frustration, embarrassment, confusion, relief
494 - Share the awkward parts: "I had no idea what I was doing"
495 - Use actual dialogue: "My client said: 'This isn't what we discussed'"
496 - Include sensory details: what you saw, heard, felt
497
4982. CONVERSATIONAL VOICE:
499 - Write like you're texting a friend who works in your industry
500 - Use contractions: "I'm", "don't", "wasn't", "here's"
501 - Start sentences with "And", "But", "So" when it feels natural
502 - Use short sentences. Really short. Like this.
503 - Break grammar rules if it sounds more natural
504 - Use parentheses for side thoughts (we all have them)
505
5063. SPECIFIC, NOT GENERIC:
507 BAD: "I learned an important lesson about communication"
508 GOOD: "I sent a 3-paragraph email when a 2-minute call would've fixed everything"
509
510 BAD: "Focus on providing value to your customers"
511 GOOD: "Last week, a customer called at 9 PM. I almost didn't answer. That call turned into our biggest deal this quarter."
512
5134. SHOW YOUR PERSONALITY:
514 - Include your quirks: "I'm the person who color-codes everything"
515 - Admit your flaws: "I'm terrible at remembering names"
516 - Share your unpopular opinions: "I think most meetings are useless"
517 - Use humor (but keep it natural, not forced)
518 - Reference pop culture if it fits naturally
519
5205. VULNERABLE BUT NOT PERFORMATIVE:
521 - Share real failures with actual consequences
522 - Don't wrap everything in a neat lesson
523 - It's okay if you're still figuring things out
524 - Admit when you don't have all the answers
525 - Show the messy middle, not just the before/after
526
5276. HOOKS THAT SOUND LIKE REAL THOUGHTS:
528 GOOD EXAMPLES:
529 - "I just lost a $40K client because I was too afraid to say no."
530 - "My competitor is cheaper, faster, and honestly? Better at marketing than me."
531 - "I've been lying to myself about why my business isn't growing."
532 - "Three years ago, I had no idea what I was doing. Still don't, but now I'm better at faking it."
533 - "The advice that everyone gives? It doesn't work for me."
534
535 BAD EXAMPLES:
536 - "Success is a journey, not a destination"
537 - "In today's fast-paced world..."
538 - "Let me share something that changed my life"
539 - "Here's what nobody tells you about..."
540
5417. STRUCTURE THAT FEELS NATURAL:
542 - Don't force a structure. Let the story flow.
543 - Use line breaks for emphasis
544 - One-sentence paragraphs are powerful
545 - Lists are fine, but make them feel spontaneous
546 - End with a real question or thought, not a forced CTA
547
5488. BUSINESS RELEVANCE (WITHOUT BEING SALESY):
549 - Weave in what you do naturally through the story
550 - Show, don't tell: "When the phone rang at 2 AM..." (shows 24/7 service)
551 - Let the value be obvious from the story
552 - Never end with "DM me to learn more"
553 - If you mention your service, make it part of the story, not the point
554
555SPECIFIC TOPICS FOR ${userProfile.business || 'their business'}:
556- The call you almost didn't answer (and why you're glad you did)
557- A customer who taught you something unexpected
558- The mistake that cost you money but taught you everything
559- Why you started doing things differently than everyone else
560- The moment you realized your old way wasn't working
561- A conversation that changed how you think about your business
562- The client you had to fire (and what you learned)
563- Something your competitor does better (and what you do instead)
564
565TONE GUIDELINES:
566- Write like you're talking to a colleague over coffee
567- Be confident without being cocky
568- Be helpful without being preachy
569- Be professional without being corporate
570- Be real without being unprofessional
571
572FOR EACH POST, PROVIDE:
5731. Hook: First line that sounds like a real thought (not a headline)
5742. Topic: What the post is actually about (in plain English)
5753. Key Points: The main beats of the story/idea (not a bulleted list format)
5764. Call to Action: A genuine question or thought (not "What do you think?")
5775. Category: Type of content
5786. Engagement Score: Realistic prediction (60-95)
5797. Best Time: When to post
5808. Hashtags: 5-7 relevant tags
5819. Reading Time: 2-4 minutes
58210. Target Audience: Who this resonates with
583
584Remember: The best LinkedIn posts sound like someone sharing a real experience with friends who happen to work in the same industry. Not a brand. Not a guru. Just a person.`;
585
586 console.log('Calling AI to generate comprehensive post suggestions...');
587
588 // Call AI API with enhanced structured output
589 const suggestions = await RM.aiCall(prompt, {
590 type: 'json_schema',
591 json_schema: {
592 name: 'enhanced_post_suggestions',
593 schema: {
594 type: 'object',
595 properties: {
596 commonThemes: {
597 type: 'array',
598 items: { type: 'string' },
599 description: 'Common themes found in the feed'
600 },
601 trendingTopics: {
602 type: 'array',
603 items: { type: 'string' },
604 description: 'Currently trending topics in the network'
605 },
606 postIdeas: {
607 type: 'array',
608 items: {
609 type: 'object',
610 properties: {
611 hook: { type: 'string', description: 'Catchy opening line' },
612 topic: { type: 'string', description: 'Main topic or question' },
613 keyPoints: {
614 type: 'array',
615 items: { type: 'string' },
616 description: 'Key points to cover'
617 },
618 callToAction: { type: 'string', description: 'Suggested CTA' },
619 category: {
620 type: 'string',
621 enum: ['Career Advice', 'Industry Trends', 'Personal Story', 'How-To Guide', 'Opinion/Thought Leadership', 'Thought Leadership', 'Question/Poll', 'Case Study', 'Tips & Tricks'],
622 description: 'Content category'
623 },
624 engagementScore: {
625 type: 'number',
626 minimum: 0,
627 maximum: 100,
628 description: 'Predicted engagement score'
629 },
630 bestTimeToPost: {
631 type: 'string',
632 enum: ['Morning (7-9 AM)', 'Midday (12-2 PM)', 'Afternoon (3-5 PM)', 'Evening (6-8 PM)'],
633 description: 'Optimal posting time'
634 },
635 hashtags: {
636 type: 'array',
637 items: { type: 'string' },
638 description: 'Relevant hashtags'
639 },
640 readingTime: {
641 type: 'number',
642 description: 'Estimated reading time in minutes'
643 },
644 targetAudience: {
645 type: 'string',
646 description: 'Target audience for this post'
647 }
648 },
649 required: ['hook', 'topic', 'keyPoints', 'callToAction', 'category', 'engagementScore', 'bestTimeToPost', 'hashtags', 'readingTime', 'targetAudience']
650 },
651 minItems: 5,
652 maxItems: 5
653 }
654 },
655 required: ['commonThemes', 'trendingTopics', 'postIdeas']
656 }
657 }
658 });
659
660 console.log('AI suggestions received:', suggestions);
661
662 // Save user profile for future use
663 await GM.setValue('userProfile', JSON.stringify(userProfile));
664
665 // Save trending topics to history
666 await saveTrendingTopics(suggestions.trendingTopics || suggestions.commonThemes);
667
668 // Increment usage counter for free users
669 if (!isPremium) {
670 await incrementUsage();
671 }
672
673 // Display enhanced suggestions
674 displayEnhancedSuggestions(suggestions);
675
676 // Save suggestions to storage
677 await GM.setValue('lastPostSuggestions', JSON.stringify(suggestions));
678 await GM.setValue('lastSuggestionsTime', Date.now());
679
680 resetButtonState(button);
681 showNotification('Post ideas generated successfully!', 'success');
682
683 } catch (error) {
684 console.error('Error generating suggestions:', error);
685 resetButtonState(button);
686 showNotification('Failed to generate post ideas. Please try again.', 'error');
687 }
688 }
689
690 // Function to collect user profile information
691 async function collectUserProfile() {
692 console.log('Collecting user profile information...');
693
694 // Try to get cached profile first
695 const cachedProfile = await GM.getValue('userProfile', null);
696 if (cachedProfile) {
697 const profile = JSON.parse(cachedProfile);
698 console.log('Using cached profile:', profile);
699 return profile;
700 }
701
702 const profile = {};
703
704 // Get name from global nav
705 const nameElement = document.querySelector('.global-nav__me-photo');
706 if (nameElement && nameElement.alt) {
707 profile.name = nameElement.alt;
708 }
709
710 // Try to get headline from feed identity module
711 const headlineElement = document.querySelector('.feed-identity-module__actor-meta .feed-identity-module__actor-headline');
712 if (headlineElement) {
713 profile.headline = headlineElement.textContent.trim();
714 }
715
716 // Extract business/industry from headline
717 if (profile.headline) {
718 // Try to extract company name
719 const companyMatch = profile.headline.match(/(?:at|@)\s+([^|•]+)/i);
720 if (companyMatch) {
721 profile.business = companyMatch[1].trim();
722 }
723
724 // Try to extract industry keywords
725 const industryKeywords = ['CEO', 'Founder', 'Director', 'Manager', 'Consultant', 'Specialist', 'Expert', 'Engineer', 'Developer', 'Designer', 'Marketing', 'Sales', 'HR', 'Finance'];
726 for (const keyword of industryKeywords) {
727 if (profile.headline.toLowerCase().includes(keyword.toLowerCase())) {
728 profile.industry = keyword;
729 break;
730 }
731 }
732 }
733
734 // Get profile URL
735 const profileLink = document.querySelector('a[href*="/in/"]');
736 if (profileLink) {
737 profile.profileUrl = profileLink.href;
738 }
739
740 console.log('Profile collected:', profile);
741 return profile;
742 }
743
744 // Function to generate variations of a post idea
745 async function generateVariations(idea) {
746 try {
747 const prompt = `Generate 3 different variations of this LinkedIn post idea, each with a different tone and style:
748
749Original Idea:
750Hook: ${idea.hook}
751Topic: ${idea.topic}
752Key Points: ${idea.keyPoints.join(', ')}
753CTA: ${idea.callToAction}
754
755Create variations with these tones:
7561. Professional and formal
7572. Casual and conversational
7583. Inspirational and motivational
759
760For each variation, provide the complete post text.`;
761
762 const variations = await RM.aiCall(prompt, {
763 type: 'json_schema',
764 json_schema: {
765 name: 'post_variations',
766 schema: {
767 type: 'object',
768 properties: {
769 variations: {
770 type: 'array',
771 items: {
772 type: 'object',
773 properties: {
774 tone: { type: 'string' },
775 postText: { type: 'string' }
776 },
777 required: ['tone', 'postText']
778 },
779 minItems: 3,
780 maxItems: 3
781 }
782 },
783 required: ['variations']
784 }
785 }
786 });
787
788 return variations.variations;
789 } catch (error) {
790 console.error('Error generating variations:', error);
791 throw error;
792 }
793 }
794
795 // Function to generate article outline
796 async function generateArticleOutline(idea) {
797 try {
798 const prompt = `Expand this LinkedIn post idea into a comprehensive article outline:
799
800Topic: ${idea.topic}
801Hook: ${idea.hook}
802Key Points: ${idea.keyPoints.join(', ')}
803
804Create a detailed article outline with:
805- Compelling title
806- Introduction
807- 5-7 main sections with subsections
808- Conclusion
809- Key takeaways`;
810
811 const outline = await RM.aiCall(prompt, {
812 type: 'json_schema',
813 json_schema: {
814 name: 'article_outline',
815 schema: {
816 type: 'object',
817 properties: {
818 title: { type: 'string' },
819 introduction: { type: 'string' },
820 sections: {
821 type: 'array',
822 items: {
823 type: 'object',
824 properties: {
825 heading: { type: 'string' },
826 subsections: {
827 type: 'array',
828 items: { type: 'string' }
829 }
830 },
831 required: ['heading', 'subsections']
832 }
833 },
834 conclusion: { type: 'string' },
835 keyTakeaways: {
836 type: 'array',
837 items: { type: 'string' }
838 }
839 },
840 required: ['title', 'introduction', 'sections', 'conclusion', 'keyTakeaways']
841 }
842 }
843 });
844
845 return outline;
846 } catch (error) {
847 console.error('Error generating article outline:', error);
848 throw error;
849 }
850 }
851
852 // Function to display enhanced suggestions with tabs
853 function displayEnhancedSuggestions(suggestions) {
854 console.log('Displaying enhanced suggestions modal');
855
856 // Remove existing modal if any
857 const existingModal = document.getElementById('post-suggestions-modal');
858 if (existingModal) {
859 existingModal.remove();
860 }
861
862 // Create modal overlay
863 const modal = document.createElement('div');
864 modal.id = 'post-suggestions-modal';
865 modal.style.cssText = `
866 position: fixed;
867 top: 0;
868 left: 0;
869 width: 100%;
870 height: 100%;
871 background: rgba(0, 0, 0, 0.75);
872 backdrop-filter: blur(8px);
873 display: flex;
874 justify-content: center;
875 align-items: center;
876 z-index: 10000;
877 animation: fadeIn 0.3s ease-in-out;
878 padding: 20px;
879 `;
880
881 // Create modal content
882 const modalContent = document.createElement('div');
883 modalContent.style.cssText = `
884 background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
885 border-radius: 20px;
886 max-width: 1000px;
887 width: 100%;
888 max-height: 90vh;
889 overflow: hidden;
890 box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
891 animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
892 display: flex;
893 flex-direction: column;
894 border: 1px solid rgba(255, 255, 255, 0.8);
895 `;
896
897 // Build modal HTML with tabs
898 let html = `
899 <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 24px; border-radius: 20px 20px 0 0; position: relative; overflow: visible;">
900 <!-- Decorative elements -->
901 <div style="position: absolute; top: -50px; right: -50px; width: 200px; height: 200px; background: rgba(255, 255, 255, 0.1); border-radius: 50%; filter: blur(40px);"></div>
902 <div style="position: absolute; bottom: -30px; left: -30px; width: 150px; height: 150px; background: rgba(255, 255, 255, 0.1); border-radius: 50%; filter: blur(30px);"></div>
903
904 <div style="position: relative; z-index: 1;">
905 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
906 <div style="display: flex; align-items: center; gap: 12px; flex: 1;">
907 <div style="background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); padding: 10px; border-radius: 12px; display: flex; align-items: center; justify-content: center;">
908 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24" height="24">
909 <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"/>
910 </svg>
911 </div>
912 <div>
913 <h2 style="margin: 0; font-size: 24px; font-weight: 700; color: white; text-shadow: 0 2px 4px rgba(0,0,0,0.1); line-height: 1.2;">AI Post Ideas</h2>
914 <p style="margin: 4px 0 0 0; font-size: 13px; color: rgba(255, 255, 255, 0.9); font-weight: 500;">Powered by advanced AI</p>
915 </div>
916 </div>
917 <button id="close-suggestions-modal" style="background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border: none; width: 36px; height: 36px; border-radius: 10px; cursor: pointer; color: white; font-size: 20px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; font-weight: 300; flex-shrink: 0;">×</button>
918 </div>
919
920 <!-- Modern Tabs -->
921 <div style="display: flex; gap: 6px; position: relative; z-index: 1; flex-wrap: wrap;">
922 <button class="tab-btn active" data-tab="ideas" style="background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border: none; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 600; color: #667eea; transition: all 0.3s; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
923 <span style="display: flex; align-items: center; gap: 6px;">
924 <span>💡</span>
925 <span>Post Ideas</span>
926 </span>
927 </button>
928 <button class="tab-btn" data-tab="customize" style="background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border: none; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 600; color: white; transition: all 0.3s;">
929 <span style="display: flex; align-items: center; gap: 6px;">
930 <span>⚙️</span>
931 <span>Customize</span>
932 </span>
933 </button>
934 <button class="tab-btn" data-tab="saved" style="background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border: none; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 600; color: white; transition: all 0.3s;">
935 <span style="display: flex; align-items: center; gap: 6px;">
936 <span>💾</span>
937 <span>Saved</span>
938 </span>
939 </button>
940 <button class="tab-btn" data-tab="analytics" style="background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border: none; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 600; color: white; transition: all 0.3s;">
941 <span style="display: flex; align-items: center; gap: 6px;">
942 <span>📊</span>
943 <span>Analytics</span>
944 </span>
945 </button>
946 </div>
947 </div>
948 </div>
949
950 <div style="overflow-y: auto; padding: 32px; flex: 1; background: #ffffff;">
951 <!-- Ideas Tab -->
952 <div id="tab-ideas" class="tab-content" style="display: block;">
953 ${generateIdeasTabContent(suggestions)}
954 </div>
955
956 <!-- Customize Tab -->
957 <div id="tab-customize" class="tab-content" style="display: none;">
958 <div id="customize-container"></div>
959 </div>
960
961 <!-- Saved Tab -->
962 <div id="tab-saved" class="tab-content" style="display: none;">
963 <div id="saved-ideas-container">
964 <p style="color: #666; text-align: center; padding: 40px;">Loading saved ideas...</p>
965 </div>
966 </div>
967
968 <!-- Analytics Tab -->
969 <div id="tab-analytics" class="tab-content" style="display: none;">
970 <div id="analytics-container">
971 <p style="color: #666; text-align: center; padding: 40px;">Loading analytics...</p>
972 </div>
973 </div>
974 </div>
975 `;
976
977 modalContent.innerHTML = html;
978 modal.appendChild(modalContent);
979 document.body.appendChild(modal);
980
981 // Load customize options immediately
982 loadCustomizeOptions(modalContent, suggestions);
983
984 // Setup tab switching
985 setupTabs(modalContent, suggestions);
986
987 // Setup event listeners
988 setupModalEventListeners(modal, modalContent, suggestions);
989
990 console.log('Enhanced suggestions modal displayed');
991 }
992
993 // Function to generate ideas tab content
994 function generateIdeasTabContent(suggestions) {
995 let html = '';
996
997 // Add trending topics section
998 if (suggestions.trendingTopics && suggestions.trendingTopics.length > 0) {
999 html += `
1000 <div style="margin-bottom: 24px; padding: 16px; background: #f3f6f8; border-radius: 8px;">
1001 <h3 style="margin: 0 0 12px 0; font-size: 16px; font-weight: 600; color: #000;">🔥 Trending Topics in Your Network</h3>
1002 <div style="display: flex; flex-wrap: wrap; gap: 8px;">
1003 ${suggestions.trendingTopics.map(topic => `
1004 <span style="background: #0a66c2; color: white; padding: 6px 12px; border-radius: 16px; font-size: 14px;">${topic}</span>
1005 `).join('')}
1006 </div>
1007 </div>
1008 `;
1009 }
1010
1011 // Add filter options
1012 html += `
1013 <div style="margin-bottom: 20px; display: flex; gap: 12px; flex-wrap: wrap; align-items: center;">
1014 <span style="font-weight: 600; color: #000;">Filter by:</span>
1015 <select id="category-filter" style="padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px; cursor: pointer; min-width: 200px;">
1016 <option value="all">All Categories</option>
1017 <option value="Career Advice">Career Advice</option>
1018 <option value="Industry Trends">Industry Trends</option>
1019 <option value="Personal Story">Personal Story</option>
1020 <option value="How-To Guide">How-To Guide</option>
1021 <option value="Opinion/Thought Leadership">Opinion/Thought Leadership</option>
1022 <option value="Question/Poll">Question/Poll</option>
1023 <option value="Case Study">Case Study</option>
1024 <option value="Tips & Tricks">Tips & Tricks</option>
1025 </select>
1026 <select id="engagement-filter" style="padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px; cursor: pointer; min-width: 200px;">
1027 <option value="all">All Engagement Levels</option>
1028 <option value="high">High Engagement (80+)</option>
1029 <option value="medium">Medium Engagement (50-79)</option>
1030 <option value="low">Low Engagement (0-49)</option>
1031 </select>
1032 </div>
1033 `;
1034
1035 // Add post ideas
1036 html += '<div id="post-ideas-container" style="display: flex; flex-direction: column; gap: 20px;">';
1037 suggestions.postIdeas.forEach((idea, index) => {
1038 html += generatePostIdeaCard(idea, index);
1039 });
1040 html += '</div>';
1041
1042 return html;
1043 }
1044
1045 // Function to generate a post idea card
1046 function generatePostIdeaCard(idea, index) {
1047 const engagementColor = idea.engagementScore >= 80 ? '#10b981' : idea.engagementScore >= 50 ? '#3b82f6' : '#6b7280';
1048 const engagementBg = idea.engagementScore >= 80 ? '#d1fae5' : idea.engagementScore >= 50 ? '#dbeafe' : '#f3f4f6';
1049
1050 return `
1051 <div class="post-idea-card" data-index="${index}" data-category="${idea.category}" data-engagement="${idea.engagementScore}"
1052 style="background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
1053 border: 2px solid #e5e7eb;
1054 border-radius: 16px;
1055 padding: 24px;
1056 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1057 position: relative;
1058 overflow: hidden;">
1059
1060 <!-- Decorative gradient bar -->
1061 <div style="position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);"></div>
1062
1063 <!-- Header Section -->
1064 <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 16px;">
1065 <div style="flex: 1;">
1066 <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap;">
1067 <span style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1068 color: white;
1069 padding: 6px 14px;
1070 border-radius: 20px;
1071 font-size: 12px;
1072 font-weight: 700;
1073 text-transform: uppercase;
1074 letter-spacing: 0.5px;
1075 box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);">
1076 ${idea.category}
1077 </span>
1078 <span style="background: ${engagementBg};
1079 color: ${engagementColor};
1080 padding: 6px 12px;
1081 border-radius: 20px;
1082 font-size: 13px;
1083 font-weight: 700;
1084 display: flex;
1085 align-items: center;
1086 gap: 4px;">
1087 <svg width="14" height="14" viewBox="0 0 24 24" fill="${engagementColor}">
1088 <path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z"/>
1089 </svg>
1090 ${idea.engagementScore}% Engagement
1091 </span>
1092 </div>
1093 <h3 style="margin: 0;
1094 font-size: 22px;
1095 font-weight: 700;
1096 color: #1f2937;
1097 line-height: 1.3;
1098 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1099 -webkit-background-clip: text;
1100 -webkit-text-fill-color: transparent;
1101 background-clip: text;">
1102 ${idea.topic}
1103 </h3>
1104 </div>
1105 </div>
1106
1107 <!-- Hook Section -->
1108 <div style="margin-bottom: 20px;
1109 padding: 16px;
1110 background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
1111 border-left: 4px solid #f59e0b;
1112 border-radius: 8px;">
1113 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
1114 <span style="font-size: 20px;">🎣</span>
1115 <strong style="color: #92400e; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Hook</strong>
1116 </div>
1117 <p style="margin: 0;
1118 color: #78350f;
1119 font-size: 16px;
1120 line-height: 1.6;
1121 font-weight: 500;
1122 font-style: italic;">
1123 "${idea.hook}"
1124 </p>
1125 </div>
1126
1127 <!-- Key Points Section -->
1128 <div style="margin-bottom: 20px;
1129 padding: 16px;
1130 background: #f0f9ff;
1131 border-radius: 12px;
1132 border: 1px solid #bae6fd;">
1133 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
1134 <span style="font-size: 18px;">💎</span>
1135 <strong style="color: #0c4a6e; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Key Points</strong>
1136 </div>
1137 <ul style="margin: 0; padding-left: 24px; color: #0f172a; list-style: none;">
1138 ${idea.keyPoints.map((point, i) => `
1139 <li style="margin-bottom: 10px;
1140 line-height: 1.6;
1141 position: relative;
1142 padding-left: 8px;
1143 font-size: 15px;">
1144 <span style="position: absolute;
1145 left: -20px;
1146 top: 0;
1147 color: #3b82f6;
1148 font-weight: 700;">
1149 ${i + 1}.
1150 </span>
1151 ${point}
1152 </li>
1153 `).join('')}
1154 </ul>
1155 </div>
1156
1157 <!-- CTA Section -->
1158 <div style="margin-bottom: 20px;
1159 padding: 16px;
1160 background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%);
1161 border-left: 4px solid #10b981;
1162 border-radius: 8px;">
1163 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
1164 <span style="font-size: 18px;">🎯</span>
1165 <strong style="color: #065f46; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Call to Action</strong>
1166 </div>
1167 <p style="margin: 0; color: #064e3b; font-size: 15px; line-height: 1.6; font-weight: 500;">
1168 ${idea.callToAction}
1169 </p>
1170 </div>
1171
1172 <!-- Meta Information Grid -->
1173 <div style="margin-bottom: 20px;
1174 display: grid;
1175 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
1176 gap: 12px;">
1177 <div style="padding: 12px;
1178 background: #fef2f2;
1179 border-radius: 10px;
1180 border: 1px solid #fecaca;">
1181 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
1182 <span style="font-size: 16px;">⏰</span>
1183 <strong style="color: #991b1b; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px;">Best Time</strong>
1184 </div>
1185 <span style="color: #7f1d1d; font-size: 14px; font-weight: 600;">${idea.bestTimeToPost}</span>
1186 </div>
1187 <div style="padding: 12px;
1188 background: #f0fdf4;
1189 border-radius: 10px;
1190 border: 1px solid #bbf7d0;">
1191 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
1192 <span style="font-size: 16px;">📖</span>
1193 <strong style="color: #166534; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px;">Reading Time</strong>
1194 </div>
1195 <span style="color: #14532d; font-size: 14px; font-weight: 600;">${idea.readingTime} minutes</span>
1196 </div>
1197 <div style="padding: 12px;
1198 background: #faf5ff;
1199 border-radius: 10px;
1200 border: 1px solid #e9d5ff;
1201 grid-column: 1 / -1;">
1202 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
1203 <span style="font-size: 16px;">🎯</span>
1204 <strong style="color: #6b21a8; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px;">Target Audience</strong>
1205 </div>
1206 <span style="color: #581c87; font-size: 14px; font-weight: 600;">${idea.targetAudience}</span>
1207 </div>
1208 </div>
1209
1210 <!-- Hashtags Section -->
1211 <div style="margin-bottom: 20px;">
1212 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
1213 <span style="font-size: 18px;">#️⃣</span>
1214 <strong style="color: #1f2937; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;">Hashtags</strong>
1215 </div>
1216 <div style="display: flex; flex-wrap: wrap; gap: 8px;">
1217 ${idea.hashtags.map(tag => `
1218 <span style="background: linear-gradient(135deg, #e0e7ff 0%, #ddd6fe 100%);
1219 color: #5b21b6;
1220 padding: 8px 14px;
1221 border-radius: 20px;
1222 font-size: 13px;
1223 font-weight: 600;
1224 border: 1px solid #c4b5fd;
1225 transition: all 0.2s;">
1226 #${tag.replace('#', '')}
1227 </span>
1228 `).join('')}
1229 </div>
1230 </div>
1231
1232 <!-- Action Buttons -->
1233 <div style="display: flex; gap: 10px; flex-wrap: wrap; padding-top: 16px; border-top: 2px solid #f3f4f6;">
1234 <button class="copy-idea-btn" data-index="${index}"
1235 style="background: white;
1236 color: #3b82f6;
1237 border: 2px solid #3b82f6;
1238 padding: 10px 18px;
1239 border-radius: 10px;
1240 cursor: pointer;
1241 font-size: 14px;
1242 font-weight: 600;
1243 transition: all 0.3s;
1244 display: flex;
1245 align-items: center;
1246 gap: 6px;">
1247 <span>📋</span>
1248 <span>Copy</span>
1249 </button>
1250 <button class="save-idea-btn" data-index="${index}"
1251 style="background: white;
1252 color: #10b981;
1253 border: 2px solid #10b981;
1254 padding: 10px 18px;
1255 border-radius: 10px;
1256 cursor: pointer;
1257 font-size: 14px;
1258 font-weight: 600;
1259 transition: all 0.3s;
1260 display: flex;
1261 align-items: center;
1262 gap: 6px;">
1263 <span>💾</span>
1264 <span>Save</span>
1265 </button>
1266 <button class="variations-btn" data-index="${index}"
1267 style="background: white;
1268 color: #8b5cf6;
1269 border: 2px solid #8b5cf6;
1270 padding: 10px 18px;
1271 border-radius: 10px;
1272 cursor: pointer;
1273 font-size: 14px;
1274 font-weight: 600;
1275 transition: all 0.3s;
1276 display: flex;
1277 align-items: center;
1278 gap: 6px;">
1279 <span>🔄</span>
1280 <span>Variations</span>
1281 </button>
1282 <button class="article-btn" data-index="${index}"
1283 style="background: white;
1284 color: #ec4899;
1285 border: 2px solid #ec4899;
1286 padding: 10px 18px;
1287 border-radius: 10px;
1288 cursor: pointer;
1289 font-size: 14px;
1290 font-weight: 600;
1291 transition: all 0.3s;
1292 display: flex;
1293 align-items: center;
1294 gap: 6px;">
1295 <span>📝</span>
1296 <span>Article</span>
1297 </button>
1298 <button class="use-idea-btn" data-index="${index}"
1299 style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1300 color: white;
1301 border: none;
1302 padding: 10px 18px;
1303 border-radius: 10px;
1304 cursor: pointer;
1305 font-size: 14px;
1306 font-weight: 600;
1307 transition: all 0.3s;
1308 box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
1309 display: flex;
1310 align-items: center;
1311 gap: 6px;">
1312 <span>✍️</span>
1313 <span>Use Now</span>
1314 </button>
1315 </div>
1316 </div>
1317 `;
1318 }
1319
1320 // Function to setup tabs
1321 function setupTabs(modalContent, suggestions) {
1322 const tabButtons = modalContent.querySelectorAll('.tab-btn');
1323 const tabContents = modalContent.querySelectorAll('.tab-content');
1324
1325 tabButtons.forEach(btn => {
1326 btn.addEventListener('click', async () => {
1327 const tabName = btn.getAttribute('data-tab');
1328
1329 // Update active tab button with smooth transitions
1330 tabButtons.forEach(b => {
1331 b.classList.remove('active');
1332 b.style.background = 'rgba(255, 255, 255, 0.2)';
1333 b.style.color = 'white';
1334 b.style.boxShadow = 'none';
1335 });
1336 btn.classList.add('active');
1337 btn.style.background = 'rgba(255, 255, 255, 0.95)';
1338 btn.style.color = '#667eea';
1339 btn.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
1340
1341 // Show corresponding tab content with fade effect
1342 tabContents.forEach(content => {
1343 content.style.display = 'none';
1344 content.style.opacity = '0';
1345 });
1346 const activeTab = modalContent.querySelector(`#tab-${tabName}`);
1347 if (activeTab) {
1348 activeTab.style.display = 'block';
1349 setTimeout(() => {
1350 activeTab.style.transition = 'opacity 0.3s ease-in-out';
1351 activeTab.style.opacity = '1';
1352 }, 10);
1353 }
1354
1355 // Load content for specific tabs
1356 if (tabName === 'saved') {
1357 await loadSavedIdeas(modalContent);
1358 } else if (tabName === 'analytics') {
1359 await loadAnalytics(modalContent);
1360 } else if (tabName === 'customize') {
1361 await loadCustomizeOptions(modalContent, suggestions);
1362 }
1363 });
1364 });
1365 }
1366
1367 // Function to load customize options
1368 async function loadCustomizeOptions(modalContent, suggestions) {
1369 const container = modalContent.querySelector('#customize-container');
1370
1371 // Get current preferences
1372 const preferences = await GM.getValue('postPreferences', null);
1373 const currentPrefs = preferences ? JSON.parse(preferences) : {
1374 tone: 'conversational',
1375 length: 'medium',
1376 includeEmojis: true,
1377 includeHashtags: true,
1378 focusAreas: []
1379 };
1380
1381 let html = `
1382 <div style="max-width: 600px; margin: 0 auto;">
1383 <h3 style="margin: 0 0 24px 0; font-size: 20px; font-weight: 600; color: #000;">Customize Your Post Style</h3>
1384
1385 <div style="margin-bottom: 24px;">
1386 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #000;">Preferred Tone</label>
1387 <select id="tone-preference" style="width: 100%; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px;">
1388 <option value="conversational" ${currentPrefs.tone === 'conversational' ? 'selected' : ''}>Conversational & Friendly</option>
1389 <option value="professional" ${currentPrefs.tone === 'professional' ? 'selected' : ''}>Professional & Formal</option>
1390 <option value="inspirational" ${currentPrefs.tone === 'inspirational' ? 'selected' : ''}>Inspirational & Motivational</option>
1391 <option value="educational" ${currentPrefs.tone === 'educational' ? 'selected' : ''}>Educational & Informative</option>
1392 <option value="storytelling" ${currentPrefs.tone === 'storytelling' ? 'selected' : ''}>Storytelling & Personal</option>
1393 </select>
1394 </div>
1395
1396 <div style="margin-bottom: 24px;">
1397 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #000;">Post Length</label>
1398 <select id="length-preference" style="width: 100%; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px;">
1399 <option value="short" ${currentPrefs.length === 'short' ? 'selected' : ''}>Short (1-2 paragraphs)</option>
1400 <option value="medium" ${currentPrefs.length === 'medium' ? 'selected' : ''}>Medium (3-4 paragraphs)</option>
1401 <option value="long" ${currentPrefs.length === 'long' ? 'selected' : ''}>Long (5+ paragraphs)</option>
1402 </select>
1403 </div>
1404
1405 <div style="margin-bottom: 24px;">
1406 <div id="emoji-preference" data-checked="${currentPrefs.includeEmojis}" style="display: flex; align-items: center; cursor: pointer; user-select: none;">
1407 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.includeEmojis ? '#667eea' : 'white'}; transition: all 0.2s;">
1408 ${currentPrefs.includeEmojis ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1409 </div>
1410 <span style="font-weight: 600; color: #000;">Include Emojis</span>
1411 </div>
1412 </div>
1413
1414 <div style="margin-bottom: 24px;">
1415 <div id="hashtag-preference" data-checked="${currentPrefs.includeHashtags}" style="display: flex; align-items: center; cursor: pointer; user-select: none;">
1416 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.includeHashtags ? '#667eea' : 'white'}; transition: all 0.2s;">
1417 ${currentPrefs.includeHashtags ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1418 </div>
1419 <span style="font-weight: 600; color: #000;">Include Hashtags</span>
1420 </div>
1421 </div>
1422
1423 <div style="margin-bottom: 24px;">
1424 <label style="display: block; margin-bottom: 12px; font-weight: 600; color: #000;">Focus Areas (Select multiple)</label>
1425 <div style="display: flex; flex-direction: column; gap: 12px;">
1426 <div class="focus-area-item" data-value="leadership" data-checked="${currentPrefs.focusAreas.includes('leadership')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('leadership') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('leadership') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1427 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('leadership') ? '#667eea' : 'white'}; transition: all 0.2s;">
1428 ${currentPrefs.focusAreas.includes('leadership') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1429 </div>
1430 <span style="font-size: 15px; color: #1f2937;">Leadership & Management</span>
1431 </div>
1432 <div class="focus-area-item" data-value="career" data-checked="${currentPrefs.focusAreas.includes('career')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('career') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('career') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1433 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('career') ? '#667eea' : 'white'}; transition: all 0.2s;">
1434 ${currentPrefs.focusAreas.includes('career') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1435 </div>
1436 <span style="font-size: 15px; color: #1f2937;">Career Development</span>
1437 </div>
1438 <div class="focus-area-item" data-value="technology" data-checked="${currentPrefs.focusAreas.includes('technology')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('technology') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('technology') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1439 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('technology') ? '#667eea' : 'white'}; transition: all 0.2s;">
1440 ${currentPrefs.focusAreas.includes('technology') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1441 </div>
1442 <span style="font-size: 15px; color: #1f2937;">Technology & Innovation</span>
1443 </div>
1444 <div class="focus-area-item" data-value="entrepreneurship" data-checked="${currentPrefs.focusAreas.includes('entrepreneurship')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('entrepreneurship') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('entrepreneurship') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1445 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('entrepreneurship') ? '#667eea' : 'white'}; transition: all 0.2s;">
1446 ${currentPrefs.focusAreas.includes('entrepreneurship') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1447 </div>
1448 <span style="font-size: 15px; color: #1f2937;">Entrepreneurship & Startups</span>
1449 </div>
1450 <div class="focus-area-item" data-value="marketing" data-checked="${currentPrefs.focusAreas.includes('marketing')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('marketing') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('marketing') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1451 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('marketing') ? '#667eea' : 'white'}; transition: all 0.2s;">
1452 ${currentPrefs.focusAreas.includes('marketing') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1453 </div>
1454 <span style="font-size: 15px; color: #1f2937;">Marketing & Sales</span>
1455 </div>
1456 <div class="focus-area-item" data-value="productivity" data-checked="${currentPrefs.focusAreas.includes('productivity')}" style="display: flex; align-items: center; cursor: pointer; padding: 10px; background: ${currentPrefs.focusAreas.includes('productivity') ? '#f0f4ff' : '#f9fafb'}; border-radius: 8px; border: 2px solid ${currentPrefs.focusAreas.includes('productivity') ? '#667eea' : '#e5e7eb'}; transition: all 0.2s; user-select: none;">
1457 <div class="custom-checkbox" style="width: 20px; height: 20px; border: 2px solid #667eea; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: ${currentPrefs.focusAreas.includes('productivity') ? '#667eea' : 'white'}; transition: all 0.2s;">
1458 ${currentPrefs.focusAreas.includes('productivity') ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : ''}
1459 </div>
1460 <span style="font-size: 15px; color: #1f2937;">Productivity & Work-Life Balance</span>
1461 </div>
1462 </div>
1463 </div>
1464
1465 <button id="save-preferences-btn" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 14px 28px; border-radius: 12px; cursor: pointer; font-size: 16px; font-weight: 700; width: 100%; transition: all 0.3s; box-shadow: 0 4px 6px rgba(102, 126, 234, 0.3);">
1466 Save Preferences
1467 </button>
1468
1469 <div style="margin-top: 24px; padding: 16px; background: #f0f9ff; border-radius: 8px; border-left: 4px solid #0a66c2;">
1470 <p style="margin: 0; color: #0c4a6e; font-size: 14px; line-height: 1.6;">
1471 💡 <strong>Tip:</strong> Your preferences will be used to generate more personalized post ideas that match your style and interests.
1472 </p>
1473 </div>
1474 </div>
1475 `;
1476
1477 container.innerHTML = html;
1478
1479 // Add event listener for save button
1480 container.querySelector('#save-preferences-btn').addEventListener('click', async () => {
1481 const preferences = {
1482 tone: container.querySelector('#tone-preference').value,
1483 length: container.querySelector('#length-preference').value,
1484 includeEmojis: container.querySelector('#emoji-preference').getAttribute('data-checked') === 'true',
1485 includeHashtags: container.querySelector('#hashtag-preference').getAttribute('data-checked') === 'true',
1486 focusAreas: Array.from(container.querySelectorAll('.focus-area-item[data-checked="true"]')).map(item => item.getAttribute('data-value'))
1487 };
1488
1489 await GM.setValue('postPreferences', JSON.stringify(preferences));
1490 showNotification('Preferences saved successfully!', 'success');
1491
1492 const btn = container.querySelector('#save-preferences-btn');
1493 btn.textContent = '✓ Saved!';
1494 setTimeout(() => {
1495 btn.textContent = 'Save Preferences';
1496 }, 2000);
1497 });
1498
1499 // Add direct click handlers to checkboxes to ensure they work properly
1500 const emojiCheckbox = container.querySelector('#emoji-preference');
1501 const hashtagCheckbox = container.querySelector('#hashtag-preference');
1502
1503 // Handle emoji checkbox clicks
1504 if (emojiCheckbox) {
1505 emojiCheckbox.addEventListener('click', () => {
1506 const currentState = emojiCheckbox.getAttribute('data-checked') === 'true';
1507 const newState = !currentState;
1508 emojiCheckbox.setAttribute('data-checked', newState);
1509
1510 // Update visual state
1511 const checkbox = emojiCheckbox.querySelector('.custom-checkbox');
1512 checkbox.style.background = newState ? '#667eea' : 'white';
1513 checkbox.innerHTML = newState ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : '';
1514
1515 console.log('Emoji checkbox toggled:', newState);
1516 });
1517 }
1518
1519 // Handle hashtag checkbox clicks
1520 if (hashtagCheckbox) {
1521 hashtagCheckbox.addEventListener('click', () => {
1522 const currentState = hashtagCheckbox.getAttribute('data-checked') === 'true';
1523 const newState = !currentState;
1524 hashtagCheckbox.setAttribute('data-checked', newState);
1525
1526 // Update visual state
1527 const checkbox = hashtagCheckbox.querySelector('.custom-checkbox');
1528 checkbox.style.background = newState ? '#667eea' : 'white';
1529 checkbox.innerHTML = newState ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : '';
1530
1531 console.log('Hashtag checkbox toggled:', newState);
1532 });
1533 }
1534
1535 // Handle focus area checkboxes
1536 container.querySelectorAll('.focus-area-item').forEach(item => {
1537 item.addEventListener('click', () => {
1538 const currentState = item.getAttribute('data-checked') === 'true';
1539 const newState = !currentState;
1540 item.setAttribute('data-checked', newState);
1541
1542 // Update visual state
1543 const checkbox = item.querySelector('.custom-checkbox');
1544 checkbox.style.background = newState ? '#667eea' : 'white';
1545 checkbox.innerHTML = newState ? '<span style="color: white; font-size: 14px; font-weight: bold;">✓</span>' : '';
1546
1547 // Update item styling
1548 item.style.background = newState ? '#f0f4ff' : '#f9fafb';
1549 item.style.borderColor = newState ? '#667eea' : '#e5e7eb';
1550
1551 console.log('Focus area toggled:', item.getAttribute('data-value'), newState);
1552 });
1553 });
1554 }
1555
1556 // Function to load saved ideas
1557 async function loadSavedIdeas(modalContent) {
1558 const container = modalContent.querySelector('#saved-ideas-container');
1559 const savedIdeas = await getSavedIdeas();
1560
1561 if (savedIdeas.length === 0) {
1562 container.innerHTML = '<p style="color: #666; text-align: center; padding: 40px;">No saved ideas yet. Save ideas from the Post Ideas tab!</p>';
1563 return;
1564 }
1565
1566 let html = '<div style="display: flex; flex-direction: column; gap: 16px;">';
1567 savedIdeas.forEach((idea, index) => {
1568 const savedDate = new Date(idea.savedAt).toLocaleDateString();
1569 html += `
1570 <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px;">
1571 <div style="display: flex; justify-content: between; align-items: start; margin-bottom: 8px;">
1572 <h4 style="margin: 0; color: #0a66c2; flex: 1;">${idea.topic}</h4>
1573 <span style="font-size: 12px; color: #666;">Saved: ${savedDate}</span>
1574 </div>
1575 <p style="margin: 8px 0;">${idea.hook}</p>
1576 <button class="copy-saved-btn" data-index="${index}" style="background: #0a66c2; color: white; border: none; padding: 8px 14px; border-radius: 12px; cursor: pointer; font-size: 13px; font-weight: 600; margin-top: 8px;">
1577 Copy Post
1578 </button>
1579 </div>
1580 `;
1581 });
1582 html += '</div>';
1583
1584 container.innerHTML = html;
1585
1586 // Add event listeners for copy buttons
1587 container.querySelectorAll('.copy-saved-btn').forEach(btn => {
1588 btn.addEventListener('click', async () => {
1589 const index = parseInt(btn.getAttribute('data-index'));
1590 const idea = savedIdeas[index];
1591 await copyIdeaToClipboard(idea);
1592 btn.textContent = '✓ Copied!';
1593 setTimeout(() => btn.textContent = 'Copy Post', 2000);
1594 });
1595 });
1596 }
1597
1598 // Function to load analytics
1599 async function loadAnalytics(modalContent) {
1600 const container = modalContent.querySelector('#analytics-container');
1601 const trendingHistory = await getTrendingHistory();
1602 const usedIdeas = await getUsedIdeas();
1603
1604 let html = '<div style="display: flex; flex-direction: column; gap: 24px;">';
1605
1606 // Trending topics over time
1607 html += `
1608 <div style="padding: 20px; background: #f9f9f9; border-radius: 8px;">
1609 <h3 style="margin: 0 0 16px 0; color: #000;">📈 Trending Topics Over Time</h3>
1610 ${generateTrendingChart(trendingHistory)}
1611 </div>
1612 `;
1613
1614 // Usage statistics
1615 html += `
1616 <div style="padding: 20px; background: #f9f9f9; border-radius: 8px;">
1617 <h3 style="margin: 0 0 16px 0; color: #000;">📊 Your Activity</h3>
1618 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px;">
1619 <div style="text-align: center; padding: 16px; background: white; border-radius: 8px;">
1620 <div style="font-size: 32px; font-weight: 700; color: #0a66c2;">${usedIdeas.length}</div>
1621 <div style="font-size: 14px; color: #666; margin-top: 4px;">Ideas Used</div>
1622 </div>
1623 <div style="text-align: center; padding: 16px; background: white; border-radius: 8px;">
1624 <div style="font-size: 32px; font-weight: 700; color: #057642;">${(await getSavedIdeas()).length}</div>
1625 <div style="font-size: 14px; color: #666; margin-top: 4px;">Ideas Saved</div>
1626 </div>
1627 <div style="text-align: center; padding: 16px; background: white; border-radius: 8px;">
1628 <div style="font-size: 32px; font-weight: 700; color: #6c5ce7;">${trendingHistory.length}</div>
1629 <div style="font-size: 14px; color: #666; margin-top: 4px;">Analyses Run</div>
1630 </div>
1631 </div>
1632 </div>
1633 `;
1634
1635 // Recent activity
1636 if (usedIdeas.length > 0) {
1637 html += `
1638 <div style="padding: 20px; background: #f9f9f9; border-radius: 8px;">
1639 <h3 style="margin: 0 0 16px 0; color: #000;">🕒 Recently Used Ideas</h3>
1640 <div style="display: flex; flex-direction: column; gap: 12px;">
1641 ${usedIdeas.slice(0, 5).map(idea => {
1642 const usedDate = new Date(idea.usedAt).toLocaleDateString();
1643 return `
1644 <div style="padding: 12px; background: white; border-radius: 6px; border-left: 3px solid #0a66c2;">
1645 <div style="font-weight: 600; color: #000; margin-bottom: 4px;">${idea.topic}</div>
1646 <div style="font-size: 13px; color: #666;">Used on ${usedDate}</div>
1647 </div>
1648 `;
1649 }).join('')}
1650 </div>
1651 </div>
1652 `;
1653 }
1654
1655 html += '</div>';
1656 container.innerHTML = html;
1657 }
1658
1659 // Function to generate trending chart visualization
1660 function generateTrendingChart(history) {
1661 if (history.length === 0) {
1662 return '<p style="color: #666; text-align: center;">No trending data yet. Generate more post ideas to see trends!</p>';
1663 }
1664
1665 // Get all unique topics
1666 const topicCounts = {};
1667 history.forEach(entry => {
1668 entry.topics.forEach(topic => {
1669 topicCounts[topic] = (topicCounts[topic] || 0) + 1;
1670 });
1671 });
1672
1673 // Sort by frequency
1674 const sortedTopics = Object.entries(topicCounts)
1675 .sort((a, b) => b[1] - a[1])
1676 .slice(0, 10);
1677
1678 if (sortedTopics.length === 0) {
1679 return '<p style="color: #666; text-align: center;">No trending topics found.</p>';
1680 }
1681
1682 const maxCount = sortedTopics[0][1];
1683
1684 let html = '<div style="display: flex; flex-direction: column; gap: 12px;">';
1685 sortedTopics.forEach(([topic, count]) => {
1686 const percentage = (count / maxCount) * 100;
1687 html += `
1688 <div>
1689 <div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
1690 <span style="font-size: 14px; color: #333; font-weight: 500;">${topic}</span>
1691 <span style="font-size: 13px; color: #666;">${count} times</span>
1692 </div>
1693 <div style="width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden;">
1694 <div style="width: ${percentage}%; height: 100%; background: linear-gradient(90deg, #0a66c2, #0052a3); border-radius: 4px; transition: width 0.3s;"></div>
1695 </div>
1696 </div>
1697 `;
1698 });
1699 html += '</div>';
1700
1701 return html;
1702 }
1703
1704 // Function to copy idea to clipboard
1705 async function copyIdeaToClipboard(idea) {
1706 const postText = `${idea.hook}
1707
1708${idea.keyPoints.map((point, i) => `${i + 1}. ${point}`).join('\n')}
1709
1710${idea.callToAction}
1711
1712${idea.hashtags ? idea.hashtags.map(tag => `#${tag.replace('#', '')}`).join(' ') : ''}`;
1713
1714 await GM.setClipboard(postText);
1715 }
1716
1717 // Function to setup modal event listeners
1718 function setupModalEventListeners(modal, modalContent, suggestions) {
1719 // Close button
1720 modalContent.querySelector('#close-suggestions-modal').addEventListener('click', () => {
1721 modal.remove();
1722 });
1723
1724 // Close on overlay click
1725 modal.addEventListener('click', (e) => {
1726 if (e.target === modal) {
1727 modal.remove();
1728 }
1729 });
1730
1731 // Filter functionality
1732 const categoryFilter = modalContent.querySelector('#category-filter');
1733 const engagementFilter = modalContent.querySelector('#engagement-filter');
1734
1735 if (categoryFilter && engagementFilter) {
1736 const applyFilters = () => {
1737 const category = categoryFilter.value;
1738 const engagement = engagementFilter.value;
1739
1740 const cards = modalContent.querySelectorAll('.post-idea-card');
1741 cards.forEach(card => {
1742 const cardCategory = card.getAttribute('data-category');
1743 const cardEngagement = parseInt(card.getAttribute('data-engagement'));
1744
1745 let showCard = true;
1746
1747 if (category !== 'all' && cardCategory !== category) {
1748 showCard = false;
1749 }
1750
1751 if (engagement === 'high' && cardEngagement < 80) {
1752 showCard = false;
1753 } else if (engagement === 'medium' && (cardEngagement < 50 || cardEngagement >= 80)) {
1754 showCard = false;
1755 } else if (engagement === 'low' && cardEngagement >= 50) {
1756 showCard = false;
1757 }
1758
1759 card.style.display = showCard ? 'block' : 'none';
1760 });
1761 };
1762
1763 categoryFilter.addEventListener('change', applyFilters);
1764 engagementFilter.addEventListener('change', applyFilters);
1765 }
1766
1767 // Copy buttons
1768 modalContent.querySelectorAll('.copy-idea-btn').forEach(btn => {
1769 btn.addEventListener('click', async (e) => {
1770 e.stopPropagation();
1771 const index = parseInt(btn.getAttribute('data-index'));
1772 const idea = suggestions.postIdeas[index];
1773
1774 await copyIdeaToClipboard(idea);
1775
1776 btn.textContent = '✓ Copied!';
1777 btn.style.background = '#057642';
1778 setTimeout(() => {
1779 btn.textContent = '📋 Copy Post';
1780 btn.style.background = '#0a66c2';
1781 }, 2000);
1782 });
1783 });
1784
1785 // Save buttons
1786 modalContent.querySelectorAll('.save-idea-btn').forEach(btn => {
1787 btn.addEventListener('click', async (e) => {
1788 e.stopPropagation();
1789 const index = parseInt(btn.getAttribute('data-index'));
1790 const idea = suggestions.postIdeas[index];
1791
1792 await savePostIdea(idea);
1793
1794 btn.textContent = '✓ Saved!';
1795 setTimeout(() => {
1796 btn.textContent = '💾 Save for Later';
1797 }, 2000);
1798
1799 showNotification('Idea saved successfully!', 'success');
1800 });
1801 });
1802
1803 // Variations buttons
1804 modalContent.querySelectorAll('.variations-btn').forEach(btn => {
1805 btn.addEventListener('click', async (e) => {
1806 e.stopPropagation();
1807
1808 // Check if user is premium
1809 const isPremium = await isPremiumUser();
1810 if (!isPremium) {
1811 showPremiumLock('Generate Variations');
1812 return;
1813 }
1814
1815 const index = parseInt(btn.getAttribute('data-index'));
1816 const idea = suggestions.postIdeas[index];
1817
1818 btn.textContent = 'Generating...';
1819 btn.disabled = true;
1820
1821 try {
1822 const variations = await generateVariations(idea);
1823 showVariationsModal(variations, idea);
1824 btn.textContent = '🔄 Generate Variations';
1825 btn.disabled = false;
1826 } catch (error) {
1827 console.error('Error generating variations:', error);
1828 showNotification('Failed to generate variations', 'error');
1829 btn.textContent = '🔄 Generate Variations';
1830 btn.disabled = false;
1831 }
1832 });
1833 });
1834
1835 // Article outline buttons
1836 modalContent.querySelectorAll('.article-btn').forEach(btn => {
1837 btn.addEventListener('click', async (e) => {
1838 e.stopPropagation();
1839
1840 // Check if user is premium
1841 const isPremium = await isPremiumUser();
1842 if (!isPremium) {
1843 showPremiumLock('Article Outline Generator');
1844 return;
1845 }
1846
1847 const index = parseInt(btn.getAttribute('data-index'));
1848 const idea = suggestions.postIdeas[index];
1849
1850 btn.textContent = 'Generating...';
1851 btn.disabled = true;
1852
1853 try {
1854 const outline = await generateArticleOutline(idea);
1855 showArticleOutlineModal(outline);
1856 btn.textContent = '📝 Article Outline';
1857 btn.disabled = false;
1858 } catch (error) {
1859 console.error('Error generating outline:', error);
1860 showNotification('Failed to generate article outline', 'error');
1861 btn.textContent = '📝 Article Outline';
1862 btn.disabled = false;
1863 }
1864 });
1865 });
1866
1867 // Use idea buttons
1868 modalContent.querySelectorAll('.use-idea-btn').forEach(btn => {
1869 btn.addEventListener('click', async (e) => {
1870 e.stopPropagation();
1871 const index = parseInt(btn.getAttribute('data-index'));
1872 const idea = suggestions.postIdeas[index];
1873
1874 await markIdeaAsUsed(idea);
1875 await copyIdeaToClipboard(idea);
1876
1877 showNotification('Post copied! Click "Start a post" on LinkedIn to paste it.', 'success');
1878
1879 // Try to open the LinkedIn post composer
1880 const startPostBtn = document.querySelector('button.share-box-feed-entry__trigger');
1881 if (startPostBtn) {
1882 startPostBtn.click();
1883 }
1884
1885 modal.remove();
1886 });
1887 });
1888
1889 // Hover effects
1890 modalContent.querySelectorAll('.post-idea-card').forEach(card => {
1891 card.addEventListener('mouseenter', () => {
1892 card.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)';
1893 });
1894 card.addEventListener('mouseleave', () => {
1895 card.style.boxShadow = 'none';
1896 });
1897 });
1898 }
1899
1900 // Function to show variations modal
1901 function showVariationsModal(variations, originalIdea) {
1902 const modal = document.createElement('div');
1903 modal.style.cssText = `
1904 position: fixed;
1905 top: 0;
1906 left: 0;
1907 width: 100%;
1908 height: 100%;
1909 background: rgba(0, 0, 0, 0.6);
1910 display: flex;
1911 justify-content: center;
1912 align-items: center;
1913 z-index: 10001;
1914 animation: fadeIn 0.3s ease-in-out;
1915 `;
1916
1917 const content = document.createElement('div');
1918 content.style.cssText = `
1919 background: white;
1920 border-radius: 8px;
1921 max-width: 700px;
1922 max-height: 80vh;
1923 overflow-y: auto;
1924 padding: 24px;
1925 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1926 `;
1927
1928 let html = `
1929 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
1930 <h2 style="margin: 0; font-size: 24px; font-weight: 600; color: #000;">Post Variations</h2>
1931 <button class="close-variations-modal" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #666;">×</button>
1932 </div>
1933 <p style="color: #666; margin-bottom: 24px;">Different versions of: <strong>${originalIdea.topic}</strong></p>
1934 <div style="display: flex; flex-direction: column; gap: 20px;">
1935 `;
1936
1937 variations.forEach((variation, index) => {
1938 html += `
1939 <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px;">
1940 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
1941 <h3 style="margin: 0; color: #0a66c2; font-size: 16px;">${variation.tone}</h3>
1942 <button class="copy-variation-btn" data-index="${index}" style="background: #0a66c2; color: white; border: none; padding: 8px 14px; border-radius: 12px; cursor: pointer; font-size: 13px; font-weight: 600;">
1943 Copy
1944 </button>
1945 </div>
1946 <p style="margin: 0;
1947 color: #333;
1948 line-height: 1.6;
1949 white-space: pre-wrap;">${variation.postText}</p>
1950 </div>
1951 `;
1952 });
1953
1954 html += '</div>';
1955 content.innerHTML = html;
1956 modal.appendChild(content);
1957 document.body.appendChild(modal);
1958
1959 // Event listeners
1960 content.querySelector('.close-variations-modal').addEventListener('click', () => modal.remove());
1961 modal.addEventListener('click', (e) => {
1962 if (e.target === modal) modal.remove();
1963 });
1964
1965 content.querySelectorAll('.copy-variation-btn').forEach(btn => {
1966 btn.addEventListener('click', async () => {
1967 const index = parseInt(btn.getAttribute('data-index'));
1968 await GM.setClipboard(variations[index].postText);
1969 btn.textContent = 'Copied!';
1970 setTimeout(() => btn.textContent = 'Copy', 2000);
1971 });
1972 });
1973 }
1974
1975 // Function to show article outline modal
1976 function showArticleOutlineModal(outline) {
1977 const modal = document.createElement('div');
1978 modal.style.cssText = `
1979 position: fixed;
1980 top: 0;
1981 left: 0;
1982 width: 100%;
1983 height: 100%;
1984 background: rgba(0, 0, 0, 0.6);
1985 display: flex;
1986 justify-content: center;
1987 align-items: center;
1988 z-index: 10001;
1989 animation: fadeIn 0.3s ease-in-out;
1990 `;
1991
1992 const content = document.createElement('div');
1993 content.style.cssText = `
1994 background: white;
1995 border-radius: 8px;
1996 max-width: 700px;
1997 max-height: 80vh;
1998 overflow-y: auto;
1999 padding: 24px;
2000 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2001 `;
2002
2003 let html = `
2004 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
2005 <h2 style="margin: 0; font-size: 24px; font-weight: 600; color: #000;">Article Outline</h2>
2006 <button class="close-outline-modal" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #666;">×</button>
2007 </div>
2008
2009 <div style="margin-bottom: 24px;">
2010 <h3 style="color: #0a66c2; font-size: 20px; margin-bottom: 12px;">${outline.title}</h3>
2011 <button class="copy-outline-btn" style="background: #0a66c2; color: white; border: none; padding: 10px 16px; border-radius: 16px; cursor: pointer; font-size: 14px; font-weight: 600; margin-bottom: 16px;">
2012 Copy Full Outline
2013 </button>
2014 </div>
2015
2016 <div style="margin-bottom: 20px;">
2017 <h4 style="color: #000; font-size: 16px; margin-bottom: 8px;">Introduction</h4>
2018 <p style="color: #333; line-height: 1.6;">${outline.introduction}</p>
2019 </div>
2020
2021 <div style="margin-bottom: 20px;">
2022 <h4 style="color: #000; font-size: 16px; margin-bottom: 12px;">Main Sections</h4>
2023 ${outline.sections.map((section, index) => `
2024 <div style="margin-bottom: 16px; padding-left: 16px; border-left: 3px solid #0a66c2;">
2025 <h5 style="color: #0a66c2; font-size: 15px; margin-bottom: 8px;">${index + 1}. ${section.heading}</h5>
2026 <ul style="margin: 0; padding-left: 20px; color: #666;">
2027 ${section.subsections.map(sub => `<li style="margin-bottom: 4px;">${sub}</li>`).join('')}
2028 </ul>
2029 </div>
2030 `).join('')}
2031 </div>
2032
2033 <div style="margin-bottom: 20px;">
2034 <h4 style="color: #000; font-size: 16px; margin-bottom: 8px;">Conclusion</h4>
2035 <p style="color: #333; line-height: 1.6;">${outline.conclusion}</p>
2036 </div>
2037
2038 <div>
2039 <h4 style="color: #000; font-size: 16px; margin-bottom: 8px;">Key Takeaways</h4>
2040 <ul style="margin: 0; padding-left: 20px; color: #333;">
2041 ${outline.keyTakeaways.map(takeaway => `<li style="margin-bottom: 4px;">${takeaway}</li>`).join('')}
2042 </ul>
2043 </div>
2044 `;
2045
2046 content.innerHTML = html;
2047 modal.appendChild(content);
2048 document.body.appendChild(modal);
2049
2050 // Event listeners
2051 content.querySelector('.close-outline-modal').addEventListener('click', () => modal.remove());
2052 modal.addEventListener('click', (e) => {
2053 if (e.target === modal) modal.remove();
2054 });
2055
2056 content.querySelector('.copy-outline-btn').addEventListener('click', async () => {
2057 const outlineText = `${outline.title}
2058
2059Introduction:
2060${outline.introduction}
2061
2062${outline.sections.map((section, index) => `
2063${index + 1}. ${section.heading}
2064${section.subsections.map(sub => ` - ${sub}`).join('\n')}
2065`).join('\n')}
2066
2067Conclusion:
2068${outline.conclusion}
2069
2070Key Takeaways:
2071${outline.keyTakeaways.map(takeaway => `- ${takeaway}`).join('\n')}`;
2072
2073 await GM.setClipboard(outlineText);
2074 const btn = content.querySelector('.copy-outline-btn');
2075 btn.textContent = 'Copied!';
2076 setTimeout(() => btn.textContent = 'Copy Full Outline', 2000);
2077 });
2078 }
2079
2080 // Function to show notifications
2081 function showNotification(message, type = 'info') {
2082 const notification = document.createElement('div');
2083 notification.style.cssText = `
2084 position: fixed;
2085 top: 80px;
2086 right: 20px;
2087 background: ${type === 'success' ? '#057642' : type === 'error' ? '#cc1016' : '#0a66c2'};
2088 color: white;
2089 padding: 16px 24px;
2090 border-radius: 8px;
2091 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2092 z-index: 10002;
2093 font-size: 14px;
2094 font-weight: 500;
2095 animation: slideInRight 0.3s ease-in-out;
2096 max-width: 400px;
2097 `;
2098 notification.textContent = message;
2099 document.body.appendChild(notification);
2100
2101 setTimeout(() => {
2102 notification.style.animation = 'slideOutRight 0.3s ease-in-out';
2103 setTimeout(() => notification.remove(), 300);
2104 }, 3000);
2105 }
2106
2107 // Add CSS animations
2108 const style = document.createElement('style');
2109 style.textContent = `
2110 @keyframes fadeIn {
2111 from { opacity: 0; }
2112 to { opacity: 1; }
2113 }
2114 @keyframes slideUp {
2115 from { transform: translateY(20px); opacity: 0; }
2116 to { transform: translateY(0); opacity: 1; }
2117 }
2118 @keyframes slideInRight {
2119 from { transform: translateX(100%); opacity: 0; }
2120 to { transform: translateX(0); opacity: 1; }
2121 }
2122 @keyframes slideOutRight {
2123 from { transform: translateX(0); opacity: 1; }
2124 to { transform: translateX(100%); opacity: 0; }
2125 }
2126 @keyframes pulseGlow {
2127 0%, 100% {
2128 box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
2129 }
2130 50% {
2131 box-shadow: 0 4px 25px rgba(102, 126, 234, 0.6);
2132 }
2133 }
2134 @keyframes shimmer {
2135 0% {
2136 left: -100%;
2137 }
2138 100% {
2139 left: 100%;
2140 }
2141 }
2142 #close-suggestions-modal:hover {
2143 background: #f3f6f8 !important;
2144 }
2145 .tab-btn:hover {
2146 background: #f3f6f8;
2147 }
2148 `;
2149 document.head.appendChild(style);
2150
2151 // Initialize the extension
2152 function init() {
2153 console.log('Initializing LinkedIn Post Idea Generator v2.0...');
2154
2155 // Wait for the page to load
2156 if (document.readyState === 'loading') {
2157 document.addEventListener('DOMContentLoaded', init);
2158 return;
2159 }
2160
2161 // Check for premium activation from Stripe success URL
2162 checkPremiumActivation();
2163
2164 // Try to create button immediately
2165 createSuggestionsButton();
2166
2167 // Use MutationObserver to handle dynamic content loading
2168 const observer = new MutationObserver(debounce(() => {
2169 createSuggestionsButton();
2170 }, 500));
2171
2172 // Observe the main feed container
2173 const feedContainer = document.querySelector('.scaffold-layout__main');
2174 if (feedContainer) {
2175 observer.observe(feedContainer, {
2176 childList: true,
2177 subtree: true
2178 });
2179 console.log('MutationObserver attached to feed container');
2180 } else {
2181 console.log('Feed container not found, will retry...');
2182 setTimeout(init, 1000);
2183 }
2184 }
2185
2186 // Start the extension
2187 init();
2188
2189})();