improve ui

This commit is contained in:
f 2025-02-05 02:54:53 +03:00
parent 7c68845eb0
commit ffcfd9c074
9 changed files with 3017 additions and 1307 deletions

View File

@ -1,15 +1,20 @@
# Add New Prompt # Add New Prompt
You'll need to add your prompt into README.md, and to the `prompts.csv` file. If your prompt includes quotes, you will need to double-quote them to escape in CSV file. You'll need to add your prompt into README.md, and to the `prompts.csv` file. If
your prompt includes quotes, you will need to double-quote them to escape in CSV
file.
If the prompt is generated by AI, please add `<mark>Generated by AI</mark>` to the end of the contribution line. If the prompt is generated by AI, please add `<mark>Generated by AI</mark>` to
the end of the contribution line.
- [ ] I've confirmed the prompt works well - [ ] I've confirmed the prompt works well
- [ ] I've added `Contributed by: [@yourusername](https://github.com/yourusername)` - [ ] I've added
`Contributed by: [@yourusername](https://github.com/yourusername)`
- [ ] I've added to the README.md - [ ] I've added to the README.md
- [ ] I've added to the `prompts.csv` - [ ] I've added to the `prompts.csv`
- [ ] Escaped quotes by double-quoting them - [ ] Escaped quotes by double-quoting them
- [ ] No spaces after commas after double quotes. e.g. `"Hello","hi"`, not `"Hello", "hi"` - [ ] No spaces after commas after double quotes. e.g. `"Hello","hi"`, not
`"Hello", "hi"`
- [ ] Removed "Act as" from the title on CSV - [ ] Removed "Act as" from the title on CSV
Please make sure you've completed all the checklist. Please make sure you've completed all the checklist.

View File

@ -33,7 +33,7 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: '18' node-version: "18"
- name: Install dependencies - name: Install dependencies
run: npm install openai@^4.0.0 @octokit/rest@^19.0.0 run: npm install openai@^4.0.0 @octokit/rest@^19.0.0
@ -52,7 +52,7 @@ jobs:
const openai = new OpenAI({ const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY apiKey: process.env.OPENAI_API_KEY
}); });
const octokit = new Octokit({ const octokit = new Octokit({
auth: process.env.GH_TOKEN auth: process.env.GH_TOKEN
}); });
@ -60,7 +60,7 @@ jobs:
const eventName = process.env.GITHUB_EVENT_NAME; const eventName = process.env.GITHUB_EVENT_NAME;
const eventPath = process.env.GITHUB_EVENT_PATH; const eventPath = process.env.GITHUB_EVENT_PATH;
const event = require(eventPath); const event = require(eventPath);
// Double check user authorization // Double check user authorization
const actor = event.sender?.login || event.pull_request?.user?.login || event.issue?.user?.login; const actor = event.sender?.login || event.pull_request?.user?.login || event.issue?.user?.login;
if (actor !== 'f') { if (actor !== 'f') {
@ -72,7 +72,7 @@ jobs:
let command = ''; let command = '';
let issueNumber = null; let issueNumber = null;
let isPullRequest = false; let isPullRequest = false;
if (eventName === 'issues') { if (eventName === 'issues') {
command = event.issue.body; command = event.issue.body;
issueNumber = event.issue.number; issueNumber = event.issue.number;
@ -96,7 +96,7 @@ jobs:
// Extract the actual command after /ai // Extract the actual command after /ai
const aiCommand = command.substring(3).trim(); const aiCommand = command.substring(3).trim();
// Handle resolve conflicts command // Handle resolve conflicts command
if (aiCommand === 'resolve' || aiCommand === 'fix conflicts') { if (aiCommand === 'resolve' || aiCommand === 'fix conflicts') {
if (!isPullRequest) { if (!isPullRequest) {
@ -112,7 +112,7 @@ jobs:
try { try {
console.log('Starting resolve command execution...'); console.log('Starting resolve command execution...');
// Get PR details // Get PR details
console.log('Fetching PR details...'); console.log('Fetching PR details...');
const { data: pr } = await octokit.pulls.get({ const { data: pr } = await octokit.pulls.get({
@ -140,7 +140,7 @@ jobs:
title = title.trim(); title = title.trim();
// Remove "Act as" or "Act as a" or "Act as an" from start if present // Remove "Act as" or "Act as a" or "Act as an" from start if present
title = title.replace(/^Act as (?:a |an )?/i, ''); title = title.replace(/^Act as (?:a |an )?/i, '');
// Capitalize each word except common articles and prepositions // Capitalize each word except common articles and prepositions
const lowercaseWords = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'for', 'with', 'in']; const lowercaseWords = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'for', 'with', 'in'];
const capitalized = title.toLowerCase().split(' ').map((word, index) => { const capitalized = title.toLowerCase().split(' ').map((word, index) => {
@ -150,7 +150,7 @@ jobs:
} }
return word; return word;
}).join(' '); }).join(' ');
// Add "Act as" prefix // Add "Act as" prefix
return `Act as ${capitalized}`; return `Act as ${capitalized}`;
}; };
@ -168,17 +168,17 @@ jobs:
console.log('Attempting to extract prompts from README changes...'); console.log('Attempting to extract prompts from README changes...');
const promptMatches = [...addedLines.matchAll(/## (?:Act as (?:a |an )?)?([^\n]+)\n(?:Contributed by:[^\n]*\n)?(?:> )?([^#]+?)(?=\n##|\n\n##|$)/ig)]; const promptMatches = [...addedLines.matchAll(/## (?:Act as (?:a |an )?)?([^\n]+)\n(?:Contributed by:[^\n]*\n)?(?:> )?([^#]+?)(?=\n##|\n\n##|$)/ig)];
for (const match of promptMatches) { for (const match of promptMatches) {
const actName = normalizeTitle(match[1]); const actName = normalizeTitle(match[1]);
const promptText = match[2].trim() const promptText = match[2].trim()
.replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '') .replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '')
.trim(); .trim();
const contributorLine = addedLines.match(/Contributed by: \[@([^\]]+)\]\(https:\/\/github\.com\/([^\)]+)\)/); const contributorLine = addedLines.match(/Contributed by: \[@([^\]]+)\]\(https:\/\/github\.com\/([^\)]+)\)/);
const contributorInfo = contributorLine const contributorInfo = contributorLine
? `Contributed by: [@${contributorLine[1]}](https://github.com/${contributorLine[2]})` ? `Contributed by: [@${contributorLine[1]}](https://github.com/${contributorLine[2]})`
: `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`; : `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`;
prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo }); prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo });
console.log(`Found prompt in README: "${actName}"`); console.log(`Found prompt in README: "${actName}"`);
foundInReadme = true; foundInReadme = true;
@ -206,7 +206,7 @@ jobs:
const promptText = matches[1][1].replace(/""/g, '"').trim() const promptText = matches[1][1].replace(/""/g, '"').trim()
.replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '') .replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '')
.trim(); .trim();
const contributorInfo = `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`; const contributorInfo = `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`;
prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo }); prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo });
console.log(`Found prompt in CSV: "${actName}"`); console.log(`Found prompt in CSV: "${actName}"`);
@ -256,16 +256,16 @@ jobs:
for (const { actName, promptText, contributorInfo } of promptsArray) { for (const { actName, promptText, contributorInfo } of promptsArray) {
// Remove markdown quote character and trim whitespace // Remove markdown quote character and trim whitespace
const cleanPrompt = promptText.replace(/^>\s*/gm, '').trim(); const cleanPrompt = promptText.replace(/^>\s*/gm, '').trim();
// For README: Add quote to each line // For README: Add quote to each line
const readmePrompt = cleanPrompt.split('\n') const readmePrompt = cleanPrompt.split('\n')
.map(line => `> ${line.trim()}`) .map(line => `> ${line.trim()}`)
.join('\n'); .join('\n');
const newSection = `## ${actName}\n${contributorInfo}\n\n${readmePrompt}\n\n`; const newSection = `## ${actName}\n${contributorInfo}\n\n${readmePrompt}\n\n`;
// For CSV: Convert to single paragraph // For CSV: Convert to single paragraph
const csvPrompt = cleanPrompt.replace(/\n+/g, ' ').trim(); const csvPrompt = cleanPrompt.replace(/\n+/g, ' ').trim();
// Insert the new section before Contributors in README // Insert the new section before Contributors in README
const contributorsIndex = readmeContent.indexOf('## Contributors'); const contributorsIndex = readmeContent.indexOf('## Contributors');
if (contributorsIndex === -1) { if (contributorsIndex === -1) {
@ -483,13 +483,13 @@ jobs:
if (file.status === 'removed') { if (file.status === 'removed') {
return `Deleted: ${file.filename}`; return `Deleted: ${file.filename}`;
} }
// Get file content for added or modified files // Get file content for added or modified files
if (file.status === 'added' || file.status === 'modified') { if (file.status === 'added' || file.status === 'modified') {
const patch = file.patch || ''; const patch = file.patch || '';
return `${file.status === 'added' ? 'Added' : 'Modified'}: ${file.filename}\nChanges:\n${patch}`; return `${file.status === 'added' ? 'Added' : 'Modified'}: ${file.filename}\nChanges:\n${patch}`;
} }
return `${file.status}: ${file.filename}`; return `${file.status}: ${file.filename}`;
})); }));
@ -551,7 +551,7 @@ jobs:
// If response contains code changes, create a new branch and PR // If response contains code changes, create a new branch and PR
if (response.includes('```')) { if (response.includes('```')) {
const branchName = `ai-bot/fix-${issueNumber}`; const branchName = `ai-bot/fix-${issueNumber}`;
// Create new branch // Create new branch
const defaultBranch = event.repository.default_branch; const defaultBranch = event.repository.default_branch;
const ref = await octokit.git.getRef({ const ref = await octokit.git.getRef({
@ -559,7 +559,7 @@ jobs:
repo: event.repository.name, repo: event.repository.name,
ref: `heads/${defaultBranch}` ref: `heads/${defaultBranch}`
}); });
await octokit.git.createRef({ await octokit.git.createRef({
owner: event.repository.owner.login, owner: event.repository.owner.login,
repo: event.repository.name, repo: event.repository.name,
@ -572,7 +572,7 @@ jobs:
for (const block of codeBlocks) { for (const block of codeBlocks) {
const [_, filePath, ...codeLines] = block.split('\n'); const [_, filePath, ...codeLines] = block.split('\n');
const content = Buffer.from(codeLines.join('\n')).toString('base64'); const content = Buffer.from(codeLines.join('\n')).toString('base64');
await octokit.repos.createOrUpdateFileContents({ await octokit.repos.createOrUpdateFileContents({
owner: event.repository.owner.login, owner: event.repository.owner.login,
repo: event.repository.name, repo: event.repository.name,
@ -620,4 +620,4 @@ jobs:
repo: context.repo.repo, repo: context.repo.repo,
issue_number: issueNumber, issue_number: issueNumber,
body: '❌ Sorry, there was an error processing your command. Please try again or contact the repository maintainers.' body: '❌ Sorry, there was an error processing your command. Please try again or contact the repository maintainers.'
}); });

View File

@ -19,7 +19,7 @@ jobs:
with: with:
script: | script: |
const pr = context.payload.pull_request; const pr = context.payload.pull_request;
// Check if PR has conflicts // Check if PR has conflicts
if (pr.mergeable === false) { if (pr.mergeable === false) {
console.log('PR has conflicts, commenting /ai resolve'); console.log('PR has conflicts, commenting /ai resolve');
@ -30,7 +30,7 @@ jobs:
body: '/ai resolve' body: '/ai resolve'
}); });
} }
// Check if PR title starts with "updated" // Check if PR title starts with "updated"
if (pr.title.toLowerCase().startsWith('updated')) { if (pr.title.toLowerCase().startsWith('updated')) {
console.log('PR title starts with "updated", commenting /ai suggest title'); console.log('PR title starts with "updated", commenting /ai suggest title');
@ -40,4 +40,4 @@ jobs:
issue_number: pr.number, issue_number: pr.number,
body: '/ai suggest title' body: '/ai suggest title'
}); });
} }

