Auto-deploy: 2026-05-18 03:06:29
This commit is contained in:
@@ -67,6 +67,15 @@ async function handleGeminiRequest({ apiKey, prompt, tab, action = 'generateText
|
||||
}
|
||||
await incrementUsage();
|
||||
return { comment, fromCache: false };
|
||||
} else if (action === 'repurposePost') {
|
||||
const result = data.result;
|
||||
if (!result) {
|
||||
console.error('[LJA-BG] Empty result. Full response:', JSON.stringify(data).substring(0, 300));
|
||||
lastError = 'Empty result from server.';
|
||||
continue;
|
||||
}
|
||||
await incrementUsage();
|
||||
return { result, fromCache: false };
|
||||
} else {
|
||||
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
|
||||
if (!text) {
|
||||
|
||||
153
post_feed.js
153
post_feed.js
@@ -218,6 +218,133 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Core: call server and repurpose post ───────────────────────────────
|
||||
async function repurposePost(postEl) {
|
||||
const settings = await getSettings();
|
||||
if (!settings || !settings.apiKey) {
|
||||
alert('Please set your Gemini API key in the extension popup first.\n(Debug: Key not found in storage)');
|
||||
return;
|
||||
}
|
||||
|
||||
const postText = extractPostText(postEl);
|
||||
if (!postText) {
|
||||
alert('Could not read post text. The post might be an image or video.');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = postEl.querySelector('.lja-rewrite-btn');
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="lja-spinner"></span> Rewriting...';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'GEMINI_REQUEST',
|
||||
payload: {
|
||||
apiKey: settings.apiKey,
|
||||
action: 'repurposePost',
|
||||
postText: postText
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
throw new Error(response.error || 'Unknown error');
|
||||
}
|
||||
|
||||
const resultText = response.data.result || response.data;
|
||||
|
||||
// We reuse the comment box UI but style it differently, or just use the same box but change the title and actions.
|
||||
createRepurposeBox(postEl, resultText);
|
||||
|
||||
} catch (e) {
|
||||
console.error('[LJA Feed]', e);
|
||||
alert('Post rewriting failed: ' + e.message);
|
||||
} finally {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '📝 Rewrite';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Create the repurpose result box ─────────────────────────────────────
|
||||
function createRepurposeBox(postEl, resultText) {
|
||||
const existing = postEl.querySelector('.' + BOX_CLASS);
|
||||
if (existing) existing.remove();
|
||||
|
||||
const isRTL = /[\u0600-\u06FF]/.test(resultText);
|
||||
|
||||
// Split text into Post and Image Prompt if the separator exists
|
||||
let postContent = resultText;
|
||||
let imagePrompt = '';
|
||||
const parts = resultText.split('--- IMAGE PROMPT ---');
|
||||
if (parts.length > 1) {
|
||||
postContent = parts[0].trim();
|
||||
imagePrompt = parts[1].trim();
|
||||
}
|
||||
|
||||
const box = document.createElement('div');
|
||||
box.className = BOX_CLASS;
|
||||
box.innerHTML = `
|
||||
<div class="lja-cb-header" style="background: linear-gradient(135deg, #FF6B6B 0%, #C92A2A 100%);">
|
||||
<span class="lja-cb-icon">✍️</span>
|
||||
<span class="lja-cb-title">Rewritten Post</span>
|
||||
<button class="lja-cb-close" title="Close">✕</button>
|
||||
</div>
|
||||
<div style="padding: 12px; font-size: 13px; color: #666; font-weight: 500; border-bottom: 1px solid rgba(0,0,0,0.05);">
|
||||
Post Content:
|
||||
</div>
|
||||
<textarea
|
||||
class="lja-cb-text"
|
||||
dir="${isRTL ? 'rtl' : 'ltr'}"
|
||||
style="text-align: ${isRTL ? 'right' : 'left'}; min-height: 120px;"
|
||||
>${postContent}</textarea>
|
||||
${imagePrompt ? `
|
||||
<div style="padding: 12px; font-size: 13px; color: #666; font-weight: 500; border-top: 1px solid rgba(0,0,0,0.05); border-bottom: 1px solid rgba(0,0,0,0.05);">
|
||||
Image Generation Prompt (Midjourney / DALL-E):
|
||||
</div>
|
||||
<textarea
|
||||
class="lja-cb-image-prompt"
|
||||
style="width: 100%; border: none; padding: 12px; background: rgba(0,0,0,0.02); resize: vertical; min-height: 80px; font-family: monospace; font-size: 12px; outline: none;"
|
||||
readonly
|
||||
>${imagePrompt}</textarea>
|
||||
` : ''}
|
||||
<div class="lja-cb-actions">
|
||||
<button class="lja-cb-copy">📋 Copy Post</button>
|
||||
${imagePrompt ? '<button class="lja-cb-copy-img">🎨 Copy Image Prompt</button>' : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
box.querySelector('.lja-cb-close').addEventListener('click', () => box.remove());
|
||||
|
||||
box.querySelector('.lja-cb-copy').addEventListener('click', function () {
|
||||
const text = box.querySelector('.lja-cb-text').value;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.textContent = '✅ Post Copied!';
|
||||
setTimeout(() => { this.textContent = '📋 Copy Post'; }, 1500);
|
||||
});
|
||||
});
|
||||
|
||||
const copyImgBtn = box.querySelector('.lja-cb-copy-img');
|
||||
if (copyImgBtn) {
|
||||
copyImgBtn.addEventListener('click', function () {
|
||||
const text = box.querySelector('.lja-cb-image-prompt').value;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.textContent = '✅ Prompt Copied!';
|
||||
setTimeout(() => { this.textContent = '🎨 Copy Image Prompt'; }, 1500);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const actionBar = findActionBar(postEl);
|
||||
if (actionBar) {
|
||||
actionBar.parentNode.insertBefore(box, actionBar.nextSibling);
|
||||
} else {
|
||||
postEl.appendChild(box);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Inject button into a single post ────────────────────────────────────
|
||||
function injectButton(postEl) {
|
||||
if (postEl.querySelector('.' + BUTTON_CLASS)) return; // Already injected
|
||||
@@ -238,7 +365,33 @@
|
||||
generateComment(postEl);
|
||||
});
|
||||
|
||||
const rewriteBtn = document.createElement('button');
|
||||
rewriteBtn.className = 'lja-rewrite-btn';
|
||||
rewriteBtn.innerHTML = '📝 Rewrite';
|
||||
rewriteBtn.title = 'Rewrite this post in your own style and generate an image prompt';
|
||||
|
||||
// Add some inline styles for the rewrite button to differentiate it
|
||||
rewriteBtn.style.background = 'linear-gradient(135deg, #FF8787 0%, #E03131 100%)';
|
||||
rewriteBtn.style.color = 'white';
|
||||
rewriteBtn.style.border = 'none';
|
||||
rewriteBtn.style.borderRadius = '16px';
|
||||
rewriteBtn.style.padding = '4px 12px';
|
||||
rewriteBtn.style.fontSize = '12px';
|
||||
rewriteBtn.style.fontWeight = '600';
|
||||
rewriteBtn.style.cursor = 'pointer';
|
||||
rewriteBtn.style.marginLeft = '8px';
|
||||
rewriteBtn.style.display = 'flex';
|
||||
rewriteBtn.style.alignItems = 'center';
|
||||
rewriteBtn.style.gap = '4px';
|
||||
|
||||
rewriteBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
repurposePost(postEl);
|
||||
});
|
||||
|
||||
actionBar.appendChild(btn);
|
||||
actionBar.appendChild(rewriteBtn);
|
||||
}
|
||||
|
||||
// ─── Process all posts on the page ───────────────────────────────────────
|
||||
|
||||
@@ -215,3 +215,58 @@ if ($action === 'generateComment') {
|
||||
echo json_encode(["success" => true, "comment" => $commentText]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// ACTION 4: Repurpose Post Generator
|
||||
// ==========================================
|
||||
if ($action === 'repurposePost') {
|
||||
$postText = substr($data['postText'] ?? '', 0, 3000);
|
||||
|
||||
if (empty($postText)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(["error" => "postText is required."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$promptFile = __DIR__ . '/prompts/repurpose_prompt.txt';
|
||||
if (!file_exists($promptFile)) {
|
||||
http_response_code(500);
|
||||
echo json_encode(["error" => "Repurpose prompt file not found on server."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$promptTemplate = file_get_contents($promptFile);
|
||||
$prompt = str_replace('{{POST_TEXT}}', $postText, $promptTemplate);
|
||||
|
||||
$payload = json_encode([
|
||||
"contents" => [["parts" => [["text" => $prompt]]]],
|
||||
"generationConfig" => ["temperature" => 0.75, "maxOutputTokens" => 800]
|
||||
]);
|
||||
|
||||
$ch = curl_init($geminiUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
http_response_code(500);
|
||||
echo json_encode(["error" => "Gemini API Error", "details" => json_decode($response)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseData = json_decode($response, true);
|
||||
$resultText = trim($responseData['candidates'][0]['content']['parts'][0]['text'] ?? '');
|
||||
|
||||
if (empty($resultText)) {
|
||||
http_response_code(500);
|
||||
echo json_encode(["error" => "Empty result from AI."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(["success" => true, "result" => $resultText]);
|
||||
exit;
|
||||
}
|
||||
|
||||
21
server/prompts/repurpose_prompt.txt
Normal file
21
server/prompts/repurpose_prompt.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
You are Hamza Ayed, a Senior Solutions Architect and technical leader.
|
||||
Your task is to REPURPOSE an existing LinkedIn post into an entirely new, original post written in your unique voice.
|
||||
|
||||
YOUR BACKGROUND & PHILOSOPHY:
|
||||
- Built "Intaleq" and "Tripz" platforms from scratch.
|
||||
- Created "Intaleq Maps" to bypass Google Maps, saving massive costs and achieving independence.
|
||||
- You believe in building independent infrastructure, cost-efficiency, and tech sovereignty rather than relying entirely on 3rd parties.
|
||||
|
||||
STRICT RULES:
|
||||
1. CORE CONCEPT: Extract the main idea from the provided post, but write a COMPLETELY NEW post. Do not just summarize. Reframe it from your Architect/Leadership perspective.
|
||||
2. LANGUAGE: Match the language of the original post. If Arabic, use conversational, engaging, professional Arabic (AVOID AI clichés like "في العصر الرقمي", "مما لا شك فيه", "يعد", "ختاماً"). Write naturally like a seasoned expert.
|
||||
3. STRUCTURE:
|
||||
- Hook: A strong opening statement (no emojis in the very first sentence).
|
||||
- Body: Your perspective on the topic. Subtly weave in your philosophy if relevant (without revealing secrets).
|
||||
- Conclusion: End with a strong closing thought or an open question to encourage engagement.
|
||||
4. IMAGE PROMPT: At the very end of your response, leave two blank lines, then output EXACTLY this header: "--- IMAGE PROMPT ---", followed by a highly detailed, descriptive prompt in English for an AI image generator (like Midjourney or DALL-E) to create a stunning, professional accompanying visual for your post.
|
||||
5. NO EMOJIS in the image prompt section. You may use a few appropriate emojis in the main post text.
|
||||
6. OUTPUT FORMAT: Return the new post text, then the image prompt section. Nothing else.
|
||||
|
||||
POST TO REPURPOSE:
|
||||
{{POST_TEXT}}
|
||||
Reference in New Issue
Block a user