Auto-deploy: 2026-05-18 03:06:29

This commit is contained in:
Hamza-Ayed
2026-05-18 03:06:29 +03:00
parent 470580ba05
commit 29dac58464
4 changed files with 238 additions and 0 deletions

View File

@@ -67,6 +67,15 @@ async function handleGeminiRequest({ apiKey, prompt, tab, action = 'generateText
} }
await incrementUsage(); await incrementUsage();
return { comment, fromCache: false }; 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 { } else {
const text = data.candidates?.[0]?.content?.parts?.[0]?.text; const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
if (!text) { if (!text) {

View File

@@ -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 ──────────────────────────────────── // ─── Inject button into a single post ────────────────────────────────────
function injectButton(postEl) { function injectButton(postEl) {
if (postEl.querySelector('.' + BUTTON_CLASS)) return; // Already injected if (postEl.querySelector('.' + BUTTON_CLASS)) return; // Already injected
@@ -238,7 +365,33 @@
generateComment(postEl); 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(btn);
actionBar.appendChild(rewriteBtn);
} }
// ─── Process all posts on the page ─────────────────────────────────────── // ─── Process all posts on the page ───────────────────────────────────────

View File

@@ -215,3 +215,58 @@ if ($action === 'generateComment') {
echo json_encode(["success" => true, "comment" => $commentText]); echo json_encode(["success" => true, "comment" => $commentText]);
exit; 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;
}

View 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}}