View File

@ -8,70 +8,70 @@ jobs:
lint_and_check_trailing_whitespaces: lint_and_check_trailing_whitespaces:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: '3.8' python-version: "3.8"
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install csvkit python -m pip install csvkit
- name: Validate CSV structure - name: Validate CSV structure
run: | run: |
echo "Checking CSV structure..." echo "Checking CSV structure..."
if ! csvclean -n prompts.csv 2>&1 | tee /tmp/csv_errors.log; then if ! csvclean -n prompts.csv 2>&1 | tee /tmp/csv_errors.log; then
echo "::error::CSV validation failed" echo "::error::CSV validation failed"
cat /tmp/csv_errors.log cat /tmp/csv_errors.log
exit 1 exit 1
fi fi
- name: Check CSV format - name: Check CSV format
run: | run: |
echo "Checking CSV format..." echo "Checking CSV format..."
if ! python -c ' if ! python -c '
import csv import csv
with open("prompts.csv", "r", encoding="utf-8") as f: with open("prompts.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f) reader = csv.reader(f)
headers = next(reader) headers = next(reader)
if headers != ["act", "prompt"]: if headers != ["act", "prompt"]:
print("Error: CSV headers must be exactly [act, prompt]") print("Error: CSV headers must be exactly [act, prompt]")
exit(1) exit(1)
for row_num, row in enumerate(reader, 2): for row_num, row in enumerate(reader, 2):
if len(row) != 2: if len(row) != 2:
print(f"Error: Row {row_num} has {len(row)} columns, expected 2") print(f"Error: Row {row_num} has {len(row)} columns, expected 2")
exit(1) exit(1)
if not row[0] or not row[1]: if not row[0] or not row[1]:
print(f"Error: Row {row_num} has empty values") print(f"Error: Row {row_num} has empty values")
exit(1) exit(1)
'; then '; then
echo "::error::CSV format check failed" echo "::error::CSV format check failed"
exit 1 exit 1
fi fi
- name: Check Trailing Whitespaces - name: Check Trailing Whitespaces
run: | run: |
echo "Checking for trailing whitespaces..." echo "Checking for trailing whitespaces..."
if grep -q "[[:space:]]$" prompts.csv; then if grep -q "[[:space:]]$" prompts.csv; then
echo "::error::Found trailing whitespaces in prompts.csv" echo "::error::Found trailing whitespaces in prompts.csv"
grep -n "[[:space:]]$" prompts.csv | while read -r line; do grep -n "[[:space:]]$" prompts.csv | while read -r line; do
echo "Line with trailing whitespace: $line" echo "Line with trailing whitespace: $line"
done done
exit 1 exit 1
fi fi
echo "No trailing whitespaces found" echo "No trailing whitespaces found"
- name: Check for UTF-8 BOM and line endings - name: Check for UTF-8 BOM and line endings
run: | run: |
echo "Checking for UTF-8 BOM and line endings..." echo "Checking for UTF-8 BOM and line endings..."
if file prompts.csv | grep -q "with BOM"; then if file prompts.csv | grep -q "with BOM"; then
echo "::error::File contains UTF-8 BOM marker" echo "::error::File contains UTF-8 BOM marker"
exit 1 exit 1
fi fi
if file prompts.csv | grep -q "CRLF"; then if file prompts.csv | grep -q "CRLF"; then
echo "::error::File contains Windows-style (CRLF) line endings" echo "::error::File contains Windows-style (CRLF) line endings"
exit 1 exit 1
fi fi

View File

@ -16,7 +16,7 @@ jobs:
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: '3.2' ruby-version: "3.2"
bundler-cache: true # This will cache dependencies bundler-cache: true # This will cache dependencies
- name: Update Bundler - name: Update Bundler

View File

@ -1,42 +1,66 @@
# Contribution Guidelines # Contribution Guidelines
Before contributing to this repository, please ensure you are adhering to the following general guidelines. Further, if you are submitting a new prompt to the repository, be sure you are also following the prompt-specific guidelines. These checks will ensure that your contributions can be easily integrated into the main repository, without any headache for the owners.
Before contributing to this repository, please ensure you are adhering to the
following general guidelines. Further, if you are submitting a new prompt to the
repository, be sure you are also following the prompt-specific guidelines. These
checks will ensure that your contributions can be easily integrated into the
main repository, without any headache for the owners.
## General Guidelines ## General Guidelines
The following guidelines should be followed when making any open-source contributions:
- [ ] Contributions should be made via a pull request to the main repository from a personal fork. The following guidelines should be followed when making any open-source
- [ ] Pull requests should be accompanied by a descriptive title and detailed explanation. contributions:
- [ ] Contributions should be made via a pull request to the main repository
from a personal fork.
- [ ] Pull requests should be accompanied by a descriptive title and detailed
explanation.
- [ ] Submit all pull requests to the repository's main branch. - [ ] Submit all pull requests to the repository's main branch.
- [ ] Before submitting a pull request, ensure additions/edits are aligned with the overall repo organization. - [ ] Before submitting a pull request, ensure additions/edits are aligned with
the overall repo organization.
- [ ] Be sure changes are compatible with the repository's license. - [ ] Be sure changes are compatible with the repository's license.
- [ ] In case of conflicts, provide helpful explanations regarding your proposed changes so that they can be approved by repo owners. - [ ] In case of conflicts, provide helpful explanations regarding your proposed
changes so that they can be approved by repo owners.
## New Prompt Guidelines ## New Prompt Guidelines
To add a new prompt to this repository, a contributor should take the following steps (in their personal fork):
To add a new prompt to this repository, a contributor should take the following
steps (in their personal fork):
1. Create and test the new prompt. 1. Create and test the new prompt.
- See the [README](https://github.com/f/awesome-chatgpt-prompts/blob/main/README.md) for guidance on how to write effective prompts. - See the
- Ensure prompts generate intended results and can be used by other users to replicate those results. [README](https://github.com/f/awesome-chatgpt-prompts/blob/main/README.md)
for guidance on how to write effective prompts.
- Ensure prompts generate intended results and can be used by other users to
replicate those results.
2. Add the prompt to `README.md` using the following markdown template: 2. Add the prompt to `README.md` using the following markdown template:
`## Prompt Title` `## Prompt Title`
`Contributed by: [@github_username](https://github.com/github_profile)` `Contributed by: [@github_username](https://github.com/github_profile)`
`> prompt content` `> prompt content`
- <b>Note:</b> If your prompt was generated by ChatGPT, append `<mark>Generated by ChatGPT</mark>` to the "Contributed by" line. - <b>Note:</b> If your prompt was generated by ChatGPT, append
`<mark>Generated by ChatGPT</mark>` to the "Contributed by" line.
3. Add the prompt to `prompts.csv`. 3. Add the prompt to `prompts.csv`.
- Put the prompt title in the `act` column, and the prompt itself in the `prompt` column. - Put the prompt title in the `act` column, and the prompt itself in the
`prompt` column.
4. Submit a pull request on the repository's main branch. 4. Submit a pull request on the repository's main branch.
- If possible, provide some documentation of how you tested your prompt and the kinds of results you received. - If possible, provide some documentation of how you tested your prompt and
- Be sure to include a detailed title and description. the kinds of results you received.
- Be sure to include a detailed title and description.
### New Prompt Checklist: ### New Prompt Checklist:
- [ ] I've confirmed the prompt works well - [ ] I've confirmed the prompt works well
- [ ] I've added `Contributed by: [@yourusername](https://github.com/yourusername)` - [ ] I've added
`Contributed by: [@yourusername](https://github.com/yourusername)`
- [ ] I've added to the README.md - [ ] I've added to the README.md
- [ ] I've added to the `prompts.csv` - [ ] I've added to the `prompts.csv`
- [ ] Escaped quotes by double-quoting them - [ ] Escaped quotes by double-quoting them
- [ ] No spaces after commas after double quotes. e.g. `"act","prompt"` not `"act", "prompt"` - [ ] No spaces after commas after double quotes. e.g. `"act","prompt"` not
`"act", "prompt"`
- [ ] Removed "Act as" from the title on CSV - [ ] Removed "Act as" from the title on CSV
Please ensure these requirements are met before submitting a pull request. Please ensure these requirements are met before submitting a pull request.

2374
README.md

File diff suppressed because it is too large Load Diff

View File

@ -318,6 +318,12 @@
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
/* Blinking cursor animation for development mode */
@keyframes blink-cursor {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.site-slogan { .site-slogan {
font-size: 0.9rem; font-size: 0.9rem;
opacity: 0.7; opacity: 0.7;
@ -1183,7 +1189,7 @@
border-radius: 12px; border-radius: 12px;
padding: 32px; padding: 32px;
width: 90%; width: 90%;
max-width: 460px; max-width: 520px;
z-index: 1002; z-index: 1002;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);
display: none; display: none;
@ -1194,10 +1200,22 @@
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
} }
.prompts-count-label {
display: none;
}
.dev-mode .count-label {
display: none;
}
.dev-mode .prompts-count-label {
display: block;
}
.copilot-suggestion-content { .copilot-suggestion-content {
margin-bottom: 24px; margin-bottom: 24px;
color: var(--text-color-light); color: var(--text-color-light);
font-size: 1rem; font-size: 1.1rem;
line-height: 1.5; line-height: 1.5;
} }
@ -1219,6 +1237,7 @@
font-size: 0.9rem; font-size: 0.9rem;
opacity: 0.8; opacity: 0.8;
order: 2; order: 2;
margin-top: 8px;
} }
.dark-mode .copilot-suggestion-checkbox { .dark-mode .copilot-suggestion-checkbox {
@ -1229,23 +1248,30 @@
width: 16px; width: 16px;
height: 16px; height: 16px;
accent-color: var(--accent-color); accent-color: var(--accent-color);
cursor: pointer;
margin: 0;
}
.copilot-suggestion-checkbox:hover {
opacity: 1;
} }
.copilot-suggestion-buttons { .copilot-suggestion-buttons {
display: flex; display: flex;
flex-direction: column;
gap: 12px; gap: 12px;
order: 1; order: 1;
} }
.copilot-suggestion-button { .copilot-suggestion-button {
padding: 10px 16px; padding: 12px 16px;
border-radius: 8px; border-radius: 8px;
font-size: 0.95rem; font-size: 0.95rem;
border: none; border: none;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
flex: 1;
font-weight: 500; font-weight: 500;
width: 100%;
} }
.copilot-suggestion-button.primary { .copilot-suggestion-button.primary {
@ -1281,15 +1307,17 @@
@media (max-width: 480px) { @media (max-width: 480px) {
.copilot-suggestion-modal { .copilot-suggestion-modal {
padding: 24px; padding: 24px;
max-width: 100%;
margin: 0 16px;
} }
.copilot-suggestion-content { .copilot-suggestion-content {
font-size: 0.95rem; font-size: 1rem;
margin-bottom: 20px; margin-bottom: 20px;
} }
.copilot-suggestion-button { .copilot-suggestion-button {
padding: 8px 14px; padding: 10px 16px;
font-size: 0.9rem; font-size: 0.9rem;
} }
} }
@ -1360,6 +1388,7 @@
<div class="search-container"> <div class="search-container">
<div class="prompt-count" id="promptCount"> <div class="prompt-count" id="promptCount">
<span class="count-label">All Prompts</span> <span class="count-label">All Prompts</span>
<span class="prompts-count-label">Developer Prompts</span>
<span class="count-number">0</span> <span class="count-number">0</span>
</div> </div>
<input type="text" id="searchInput" placeholder="Search prompts..."> <input type="text" id="searchInput" placeholder="Search prompts...">
@ -1442,800 +1471,7 @@
</div> </div>
</footer> </footer>
</div> </div>
<script> <script src="script.js"></script>
// Dark mode functionality
function toggleDarkMode() {
const body = document.body;
const toggle = document.querySelector('.dark-mode-toggle');
const sunIcon = toggle.querySelector('.sun-icon');
const moonIcon = toggle.querySelector('.moon-icon');
body.classList.toggle('dark-mode');
const isDarkMode = body.classList.contains('dark-mode');
localStorage.setItem('dark-mode', isDarkMode);
sunIcon.style.display = isDarkMode ? 'none' : 'block';
moonIcon.style.display = isDarkMode ? 'block' : 'none';
}
// Initialize everything after DOM loads
document.addEventListener('DOMContentLoaded', () => {
// Initialize dev mode
const devModeToggle = document.getElementById('devModeToggle');
const initialDevMode = localStorage.getItem('dev-mode') === 'true';
devModeToggle.checked = initialDevMode;
// Initialize chat button icons
updateChatButtonIcons(initialDevMode);
// Handle dev mode toggle
devModeToggle.addEventListener('change', (e) => {
const newDevMode = e.target.checked;
localStorage.setItem('dev-mode', newDevMode);
// Update chat button icons
updateChatButtonIcons(newDevMode);
// Check if we should show Copilot suggestion
if (newDevMode) {
const currentPlatform = document.querySelector('.platform-tag.active');
const shouldNotShow = localStorage.getItem('copilot-suggestion-hidden') === 'true';
if (currentPlatform &&
currentPlatform.dataset.platform !== 'github-copilot' &&
!shouldNotShow) {
showCopilotSuggestion();
}
}
filterPrompts();
});
// Fetch GitHub stars
fetch('https://api.github.com/repos/f/awesome-chatgpt-prompts')
.then(response => response.json())
.then(data => {
const stars = data.stargazers_count;
document.getElementById('starCount').textContent = stars.toLocaleString();
})
.catch(error => {
console.error('Error fetching star count:', error);
document.getElementById('starCount').textContent = '50k+';
});
// Create prompt cards
createPromptCards();
// Initialize dark mode
const isDarkMode = localStorage.getItem('dark-mode');
const toggle = document.querySelector('.dark-mode-toggle');
const sunIcon = toggle.querySelector('.sun-icon');
const moonIcon = toggle.querySelector('.moon-icon');
// Set dark mode by default if not set
if (isDarkMode === null) {
localStorage.setItem('dark-mode', 'true');
document.body.classList.add('dark-mode');
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
} else if (isDarkMode === 'true') {
document.body.classList.add('dark-mode');
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
} else {
sunIcon.style.display = 'block';
moonIcon.style.display = 'none';
}
// Initialize search functionality
initializeSearch();
// Initialize chat button icons on page load
const isDevMode = localStorage.getItem('dev-mode') === 'true';
updateChatButtonIcons(isDevMode);
});
// Search functionality
async function initializeSearch() {
try {
const response = await fetch('/prompts.csv');
const csvText = await response.text();
const prompts = parseCSV(csvText);
// Sort prompts alphabetically by act
prompts.sort((a, b) => a.act.localeCompare(b.act));
const searchInput = document.getElementById('searchInput');
const searchResults = document.getElementById('searchResults');
const promptCount = document.getElementById('promptCount');
const isDevMode = document.getElementById('devModeToggle').checked;
// Update prompt count
const totalPrompts = isDevMode ? prompts.filter(p => p.for_devs === true).length : prompts.length;
updatePromptCount(totalPrompts, totalPrompts);
// Show filtered prompts initially
const filteredPrompts = isDevMode ? prompts.filter(p => p.for_devs === true) : prompts;
displaySearchResults(filteredPrompts);
searchInput.addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
const filteredPrompts = prompts.filter(prompt => {
const matchesSearch = prompt.act.toLowerCase().includes(searchTerm) ||
prompt.prompt.toLowerCase().includes(searchTerm);
return isDevMode ? (matchesSearch && prompt.for_devs === true) : matchesSearch;
});
// Update count with filtered results
const totalPrompts = isDevMode ? prompts.filter(p => p.for_devs === true).length : prompts.length;
updatePromptCount(filteredPrompts.length, totalPrompts);
displaySearchResults(filteredPrompts);
});
} catch (error) {
console.error('Error loading prompts:', error);
}
}
function updatePromptCount(filteredCount, totalCount) {
const promptCount = document.getElementById('promptCount');
const countLabel = promptCount.querySelector('.count-label');
const countNumber = promptCount.querySelector('.count-number');
if (filteredCount === totalCount) {
promptCount.classList.remove('filtered');
countLabel.textContent = 'All Prompts';
countNumber.textContent = totalCount;
} else {
promptCount.classList.add('filtered');
countLabel.textContent = `Found ${filteredCount} of ${totalCount}`;
countNumber.textContent = filteredCount;
}
}
function parseCSV(csv) {
const lines = csv.split('\n');
const headers = lines[0].split(',').map(header => header.replace(/"/g, '').trim());
return lines.slice(1).map(line => {
const values = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
const entry = {};
headers.forEach((header, index) => {
let value = values[index] ? values[index].replace(/"/g, '').trim() : '';
// Remove backticks from the act/title
if (header === 'act') {
value = value.replace(/`/g, '');
}
// Convert 'TRUE'/'FALSE' strings to boolean for for_devs
if (header === 'for_devs') {
value = value.toUpperCase() === 'TRUE';
}
entry[header] = value;
});
return entry;
}).filter(entry => entry.act && entry.prompt);
}
function displaySearchResults(results) {
const searchResults = document.getElementById('searchResults');
const searchInput = document.getElementById('searchInput');
const isDevMode = document.getElementById('devModeToggle').checked;
// Filter results based on dev mode
if (isDevMode) {
results = results.filter(result => result.for_devs === true);
}
searchResults.innerHTML = '';
if (window.innerWidth <= 768 && !searchInput.value.trim()) {
return;
}
if (results.length === 0) {
const li = document.createElement('li');
li.className = 'search-result-item add-prompt';
li.innerHTML = `
<a href="https://github.com/f/awesome-chatgpt-prompts/pulls" target="_blank" style="text-decoration: none; color: inherit; display: flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
Add this prompt
</a>
`;
searchResults.appendChild(li);
return;
}
results.forEach(result => {
const li = document.createElement('li');
li.className = 'search-result-item';
li.textContent = result.act;
li.addEventListener('click', () => {
// Find the prompt card with matching title
const cards = document.querySelectorAll('.prompt-card');
const targetCard = Array.from(cards).find(card => {
const cardTitle = card.querySelector('.prompt-title').textContent
.replace(/\s+/g, ' ') // Normalize whitespace
.replace(/[\n\r]/g, '') // Remove newlines
.trim();
const searchTitle = result.act
.replace(/\s+/g, ' ') // Normalize whitespace
.replace(/[\n\r]/g, '') // Remove newlines
.trim();
return cardTitle.toLowerCase().includes(searchTitle.toLowerCase()) || searchTitle.toLowerCase().includes(cardTitle.toLowerCase());
});
if (targetCard) {
// Remove highlight from all cards
cards.forEach(card => {
card.style.transition = 'all 0.3s ease';
card.style.transform = 'none';
card.style.boxShadow = 'none';
card.style.borderColor = '';
});
// Different scroll behavior for mobile and desktop
const isMobile = window.innerWidth <= 768;
const headerHeight = document.querySelector('.site-header').offsetHeight;
if (isMobile) {
// On mobile, scroll the window
const cardRect = targetCard.getBoundingClientRect();
const scrollTop = window.pageYOffset + cardRect.top - headerHeight - 20;
window.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
} else {
// On desktop, scroll the main-content container
const mainContent = document.querySelector('.main-content');
const cardRect = targetCard.getBoundingClientRect();
const scrollTop = mainContent.scrollTop + cardRect.top - headerHeight - 20;
mainContent.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
}
// Add highlight effect after scrolling completes
setTimeout(() => {
targetCard.style.transform = 'scale(1.02)';
targetCard.style.boxShadow = '0 0 0 2px var(--accent-color)';
targetCard.style.borderColor = 'var(--accent-color)';
// Remove highlight after animation
setTimeout(() => {
targetCard.style.transform = 'none';
targetCard.style.boxShadow = 'none';
targetCard.style.borderColor = '';
}, 2000);
}, 500); // Wait for scroll to complete
} else {
console.log('Card not found for:', result.act);
}
});
searchResults.appendChild(li);
});
}
// Function to filter prompts based on dev mode
function filterPrompts() {
const isDevMode = document.getElementById('devModeToggle').checked;
const searchInput = document.getElementById('searchInput');
const searchTerm = searchInput.value.toLowerCase();
// Re-fetch and filter prompts
fetch('/prompts.csv')
.then(response => response.text())
.then(csvText => {
const prompts = parseCSV(csvText);
const filteredPrompts = prompts.filter(prompt => {
const matchesSearch = !searchTerm ||
prompt.act.toLowerCase().includes(searchTerm) ||
prompt.prompt.toLowerCase().includes(searchTerm);
return isDevMode ? (matchesSearch && prompt.for_devs === true) : matchesSearch;
});
// Update count with filtered results
updatePromptCount(filteredPrompts.length, isDevMode ? prompts.filter(p => p.for_devs === true).length : prompts.length);
displaySearchResults(filteredPrompts);
// Update prompt cards visibility
const promptsGrid = document.querySelector('.prompts-grid');
if (promptsGrid) {
const cards = promptsGrid.querySelectorAll('.prompt-card:not(.contribute-card)');
cards.forEach(card => {
const title = card.querySelector('.prompt-title').textContent.trim();
const matchingPrompt = prompts.find(p => {
const pTitle = p.act.replace(/\s+/g, ' ').replace(/[\n\r]/g, '').trim();
const cardTitle = title.replace(/\s+/g, ' ').replace(/[\n\r]/g, '').trim();
return pTitle.toLowerCase() === cardTitle.toLowerCase() ||
pTitle.toLowerCase().includes(cardTitle.toLowerCase()) ||
cardTitle.toLowerCase().includes(pTitle.toLowerCase());
});
// Show card if not in dev mode or if it's a dev prompt in dev mode
card.style.display = (!isDevMode || (matchingPrompt && matchingPrompt.for_devs === true)) ? '' : 'none';
});
}
});
}
// Update the modal initialization and event listeners
function createPromptCards() {
const container = document.querySelector('.container-lg.markdown-body');
const promptsGrid = document.createElement('div');
promptsGrid.className = 'prompts-grid';
// Add contribute box
const contributeCard = document.createElement('div');
contributeCard.className = 'prompt-card contribute-card';
contributeCard.innerHTML = `
<a href="https://github.com/f/awesome-chatgpt-prompts/pulls" target="_blank" style="text-decoration: none; color: inherit; height: 100%; display: flex; flex-direction: column;">
<div class="prompt-title" style="display: flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
Add Your Prompt
</div>
<p class="prompt-content" style="flex-grow: 1;">
Share your creative prompts with the community! Submit a pull request to add your prompts to the collection.
</p>
<span class="contributor-badge">Contribute Now</span>
</a>
`;
promptsGrid.appendChild(contributeCard);
// Fetch prompts.csv to get for_devs information
fetch('/prompts.csv')
.then(response => response.text())
.then(csvText => {
const prompts = parseCSV(csvText);
const isDevMode = document.getElementById('devModeToggle').checked;
const promptElements = document.querySelectorAll('h2[id^=act] + p + blockquote');
promptElements.forEach((blockquote) => {
const title = blockquote.previousElementSibling.previousElementSibling.textContent.trim();
const content = blockquote.textContent.trim();
// Find matching prompt in CSV
const matchingPrompt = prompts.find(p => {
const csvTitle = p.act.replace(/\s+/g, ' ').replace(/[\n\r]/g, '').trim();
const elementTitle = title.replace(/\s+/g, ' ').replace(/[\n\r]/g, '').trim();
return csvTitle.toLowerCase() === elementTitle.toLowerCase() ||
csvTitle.toLowerCase().includes(elementTitle.toLowerCase()) ||
elementTitle.toLowerCase().includes(csvTitle.toLowerCase());
});
// Extract contributor from the paragraph element
const contributorParagraph = blockquote.previousElementSibling;
const contributorText = contributorParagraph.textContent;
let contributor = null;
// Try different contributor formats
const formats = [
/Contributed by: \[([^\]]+)\]/i,
/Contributed by \[([^\]]+)\]/i,
/Contributed by: @([^\s]+)/i,
/Contributed by @([^\s]+)/i,
/Contributed by: \[@([^\]]+)\]/i,
/Contributed by \[@([^\]]+)\]/i
];
for (const format of formats) {
const match = contributorText.match(format);
if (match) {
contributor = match[1];
// Remove @ if it exists at the start
contributor = contributor.replace(/^@/, '');
break;
}
}
// Set default contributor to 'f' if none found
if (!contributor) {
contributor = 'f';
}
const card = document.createElement('div');
card.className = 'prompt-card';
// Set initial visibility based on dev mode
if (isDevMode && (!matchingPrompt || !matchingPrompt.for_devs)) {
card.style.display = 'none';
}
card.innerHTML = `
<div class="prompt-title">
${title}
<div class="action-buttons">
<button class="chat-button" title="Open in AI Chat" onclick="openInChat(this, '${encodeURIComponent(content.trim())}')">
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
</button>
<button class="copy-button" title="Copy prompt" onclick="copyPrompt(this, '${encodeURIComponent(content.trim())}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
</svg>
</button>
</div>
</div>
<p class="prompt-content">${content}</p>
<a href="https://github.com/${contributor}" class="contributor-badge" target="_blank" rel="noopener">@${contributor}</a>
`;
// Add click event for showing modal
card.addEventListener('click', (e) => {
if (!e.target.closest('.copy-button') && !e.target.closest('.contributor-badge')) {
showModal(title, content);
}
});
const copyButton = card.querySelector('.copy-button');
copyButton.addEventListener('click', async (e) => {
e.stopPropagation();
try {
await navigator.clipboard.writeText(content);
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
`;
setTimeout(() => {
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;
}, 2000);
} catch (err) {
alert('Failed to copy prompt to clipboard');
}
});
promptsGrid.appendChild(card);
});
container.innerHTML = '';
container.appendChild(promptsGrid);
// Initialize modal event listeners
initializeModalListeners();
})
.catch(error => {
console.error('Error loading prompts:', error);
});
}
function initializeModalListeners() {
const modalOverlay = document.getElementById('modalOverlay');
const modalClose = document.querySelector('.modal-close');
if (!modalOverlay || !modalClose) return;
modalClose.addEventListener('click', hideModal);
modalOverlay.addEventListener('click', (e) => {
if (e.target === modalOverlay) {
hideModal();
}
});
}
// Add global event listener for Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
hideModal();
}
});
function createModal() {
const modalHTML = `
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title"></h2>
<div class="modal-actions">
<button class="modal-copy-button" title="Copy prompt">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
<button class="modal-close" title="Close">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
<div class="modal-content"></div>
<div class="modal-footer">
<div class="modal-footer-left">
<a class="modal-contributor" target="_blank" rel="noopener"></a>
</div>
<div class="modal-footer-right">
<button class="modal-chat-button" onclick="openModalChat()">
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
Start Chat
</button>
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHTML);
initializeModalListeners();
}
function showModal(title, content) {
let modalOverlay = document.getElementById('modalOverlay');
if (!modalOverlay) {
createModal();
modalOverlay = document.getElementById('modalOverlay');
}
const modalTitle = modalOverlay.querySelector('.modal-title');
const modalContent = modalOverlay.querySelector('.modal-content');
const modalCopyButton = modalOverlay.querySelector('.modal-copy-button');
const modalContributor = modalOverlay.querySelector('.modal-contributor');
const modalChatButton = modalOverlay.querySelector('.modal-chat-button');
if (!modalTitle || !modalContent) return;
modalTitle.textContent = title;
modalContent.textContent = content;
// Update chat button text with platform name and handle visibility
const platform = document.querySelector('.platform-tag.active');
const isDevMode = document.getElementById('devModeToggle').checked;
if (platform) {
const shouldHideChat = ['gemini', 'llama'].includes(platform.dataset.platform);
modalChatButton.style.display = shouldHideChat ? 'none' : 'flex';
if (!shouldHideChat) {
const chatIcon = modalChatButton.querySelector('.chat-icon');
const terminalIcon = modalChatButton.querySelector('.terminal-icon');
if (chatIcon && terminalIcon) {
chatIcon.style.display = isDevMode ? 'none' : 'block';
terminalIcon.style.display = isDevMode ? 'block' : 'none';
}
modalChatButton.innerHTML = `
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: ${isDevMode ? 'none' : 'block'}">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: ${isDevMode ? 'block' : 'none'}">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
Chat with ${platform.textContent}
`;
}
}
// Store content for chat button
modalChatButton.dataset.content = content;
// Find the contributor for this prompt
const promptCard = Array.from(document.querySelectorAll('.prompt-card')).find(card =>
card.querySelector('.prompt-title').textContent.trim() === title.trim()
);
if (promptCard) {
const contributorBadge = promptCard.querySelector('.contributor-badge');
if (contributorBadge) {
modalContributor.href = contributorBadge.href;
modalContributor.textContent = `Contributed by ${contributorBadge.textContent}`;
}
}
// Add copy functionality
modalCopyButton.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(content);
modalCopyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
`;
setTimeout(() => {
modalCopyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;
}, 2000);
} catch (err) {
alert('Failed to copy prompt to clipboard');
}
});
modalOverlay.style.display = 'block';
document.body.style.overflow = 'hidden';
}
function hideModal() {
const modalOverlay = document.getElementById('modalOverlay');
if (!modalOverlay) return;
modalOverlay.style.display = 'none';
document.body.style.overflow = '';
// Optional: Remove modal from DOM when hidden
modalOverlay.remove();
}
let selectedPlatform = localStorage.getItem('selected-platform') || 'github-copilot'; // Get from localStorage or default to github
// Platform toggle functionality
document.querySelectorAll('.platform-tag').forEach(button => {
button.addEventListener('click', () => {
document.querySelectorAll('.platform-tag').forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
selectedPlatform = button.dataset.platform;
localStorage.setItem('selected-platform', selectedPlatform);
// Hide/show chat buttons based on platform
const chatButtons = document.querySelectorAll('.chat-button, .modal-chat-button');
const shouldHideChat = ['gemini', 'llama'].includes(selectedPlatform);
chatButtons.forEach(btn => {
btn.style.display = shouldHideChat ? 'none' : 'flex';
});
});
});
// Set active platform from localStorage and handle initial button visibility
const platformToActivate = document.querySelector(`[data-platform="${selectedPlatform}"]`) ||
document.querySelector('[data-platform="github-copilot"]');
platformToActivate.classList.add('active');
// Set initial chat button visibility
const shouldHideChat = ['gemini', 'llama'].includes(selectedPlatform);
document.querySelectorAll('.chat-button, .modal-chat-button').forEach(btn => {
btn.style.display = shouldHideChat ? 'none' : 'flex';
});
// Function to open prompt in selected AI chat platform
function openInChat(button, encodedPrompt) {
const promptText = decodeURIComponent(encodedPrompt);
const platform = document.querySelector('.platform-tag.active');
if (!platform) return;
const baseUrl = platform.dataset.url;
let url;
switch (platform.dataset.platform) {
case 'github-copilot':
url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`;
break;
case 'chatgpt':
url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`;
break;
case 'claude':
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
break;
case 'perplexity':
url = `${baseUrl}/search?q=${encodeURIComponent(promptText)}`;
break;
case 'mistral':
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
break;
default:
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
}
window.open(url, '_blank');
}
// Existing copy function
async function copyPrompt(button, encodedPrompt) {
const promptText = decodeURIComponent(encodedPrompt);
try {
await navigator.clipboard.writeText(promptText);
const originalHTML = button.innerHTML;
button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17 4 12"/></svg>';
setTimeout(() => {
button.innerHTML = originalHTML;
}, 1000);
} catch (err) {
console.error('Failed to copy text: ', err);
}
}
// Function to handle chat button click in modal
function openModalChat() {
const modalContent = document.querySelector('.modal-content');
if (modalContent) {
const content = modalContent.textContent;
openInChat(null, encodeURIComponent(content.trim()));
}
}
// Add these functions before the closing script tag
function showCopilotSuggestion() {
const modal = document.getElementById('copilotSuggestionModal');
const backdrop = document.querySelector('.copilot-suggestion-backdrop');
if (modal) {
if (!backdrop) {
const backdropDiv = document.createElement('div');
backdropDiv.className = 'copilot-suggestion-backdrop';
document.body.appendChild(backdropDiv);
}
modal.style.display = 'block';
backdrop.style.display = 'block';
document.body.style.overflow = 'hidden';
}
}
function hideCopilotSuggestion(switchToCopilot) {
const modal = document.getElementById('copilotSuggestionModal');
const backdrop = document.querySelector('.copilot-suggestion-backdrop');
const doNotShowCheckbox = document.getElementById('doNotShowAgain');
if (doNotShowCheckbox && doNotShowCheckbox.checked) {
localStorage.setItem('copilot-suggestion-hidden', 'true');
}
if (switchToCopilot) {
const copilotButton = document.querySelector('[data-platform="github-copilot"]');
if (copilotButton) {
copilotButton.click();
}
}
if (modal) {
modal.style.display = 'none';
if (backdrop) {
backdrop.style.display = 'none';
}
document.body.style.overflow = '';
}
}
// Function to update chat button icons based on dev mode
function updateChatButtonIcons(isDevMode) {
document.querySelectorAll('.chat-button, .modal-chat-button').forEach(button => {
const chatIcon = button.querySelector('.chat-icon');
const terminalIcon = button.querySelector('.terminal-icon');
if (chatIcon && terminalIcon) {
chatIcon.style.display = isDevMode ? 'none' : 'block';
terminalIcon.style.display = isDevMode ? 'block' : 'none';
}
});
}
</script>
<style>video { max-width: 100% !important; }</style> <style>video { max-width: 100% !important; }</style>
<!-- Google tag (gtag.js) --> <!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MSNHFWTE77"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-MSNHFWTE77"></script>

857
script.js Normal file
View File

@ -0,0 +1,857 @@
// Dark mode functionality
function toggleDarkMode() {
const body = document.body;
const toggle = document.querySelector(".dark-mode-toggle");
const sunIcon = toggle.querySelector(".sun-icon");
const moonIcon = toggle.querySelector(".moon-icon");
body.classList.toggle("dark-mode");
const isDarkMode = body.classList.contains("dark-mode");
localStorage.setItem("dark-mode", isDarkMode);
sunIcon.style.display = isDarkMode ? "none" : "block";
moonIcon.style.display = isDarkMode ? "block" : "none";
}
// Initialize everything after DOM loads
document.addEventListener("DOMContentLoaded", () => {
// Initialize dev mode
const devModeToggle = document.getElementById("devModeToggle");
const initialDevMode = localStorage.getItem("dev-mode") === "true";
devModeToggle.checked = initialDevMode;
// Initialize chat button icons
updateChatButtonIcons(initialDevMode);
// Handle dev mode toggle
devModeToggle.addEventListener("change", (e) => {
const newDevMode = e.target.checked;
localStorage.setItem("dev-mode", newDevMode);
// Toggle dev-mode class on body element
document.body.classList.toggle("dev-mode", newDevMode);
// Update chat button icons
updateChatButtonIcons(newDevMode);
// Check if we should show Copilot suggestion
if (newDevMode) {
const currentPlatform = document.querySelector(".platform-tag.active");
const shouldNotShow =
localStorage.getItem("copilot-suggestion-hidden") === "true";
if (
currentPlatform &&
currentPlatform.dataset.platform !== "github-copilot" &&
!shouldNotShow
) {
showCopilotSuggestion();
}
}
filterPrompts();
});
// Fetch GitHub stars
fetch("https://api.github.com/repos/f/awesome-chatgpt-prompts")
.then((response) => response.json())
.then((data) => {
const stars = data.stargazers_count;
document.getElementById("starCount").textContent = stars.toLocaleString();
})
.catch((error) => {
console.error("Error fetching star count:", error);
document.getElementById("starCount").textContent = "50k+";
});
// Create prompt cards
createPromptCards();
// Initialize dark mode
const isDarkMode = localStorage.getItem("dark-mode");
const toggle = document.querySelector(".dark-mode-toggle");
const sunIcon = toggle.querySelector(".sun-icon");
const moonIcon = toggle.querySelector(".moon-icon");
// Set dark mode by default if not set
if (isDarkMode === null) {
localStorage.setItem("dark-mode", "true");
document.body.classList.add("dark-mode");
sunIcon.style.display = "none";
moonIcon.style.display = "block";
} else if (isDarkMode === "true") {
document.body.classList.add("dark-mode");
sunIcon.style.display = "none";
moonIcon.style.display = "block";
} else {
sunIcon.style.display = "block";
moonIcon.style.display = "none";
}
// Initialize search functionality
initializeSearch();
// Initialize chat button icons on page load
const isDevMode = localStorage.getItem("dev-mode") === "true";
document.body.classList.toggle("dev-mode", isDevMode);
updateChatButtonIcons(isDevMode);
});
// Search functionality
async function initializeSearch() {
try {
const response = await fetch("/prompts.csv");
const csvText = await response.text();
const prompts = parseCSV(csvText);
// Sort prompts alphabetically by act
prompts.sort((a, b) => a.act.localeCompare(b.act));
const searchInput = document.getElementById("searchInput");
const searchResults = document.getElementById("searchResults");
const promptCount = document.getElementById("promptCount");
const isDevMode = document.getElementById("devModeToggle").checked;
// Update prompt count
const totalPrompts = isDevMode
? prompts.filter((p) => p.for_devs === true).length
: prompts.length;
updatePromptCount(totalPrompts, totalPrompts);
// Show filtered prompts initially
const filteredPrompts = isDevMode
? prompts.filter((p) => p.for_devs === true)
: prompts;
displaySearchResults(filteredPrompts);
searchInput.addEventListener("input", (e) => {
const searchTerm = e.target.value.toLowerCase();
const filteredPrompts = prompts.filter((prompt) => {
const matchesSearch = prompt.act.toLowerCase().includes(searchTerm) ||
prompt.prompt.toLowerCase().includes(searchTerm);
return isDevMode
? (matchesSearch && prompt.for_devs === true)
: matchesSearch;
});
// Update count with filtered results
const totalPrompts = isDevMode
? prompts.filter((p) => p.for_devs === true).length
: prompts.length;
updatePromptCount(filteredPrompts.length, totalPrompts);
displaySearchResults(filteredPrompts);
});
} catch (error) {
console.error("Error loading prompts:", error);
}
}
function updatePromptCount(filteredCount, totalCount) {
const promptCount = document.getElementById("promptCount");
const countLabel = promptCount.querySelector(".count-label");
const countNumber = promptCount.querySelector(".count-number");
if (filteredCount === totalCount) {
promptCount.classList.remove("filtered");
countLabel.textContent = "All Prompts";
countNumber.textContent = totalCount;
} else {
promptCount.classList.add("filtered");
countLabel.textContent = `Found ${filteredCount} of ${totalCount}`;
countNumber.textContent = filteredCount;
}
}
function parseCSV(csv) {
const lines = csv.split("\n");
const headers = lines[0].split(",").map((header) =>
header.replace(/"/g, "").trim()
);
return lines.slice(1).map((line) => {
const values = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
const entry = {};
headers.forEach((header, index) => {
let value = values[index] ? values[index].replace(/"/g, "").trim() : "";
// Remove backticks from the act/title
if (header === "act") {
value = value.replace(/`/g, "");
}
// Convert 'TRUE'/'FALSE' strings to boolean for for_devs
if (header === "for_devs") {
value = value.toUpperCase() === "TRUE";
}
entry[header] = value;
});
return entry;
}).filter((entry) => entry.act && entry.prompt);
}
function displaySearchResults(results) {
const searchResults = document.getElementById("searchResults");
const searchInput = document.getElementById("searchInput");
const isDevMode = document.getElementById("devModeToggle").checked;
// Filter results based on dev mode
if (isDevMode) {
results = results.filter((result) => result.for_devs === true);
}
searchResults.innerHTML = "";
if (window.innerWidth <= 768 && !searchInput.value.trim()) {
return;
}
if (results.length === 0) {
const li = document.createElement("li");
li.className = "search-result-item add-prompt";
li.innerHTML = `
<a href="https://github.com/f/awesome-chatgpt-prompts/pulls" target="_blank" style="text-decoration: none; color: inherit; display: flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
Add this prompt
</a>
`;
searchResults.appendChild(li);
return;
}
results.forEach((result) => {
const li = document.createElement("li");
li.className = "search-result-item";
li.textContent = result.act;
li.addEventListener("click", () => {
// Find the prompt card with matching title
const cards = document.querySelectorAll(".prompt-card");
const targetCard = Array.from(cards).find((card) => {
const cardTitle = card.querySelector(".prompt-title").textContent
.replace(/\s+/g, " ") // Normalize whitespace
.replace(/[\n\r]/g, "") // Remove newlines
.trim();
const searchTitle = result.act
.replace(/\s+/g, " ") // Normalize whitespace
.replace(/[\n\r]/g, "") // Remove newlines
.trim();
return cardTitle.toLowerCase().includes(searchTitle.toLowerCase()) ||
searchTitle.toLowerCase().includes(cardTitle.toLowerCase());
});
if (targetCard) {
// Remove highlight from all cards
cards.forEach((card) => {
card.style.transition = "all 0.3s ease";
card.style.transform = "none";
card.style.boxShadow = "none";
card.style.borderColor = "";
});
// Different scroll behavior for mobile and desktop
const isMobile = window.innerWidth <= 768;
const headerHeight =
document.querySelector(".site-header").offsetHeight;
if (isMobile) {
// On mobile, scroll the window
const cardRect = targetCard.getBoundingClientRect();
const scrollTop = window.pageYOffset + cardRect.top - headerHeight -
20;
window.scrollTo({
top: scrollTop,
behavior: "smooth",
});
} else {
// On desktop, scroll the main-content container
const mainContent = document.querySelector(".main-content");
const cardRect = targetCard.getBoundingClientRect();
const scrollTop = mainContent.scrollTop + cardRect.top -
headerHeight - 20;
mainContent.scrollTo({
top: scrollTop,
behavior: "smooth",
});
}
// Add highlight effect after scrolling completes
setTimeout(() => {
targetCard.style.transform = "scale(1.02)";
targetCard.style.boxShadow = "0 0 0 2px var(--accent-color)";
targetCard.style.borderColor = "var(--accent-color)";
// Remove highlight after animation
setTimeout(() => {
targetCard.style.transform = "none";
targetCard.style.boxShadow = "none";
targetCard.style.borderColor = "";
}, 2000);
}, 500); // Wait for scroll to complete
} else {
console.log("Card not found for:", result.act);
}
});
searchResults.appendChild(li);
});
}
// Function to filter prompts based on dev mode
function filterPrompts() {
const isDevMode = document.getElementById("devModeToggle").checked;
const searchInput = document.getElementById("searchInput");
const searchTerm = searchInput.value.toLowerCase();
// Re-fetch and filter prompts
fetch("/prompts.csv")
.then((response) => response.text())
.then((csvText) => {
const prompts = parseCSV(csvText);
const filteredPrompts = prompts.filter((prompt) => {
const matchesSearch = !searchTerm ||
prompt.act.toLowerCase().includes(searchTerm) ||
prompt.prompt.toLowerCase().includes(searchTerm);
return isDevMode
? (matchesSearch && prompt.for_devs === true)
: matchesSearch;
});
// Update count with filtered results
updatePromptCount(
filteredPrompts.length,
isDevMode
? prompts.filter((p) => p.for_devs === true).length
: prompts.length,
);
displaySearchResults(filteredPrompts);
// Update prompt cards visibility
const promptsGrid = document.querySelector(".prompts-grid");
if (promptsGrid) {
const cards = promptsGrid.querySelectorAll(
".prompt-card:not(.contribute-card)",
);
cards.forEach((card) => {
const title = card.querySelector(".prompt-title").textContent.trim();
const matchingPrompt = prompts.find((p) => {
const pTitle = p.act.replace(/\s+/g, " ").replace(/[\n\r]/g, "")
.trim();
const cardTitle = title.replace(/\s+/g, " ").replace(/[\n\r]/g, "")
.trim();
return pTitle.toLowerCase() === cardTitle.toLowerCase() ||
pTitle.toLowerCase().includes(cardTitle.toLowerCase()) ||
cardTitle.toLowerCase().includes(pTitle.toLowerCase());
});
// Show card if not in dev mode or if it's a dev prompt in dev mode
card.style.display =
(!isDevMode || (matchingPrompt && matchingPrompt.for_devs === true))
? ""
: "none";
});
}
});
}
// Update the modal initialization and event listeners
function createPromptCards() {
const container = document.querySelector(".container-lg.markdown-body");
const promptsGrid = document.createElement("div");
promptsGrid.className = "prompts-grid";
// Add contribute box
const contributeCard = document.createElement("div");
contributeCard.className = "prompt-card contribute-card";
contributeCard.innerHTML = `
<a href="https://github.com/f/awesome-chatgpt-prompts/pulls" target="_blank" style="text-decoration: none; color: inherit; height: 100%; display: flex; flex-direction: column;">
<div class="prompt-title" style="display: flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
Add Your Prompt
</div>
<p class="prompt-content" style="flex-grow: 1;">
Share your creative prompts with the community! Submit a pull request to add your prompts to the collection.
</p>
<span class="contributor-badge">Contribute Now</span>
</a>
`;
promptsGrid.appendChild(contributeCard);
// Fetch prompts.csv to get for_devs information
fetch("/prompts.csv")
.then((response) => response.text())
.then((csvText) => {
const prompts = parseCSV(csvText);
const isDevMode = document.getElementById("devModeToggle").checked;
const promptElements = document.querySelectorAll(
"h2[id^=act] + p + blockquote",
);
promptElements.forEach((blockquote) => {
const title = blockquote.previousElementSibling.previousElementSibling
.textContent.trim();
const content = blockquote.textContent.trim();
// Find matching prompt in CSV
const matchingPrompt = prompts.find((p) => {
const csvTitle = p.act.replace(/\s+/g, " ").replace(/[\n\r]/g, "")
.trim();
const elementTitle = title.replace(/\s+/g, " ").replace(/[\n\r]/g, "")
.trim();
return csvTitle.toLowerCase() === elementTitle.toLowerCase() ||
csvTitle.toLowerCase().includes(elementTitle.toLowerCase()) ||
elementTitle.toLowerCase().includes(csvTitle.toLowerCase());
});
// Extract contributor from the paragraph element
const contributorParagraph = blockquote.previousElementSibling;
const contributorText = contributorParagraph.textContent;
let contributor = null;
// Try different contributor formats
const formats = [
/Contributed by: \[([^\]]+)\]/i,
/Contributed by \[([^\]]+)\]/i,
/Contributed by: @([^\s]+)/i,
/Contributed by @([^\s]+)/i,
/Contributed by: \[@([^\]]+)\]/i,
/Contributed by \[@([^\]]+)\]/i,
];
for (const format of formats) {
const match = contributorText.match(format);
if (match) {
contributor = match[1];
// Remove @ if it exists at the start
contributor = contributor.replace(/^@/, "");
break;
}
}
// Set default contributor to 'f' if none found
if (!contributor) {
contributor = "f";
}
const card = document.createElement("div");
card.className = "prompt-card";
// Set initial visibility based on dev mode
if (isDevMode && (!matchingPrompt || !matchingPrompt.for_devs)) {
card.style.display = "none";
}
card.innerHTML = `
<div class="prompt-title">
${title}
<div class="action-buttons">
<button class="chat-button" title="Open in AI Chat" onclick="openInChat(this, '${
encodeURIComponent(content.trim())
}')">
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
</button>
<button class="copy-button" title="Copy prompt" onclick="copyPrompt(this, '${
encodeURIComponent(content.trim())
}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
</svg>
</button>
</div>
</div>
<p class="prompt-content">${content}</p>
<a href="https://github.com/${contributor}" class="contributor-badge" target="_blank" rel="noopener">@${contributor}</a>
`;
// Add click event for showing modal
card.addEventListener("click", (e) => {
if (
!e.target.closest(".copy-button") &&
!e.target.closest(".contributor-badge")
) {
showModal(title, content);
}
});
const copyButton = card.querySelector(".copy-button");
copyButton.addEventListener("click", async (e) => {
e.stopPropagation();
try {
await navigator.clipboard.writeText(content);
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
`;
setTimeout(() => {
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;
}, 2000);
} catch (err) {
alert("Failed to copy prompt to clipboard");
}
});
promptsGrid.appendChild(card);
});
container.innerHTML = "";
container.appendChild(promptsGrid);
// Initialize modal event listeners
initializeModalListeners();
})
.catch((error) => {
console.error("Error loading prompts:", error);
});
}
function initializeModalListeners() {
const modalOverlay = document.getElementById("modalOverlay");
const modalClose = document.querySelector(".modal-close");
if (!modalOverlay || !modalClose) return;
modalClose.addEventListener("click", hideModal);
modalOverlay.addEventListener("click", (e) => {
if (e.target === modalOverlay) {
hideModal();
}
});
}
// Add global event listener for Escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
hideModal();
}
});
function createModal() {
const modalHTML = `
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title"></h2>
<div class="modal-actions">
<button class="modal-copy-button" title="Copy prompt">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
<button class="modal-close" title="Close">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
<div class="modal-content"></div>
<div class="modal-footer">
<div class="modal-footer-left">
<a class="modal-contributor" target="_blank" rel="noopener"></a>
</div>
<div class="modal-footer-right">
<button class="modal-chat-button" onclick="openModalChat()">
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" style="display: none;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
Start Chat
</button>
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML("beforeend", modalHTML);
initializeModalListeners();
}
function showModal(title, content) {
let modalOverlay = document.getElementById("modalOverlay");
if (!modalOverlay) {
createModal();
modalOverlay = document.getElementById("modalOverlay");
}
const modalTitle = modalOverlay.querySelector(".modal-title");
const modalContent = modalOverlay.querySelector(".modal-content");
const modalCopyButton = modalOverlay.querySelector(".modal-copy-button");
const modalContributor = modalOverlay.querySelector(".modal-contributor");
const modalChatButton = modalOverlay.querySelector(".modal-chat-button");
if (!modalTitle || !modalContent) return;
modalTitle.textContent = title;
modalContent.textContent = content;
// Update chat button text with platform name and handle visibility
const platform = document.querySelector(".platform-tag.active");
const isDevMode = document.getElementById("devModeToggle").checked;
if (platform) {
const shouldHideChat = ["gemini", "llama"].includes(
platform.dataset.platform,
);
modalChatButton.style.display = shouldHideChat ? "none" : "flex";
if (!shouldHideChat) {
const chatIcon = modalChatButton.querySelector(".chat-icon");
const terminalIcon = modalChatButton.querySelector(".terminal-icon");
if (chatIcon && terminalIcon) {
chatIcon.style.display = isDevMode ? "none" : "block";
terminalIcon.style.display = isDevMode ? "block" : "none";
}
modalChatButton.innerHTML = `
<svg class="chat-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: ${
isDevMode ? "none" : "block"
}">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<svg class="terminal-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: ${
isDevMode ? "block" : "none"
}">
<polyline points="4 17 10 11 4 5"></polyline>
<line x1="12" y1="19" x2="20" y2="19"></line>
</svg>
Chat with ${platform.textContent}
`;
}
}
// Store content for chat button
modalChatButton.dataset.content = content;
// Find the contributor for this prompt
const promptCard = Array.from(document.querySelectorAll(".prompt-card")).find(
(card) =>
card.querySelector(".prompt-title").textContent.trim() === title.trim(),
);
if (promptCard) {
const contributorBadge = promptCard.querySelector(".contributor-badge");
if (contributorBadge) {
modalContributor.href = contributorBadge.href;
modalContributor.textContent =
`Contributed by ${contributorBadge.textContent}`;
}
}
// Add copy functionality
modalCopyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(content);
modalCopyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
`;
setTimeout(() => {
modalCopyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;
}, 2000);
} catch (err) {
alert("Failed to copy prompt to clipboard");
}
});
modalOverlay.style.display = "block";
document.body.style.overflow = "hidden";
}
function hideModal() {
const modalOverlay = document.getElementById("modalOverlay");
if (!modalOverlay) return;
modalOverlay.style.display = "none";
document.body.style.overflow = "";
// Optional: Remove modal from DOM when hidden
modalOverlay.remove();
}
let selectedPlatform = localStorage.getItem("selected-platform") ||
"github-copilot"; // Get from localStorage or default to github
// Platform toggle functionality
document.querySelectorAll(".platform-tag").forEach((button) => {
button.addEventListener("click", () => {
document.querySelectorAll(".platform-tag").forEach((btn) =>
btn.classList.remove("active")
);
button.classList.add("active");
selectedPlatform = button.dataset.platform;
localStorage.setItem("selected-platform", selectedPlatform);
// Hide/show chat buttons based on platform
const chatButtons = document.querySelectorAll(
".chat-button, .modal-chat-button",
);
const shouldHideChat = ["gemini", "llama"].includes(selectedPlatform);
chatButtons.forEach((btn) => {
btn.style.display = shouldHideChat ? "none" : "flex";
});
});
});
// Set active platform from localStorage and handle initial button visibility
const platformToActivate =
document.querySelector(`[data-platform="${selectedPlatform}"]`) ||
document.querySelector('[data-platform="github-copilot"]');
platformToActivate.classList.add("active");
// Set initial chat button visibility
const shouldHideChat = ["gemini", "llama"].includes(selectedPlatform);
document.querySelectorAll(".chat-button, .modal-chat-button").forEach((btn) => {
btn.style.display = shouldHideChat ? "none" : "flex";
});
// Function to open prompt in selected AI chat platform
function openInChat(button, encodedPrompt) {
const promptText = decodeURIComponent(encodedPrompt);
const platform = document.querySelector(".platform-tag.active");
if (!platform) return;
const baseUrl = platform.dataset.url;
let url;
switch (platform.dataset.platform) {
case "github-copilot":
url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`;
break;
case "chatgpt":
url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`;
break;
case "claude":
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
break;
case "perplexity":
url = `${baseUrl}/search?q=${encodeURIComponent(promptText)}`;
break;
case "mistral":
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
break;
default:
url = `${baseUrl}?q=${encodeURIComponent(promptText)}`;
}
window.open(url, "_blank");
}
// Existing copy function
async function copyPrompt(button, encodedPrompt) {
const promptText = decodeURIComponent(encodedPrompt);
try {
await navigator.clipboard.writeText(promptText);
const originalHTML = button.innerHTML;
button.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17 4 12"/></svg>';
setTimeout(() => {
button.innerHTML = originalHTML;
}, 1000);
} catch (err) {
console.error("Failed to copy text: ", err);
}
}
// Function to handle chat button click in modal
function openModalChat() {
const modalContent = document.querySelector(".modal-content");
if (modalContent) {
const content = modalContent.textContent;
openInChat(null, encodeURIComponent(content.trim()));
}
}
// Add these functions before the closing script tag
function showCopilotSuggestion() {
const modal = document.getElementById("copilotSuggestionModal");
const backdrop = document.querySelector(".copilot-suggestion-backdrop");
if (modal) {
if (!backdrop) {
const backdropDiv = document.createElement("div");
backdropDiv.className = "copilot-suggestion-backdrop";
document.body.appendChild(backdropDiv);
}
modal.style.display = "block";
backdrop.style.display = "block";
document.body.style.overflow = "hidden";
}
}
function hideCopilotSuggestion(switchToCopilot) {
const modal = document.getElementById("copilotSuggestionModal");
const backdrop = document.querySelector(".copilot-suggestion-backdrop");
const doNotShowCheckbox = document.getElementById("doNotShowAgain");
if (doNotShowCheckbox && doNotShowCheckbox.checked) {
localStorage.setItem("copilot-suggestion-hidden", "true");
}
if (switchToCopilot) {
const copilotButton = document.querySelector(
'[data-platform="github-copilot"]',
);
if (copilotButton) {
copilotButton.click();
}
}
if (modal) {
modal.style.display = "none";
if (backdrop) {
backdrop.style.display = "none";
}
document.body.style.overflow = "";
}
}
// Function to update chat button icons based on dev mode
function updateChatButtonIcons(isDevMode) {
document.querySelectorAll(".chat-button, .modal-chat-button").forEach(
(button) => {
const chatIcon = button.querySelector(".chat-icon");
const terminalIcon = button.querySelector(".terminal-icon");
if (chatIcon && terminalIcon) {
chatIcon.style.display = isDevMode ? "none" : "block";
terminalIcon.style.display = isDevMode ? "block" : "none";
}
},
);
}