├── .cursorrules ├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ ├── ai_bot.yml │ ├── auto_commands.yml │ ├── csv_linter.yml │ └── publish.yml ├── .gitignore ├── .windsurfrules ├── CNAME ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── _config.yml ├── _layouts └── default.html ├── prompts.csv ├── script.js ├── scripts └── find-prompt ├── style.css ├── vibe ├── index.md └── script.js └── vibeprompts.csv /.cursorrules: -------------------------------------------------------------------------------- 1 | # Project Configuration 2 | 3 | ## Project Type 4 | - Static Site Generator: Jekyll 5 | - Hosting: GitHub Pages 6 | 7 | ## Build Commands 8 | - `bundle install`: Install dependencies 9 | - `bundle exec jekyll serve`: Run development server 10 | - `bundle exec jekyll build`: Build site for production 11 | 12 | ## Important Files 13 | - `_config.yml`: Jekyll configuration 14 | - `Gemfile`: Ruby dependencies 15 | - `_site/`: Build output directory (generated) 16 | - `_posts/`: Blog posts directory 17 | - `_layouts/`: Template layouts 18 | - `_includes/`: Reusable components 19 | 20 | ## GitHub Pages Settings 21 | - Branch: gh-pages (or main/master, depending on your setup) 22 | - Build Source: Jekyll -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [f] 4 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Add New Prompt 2 | 3 | You'll need to add your prompt into README.md, and to the `prompts.csv` file. If 4 | your prompt includes quotes, you will need to double-quote them to escape in CSV 5 | file. 6 | 7 | If the prompt is generated by AI, please add `Generated by AI` to 8 | the end of the contribution line. 9 | 10 | - [ ] I've confirmed the prompt works well 11 | - [ ] I've added 12 | `Contributed by: [@yourusername](https://github.com/yourusername)` 13 | - [ ] I've added to the README.md 14 | - [ ] I've added to the `prompts.csv` 15 | - [ ] Escaped quotes by double-quoting them 16 | - [ ] No spaces after commas after double quotes. e.g. `"Hello","hi"`, not 17 | `"Hello", "hi"` 18 | - [ ] Removed "Act as" from the title on CSV 19 | 20 | Please make sure you've completed all the checklist. 21 | -------------------------------------------------------------------------------- /.github/workflows/ai_bot.yml: -------------------------------------------------------------------------------- 1 | name: AI Bot 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | issue_comment: 7 | types: [created] 8 | pull_request_review_comment: 9 | types: [created] 10 | pull_request: 11 | types: [opened, edited, synchronize] 12 | 13 | jobs: 14 | respond-to-commands: 15 | runs-on: ubuntu-latest 16 | if: | 17 | (github.actor == 'f') && 18 | ((github.event_name == 'issues' && contains(github.event.issue.body, '/ai')) || 19 | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/ai')) || 20 | (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/ai')) || 21 | (github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/ai'))) 22 | permissions: 23 | contents: write 24 | pull-requests: write 25 | issues: write 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 31 | token: ${{ secrets.PAT_TOKEN }} 32 | 33 | - name: Setup Node.js 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: "18" 37 | 38 | - name: Install dependencies 39 | run: npm install openai@^4.0.0 @octokit/rest@^19.0.0 40 | 41 | - name: Process command 42 | id: process 43 | env: 44 | GH_TOKEN: ${{ secrets.PAT_TOKEN }} 45 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 46 | run: | 47 | node << 'EOF' 48 | const OpenAI = require('openai'); 49 | const { Octokit } = require('@octokit/rest'); 50 | 51 | async function main() { 52 | const openai = new OpenAI({ 53 | apiKey: process.env.OPENAI_API_KEY 54 | }); 55 | 56 | const octokit = new Octokit({ 57 | auth: process.env.GH_TOKEN 58 | }); 59 | 60 | const eventName = process.env.GITHUB_EVENT_NAME; 61 | const eventPath = process.env.GITHUB_EVENT_PATH; 62 | const event = require(eventPath); 63 | 64 | // Double check user authorization 65 | const actor = event.sender?.login || event.pull_request?.user?.login || event.issue?.user?.login; 66 | if (actor !== 'f') { 67 | console.log('Unauthorized user attempted to use the bot:', actor); 68 | return; 69 | } 70 | 71 | // Get command and context 72 | let command = ''; 73 | let issueNumber = null; 74 | let isPullRequest = false; 75 | 76 | if (eventName === 'issues') { 77 | command = event.issue.body; 78 | issueNumber = event.issue.number; 79 | } else if (eventName === 'issue_comment') { 80 | command = event.comment.body; 81 | issueNumber = event.issue.number; 82 | isPullRequest = !!event.issue.pull_request; 83 | } else if (eventName === 'pull_request_review_comment') { 84 | command = event.comment.body; 85 | issueNumber = event.pull_request.number; 86 | isPullRequest = true; 87 | } else if (eventName === 'pull_request') { 88 | command = event.pull_request.body; 89 | issueNumber = event.pull_request.number; 90 | isPullRequest = true; 91 | } 92 | 93 | if (!command.startsWith('/ai')) { 94 | return; 95 | } 96 | 97 | // Extract the actual command after /ai 98 | const aiCommand = command.substring(3).trim(); 99 | 100 | // Handle resolve conflicts command 101 | if (aiCommand === 'resolve' || aiCommand === 'fix conflicts') { 102 | if (!isPullRequest) { 103 | console.log('Command rejected: Not a pull request'); 104 | await octokit.issues.createComment({ 105 | owner: event.repository.owner.login, 106 | repo: event.repository.name, 107 | issue_number: issueNumber, 108 | body: '❌ The resolve command can only be used on pull requests.' 109 | }); 110 | return; 111 | } 112 | 113 | try { 114 | console.log('Starting resolve command execution...'); 115 | 116 | // Get PR details 117 | console.log('Fetching PR details...'); 118 | const { data: pr } = await octokit.pulls.get({ 119 | owner: event.repository.owner.login, 120 | repo: event.repository.name, 121 | pull_number: issueNumber 122 | }); 123 | console.log(`Original PR found: #${issueNumber} from ${pr.user.login}`); 124 | 125 | // Get the PR diff to extract the new prompt 126 | console.log('Fetching PR file changes...'); 127 | const { data: files } = await octokit.pulls.listFiles({ 128 | owner: event.repository.owner.login, 129 | repo: event.repository.name, 130 | pull_number: issueNumber 131 | }); 132 | console.log(`Found ${files.length} changed files`); 133 | 134 | // Extract prompt from changes 135 | console.log('Analyzing changes to extract prompt information...'); 136 | const prompts = new Map(); // Use Map to deduplicate prompts by title 137 | 138 | // Helper function to normalize prompt titles 139 | const normalizeTitle = (title) => { 140 | title = title.trim(); 141 | // Remove "Act as" or "Act as a" or "Act as an" from start if present 142 | title = title.replace(/^Act as (?:a |an )?/i, ''); 143 | 144 | // Capitalize each word except common articles and prepositions 145 | const lowercaseWords = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'for', 'with', 'in']; 146 | const capitalized = title.toLowerCase().split(' ').map((word, index) => { 147 | // Always capitalize first and last word 148 | if (index === 0 || !lowercaseWords.includes(word)) { 149 | return word.charAt(0).toUpperCase() + word.slice(1); 150 | } 151 | return word; 152 | }).join(' '); 153 | 154 | // Add "Act as" prefix 155 | return `Act as ${capitalized}`; 156 | }; 157 | 158 | // First try to find prompts in README 159 | let foundInReadme = false; 160 | for (const file of files) { 161 | console.log(`Processing file: ${file.filename}`); 162 | if (file.filename === 'README.md') { 163 | const patch = file.patch || ''; 164 | const addedLines = patch.split('\n') 165 | .filter(line => line.startsWith('+')) 166 | .map(line => line.substring(1)) 167 | .join('\n'); 168 | 169 | console.log('Attempting to extract prompts from README changes...'); 170 | const promptMatches = [...addedLines.matchAll(/## (?:Act as (?:a |an )?)?([^\n]+)\n(?:Contributed by:[^\n]*\n)?(?:> )?([^#]+?)(?=\n##|\n\n##|$)/ig)]; 171 | 172 | for (const match of promptMatches) { 173 | const actName = normalizeTitle(match[1]); 174 | const promptText = match[2].trim() 175 | .replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '') 176 | .trim(); 177 | const contributorLine = addedLines.match(/Contributed by: \[@([^\]]+)\]\(https:\/\/github\.com\/([^\)]+)\)/); 178 | const contributorInfo = contributorLine 179 | ? `Contributed by: [@${contributorLine[1]}](https://github.com/${contributorLine[2]})` 180 | : `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`; 181 | 182 | prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo }); 183 | console.log(`Found prompt in README: "${actName}"`); 184 | foundInReadme = true; 185 | } 186 | } 187 | } 188 | 189 | // Only look in CSV if we didn't find anything in README 190 | if (!foundInReadme) { 191 | console.log('No prompts found in README, checking CSV...'); 192 | for (const file of files) { 193 | if (file.filename === 'prompts.csv') { 194 | const patch = file.patch || ''; 195 | const addedLines = patch.split('\n') 196 | .filter(line => line.startsWith('+')) 197 | .map(line => line.substring(1)) 198 | .filter(line => line.trim()); // Remove empty lines 199 | 200 | console.log('Attempting to extract prompts from CSV changes...'); 201 | for (const line of addedLines) { 202 | // Parse CSV line considering escaped quotes 203 | const matches = [...line.matchAll(/"([^"]*(?:""[^"]*)*)"/g)]; 204 | if (matches.length >= 2) { 205 | const actName = normalizeTitle(matches[0][1].replace(/""/g, '"').trim()); 206 | const promptText = matches[1][1].replace(/""/g, '"').trim() 207 | .replace(/^(?:Contributed by:?[^\n]*\n\s*)+/i, '') 208 | .trim(); 209 | 210 | const contributorInfo = `Contributed by: [@${pr.user.login}](https://github.com/${pr.user.login})`; 211 | prompts.set(actName.toLowerCase(), { actName, promptText, contributorInfo }); 212 | console.log(`Found prompt in CSV: "${actName}"`); 213 | } 214 | } 215 | } 216 | } 217 | } 218 | 219 | if (prompts.size === 0) { 220 | console.log('Failed to extract prompt information'); 221 | await octokit.issues.createComment({ 222 | owner: event.repository.owner.login, 223 | repo: event.repository.name, 224 | issue_number: issueNumber, 225 | body: '❌ Could not extract prompt information from changes' 226 | }); 227 | return; 228 | } 229 | 230 | // Get content from main branch 231 | console.log('Fetching current content from main branch...'); 232 | const { data: readmeFile } = await octokit.repos.getContent({ 233 | owner: event.repository.owner.login, 234 | repo: event.repository.name, 235 | path: 'README.md', 236 | ref: 'main' 237 | }); 238 | 239 | const { data: csvFile } = await octokit.repos.getContent({ 240 | owner: event.repository.owner.login, 241 | repo: event.repository.name, 242 | path: 'prompts.csv', 243 | ref: 'main' 244 | }); 245 | 246 | // Prepare new content 247 | console.log('Preparing content updates...'); 248 | let readmeContent = Buffer.from(readmeFile.content, 'base64').toString('utf-8'); 249 | let csvContent = Buffer.from(csvFile.content, 'base64').toString('utf-8'); 250 | if (!csvContent.endsWith('\n')) csvContent += '\n'; 251 | 252 | // Convert Map to array for processing 253 | const promptsArray = Array.from(prompts.values()); 254 | 255 | // Process each prompt 256 | for (const { actName, promptText, contributorInfo } of promptsArray) { 257 | // Remove markdown quote character and trim whitespace 258 | const cleanPrompt = promptText.replace(/^>\s*/gm, '').trim(); 259 | 260 | // For README: Add quote to each line 261 | const readmePrompt = cleanPrompt.split('\n') 262 | .map(line => `> ${line.trim()}`) 263 | .join('\n'); 264 | const newSection = `## ${actName}\n${contributorInfo}\n\n${readmePrompt}\n\n`; 265 | 266 | // For CSV: Convert to single paragraph 267 | const csvPrompt = cleanPrompt.replace(/\n+/g, ' ').trim(); 268 | 269 | // Insert the new section before Contributors in README 270 | const contributorsIndex = readmeContent.indexOf('## Contributors'); 271 | if (contributorsIndex === -1) { 272 | console.log('Contributors section not found, appending to end'); 273 | readmeContent += newSection; 274 | } else { 275 | console.log('Inserting before Contributors section'); 276 | readmeContent = readmeContent.slice(0, contributorsIndex) + newSection + readmeContent.slice(contributorsIndex); 277 | } 278 | 279 | // Add to CSV content 280 | csvContent += `"${actName.replace(/^Act as an?/i, '').replace(/"/g, '""')}","${csvPrompt.replace(/"/g, '""')}"\n`; 281 | } 282 | 283 | // Create new branch 284 | const branchName = `prompt/${promptsArray.map(p => p.actName.toLowerCase().replace(/[^a-z0-9]+/g, '-')).join('-')}`; 285 | console.log(`Creating new branch: ${branchName}`); 286 | 287 | // Check if branch exists and delete it 288 | try { 289 | console.log('Checking if branch already exists...'); 290 | const { data: existingRef } = await octokit.git.getRef({ 291 | owner: event.repository.owner.login, 292 | repo: event.repository.name, 293 | ref: `heads/${branchName}` 294 | }); 295 | 296 | if (existingRef) { 297 | // Check for existing PRs from this branch 298 | console.log('Checking for existing PRs from this branch...'); 299 | const { data: existingPRs } = await octokit.pulls.list({ 300 | owner: event.repository.owner.login, 301 | repo: event.repository.name, 302 | head: `${event.repository.owner.login}:${branchName}`, 303 | state: 'open' 304 | }); 305 | 306 | // Close any existing PRs 307 | for (const pr of existingPRs) { 308 | console.log(`Closing existing PR #${pr.number}...`); 309 | await octokit.pulls.update({ 310 | owner: event.repository.owner.login, 311 | repo: event.repository.name, 312 | pull_number: pr.number, 313 | state: 'closed' 314 | }); 315 | } 316 | 317 | console.log('Branch exists, deleting it...'); 318 | await octokit.git.deleteRef({ 319 | owner: event.repository.owner.login, 320 | repo: event.repository.name, 321 | ref: `heads/${branchName}` 322 | }); 323 | console.log('Existing branch deleted'); 324 | } 325 | } catch (error) { 326 | // 404 means branch doesn't exist, which is fine 327 | if (error.status !== 404) { 328 | throw error; 329 | } 330 | console.log('Branch does not exist, proceeding with creation'); 331 | } 332 | 333 | // Get main branch ref 334 | const { data: mainRef } = await octokit.git.getRef({ 335 | owner: event.repository.owner.login, 336 | repo: event.repository.name, 337 | ref: 'heads/main' 338 | }); 339 | 340 | // Create new branch 341 | await octokit.git.createRef({ 342 | owner: event.repository.owner.login, 343 | repo: event.repository.name, 344 | ref: `refs/heads/${branchName}`, 345 | sha: mainRef.object.sha 346 | }); 347 | 348 | // Get current files from the new branch 349 | console.log('Getting current file SHAs...'); 350 | const { data: currentReadme } = await octokit.repos.getContent({ 351 | owner: event.repository.owner.login, 352 | repo: event.repository.name, 353 | path: 'README.md', 354 | ref: branchName 355 | }); 356 | 357 | const { data: currentCsv } = await octokit.repos.getContent({ 358 | owner: event.repository.owner.login, 359 | repo: event.repository.name, 360 | path: 'prompts.csv', 361 | ref: branchName 362 | }); 363 | 364 | // Update files with correct author 365 | console.log('Updating README.md...'); 366 | await octokit.repos.createOrUpdateFileContents({ 367 | owner: event.repository.owner.login, 368 | repo: event.repository.name, 369 | path: 'README.md', 370 | message: promptsArray.length === 1 371 | ? `feat: Add "${promptsArray[0].actName}" to README` 372 | : `feat: Add multiple prompts to README`, 373 | content: Buffer.from(readmeContent).toString('base64'), 374 | branch: branchName, 375 | sha: currentReadme.sha, 376 | committer: { 377 | name: pr.user.login, 378 | email: `${pr.user.login}@users.noreply.github.com` 379 | }, 380 | author: { 381 | name: pr.user.login, 382 | email: `${pr.user.login}@users.noreply.github.com` 383 | } 384 | }); 385 | 386 | console.log('Updating prompts.csv...'); 387 | await octokit.repos.createOrUpdateFileContents({ 388 | owner: event.repository.owner.login, 389 | repo: event.repository.name, 390 | path: 'prompts.csv', 391 | message: promptsArray.length === 1 392 | ? `feat: Add "${promptsArray[0].actName}" to prompts.csv` 393 | : `feat: Add multiple prompts to prompts.csv`, 394 | content: Buffer.from(csvContent).toString('base64'), 395 | branch: branchName, 396 | sha: currentCsv.sha, 397 | committer: { 398 | name: pr.user.login, 399 | email: `${pr.user.login}@users.noreply.github.com` 400 | }, 401 | author: { 402 | name: pr.user.login, 403 | email: `${pr.user.login}@users.noreply.github.com` 404 | } 405 | }); 406 | 407 | // Create new PR 408 | const prTitle = promptsArray.length === 1 409 | ? `feat: Add "${promptsArray[0].actName}"` 410 | : `feat: Add multiple prompts (${promptsArray.map(p => `"${p.actName}"`).join(', ')})`; 411 | 412 | const prBody = promptsArray.length === 1 413 | ? `This PR supersedes #${issueNumber} with proper formatting. Original PR by @${pr.user.login}. Added "${promptsArray[0].actName}" to README.md and prompts.csv, preserving original attribution.` 414 | : `This PR supersedes #${issueNumber} with proper formatting. Original PR by @${pr.user.login}.\n\nAdded the following prompts:\n${promptsArray.map(p => `- "${p.actName}"`).join('\n')}\n\nAll prompts have been added to README.md and prompts.csv, preserving original attribution.`; 415 | 416 | const { data: newPr } = await octokit.pulls.create({ 417 | owner: event.repository.owner.login, 418 | repo: event.repository.name, 419 | title: prTitle, 420 | head: branchName, 421 | base: 'main', 422 | body: prBody 423 | }); 424 | 425 | // Comment on original PR 426 | await octokit.issues.createComment({ 427 | owner: event.repository.owner.login, 428 | repo: event.repository.name, 429 | issue_number: issueNumber, 430 | body: `I've created a new PR #${newPr.number} with your contribution properly formatted. This PR will be closed in favor of the new one.` 431 | }); 432 | 433 | // Close original PR 434 | await octokit.pulls.update({ 435 | owner: event.repository.owner.login, 436 | repo: event.repository.name, 437 | pull_number: issueNumber, 438 | state: 'closed' 439 | }); 440 | 441 | console.log(`Created new PR #${newPr.number} and closed original PR #${issueNumber}`); 442 | 443 | } catch (error) { 444 | console.error('Error details:', error); 445 | await octokit.issues.createComment({ 446 | owner: event.repository.owner.login, 447 | repo: event.repository.name, 448 | issue_number: issueNumber, 449 | body: `❌ Error while trying to create new PR:\n\`\`\`\n${error.message}\n\`\`\`` 450 | }); 451 | } 452 | return; 453 | } 454 | 455 | // Handle rename command specifically 456 | if (aiCommand.startsWith('rename') || aiCommand === 'suggest title') { 457 | if (!isPullRequest) { 458 | await octokit.issues.createComment({ 459 | owner: event.repository.owner.login, 460 | repo: event.repository.name, 461 | issue_number: issueNumber, 462 | body: '❌ The rename command can only be used on pull requests.' 463 | }); 464 | return; 465 | } 466 | 467 | // Get PR details for context 468 | const { data: pr } = await octokit.pulls.get({ 469 | owner: event.repository.owner.login, 470 | repo: event.repository.name, 471 | pull_number: issueNumber 472 | }); 473 | 474 | // Get the list of files changed in the PR 475 | const { data: files } = await octokit.pulls.listFiles({ 476 | owner: event.repository.owner.login, 477 | repo: event.repository.name, 478 | pull_number: issueNumber 479 | }); 480 | 481 | // Process file changes 482 | const fileChanges = await Promise.all(files.map(async file => { 483 | if (file.status === 'removed') { 484 | return `Deleted: ${file.filename}`; 485 | } 486 | 487 | // Get file content for added or modified files 488 | if (file.status === 'added' || file.status === 'modified') { 489 | const patch = file.patch || ''; 490 | return `${file.status === 'added' ? 'Added' : 'Modified'}: ${file.filename}\nChanges:\n${patch}`; 491 | } 492 | 493 | return `${file.status}: ${file.filename}`; 494 | })); 495 | 496 | const completion = await openai.chat.completions.create({ 497 | model: "gpt-3.5-turbo", 498 | messages: [ 499 | { 500 | role: "system", 501 | content: "You are a helpful assistant that generates clear and concise pull request titles. Follow these rules:\n1. Use conventional commit style (feat:, fix:, docs:, etc.)\n2. Focus on WHAT changed, not HOW or WHERE\n3. Keep it short and meaningful\n4. Don't mention file names or technical implementation details\n5. Return ONLY the new title, nothing else\n\nGood examples:\n- feat: Add \"Act as a Career Coach\"\n- fix: Correct typo in Linux Terminal prompt\n- docs: Update installation instructions\n- refactor: Improve error handling" 502 | }, 503 | { 504 | role: "user", 505 | content: `Based on these file changes, generate a concise PR title:\n\n${fileChanges.join('\n\n')}` 506 | } 507 | ], 508 | temperature: 0.5, 509 | max_tokens: 60 510 | }); 511 | 512 | const newTitle = completion.choices[0].message.content.trim(); 513 | 514 | // Update PR title 515 | await octokit.pulls.update({ 516 | owner: event.repository.owner.login, 517 | repo: event.repository.name, 518 | pull_number: issueNumber, 519 | title: newTitle 520 | }); 521 | 522 | // Add comment about the rename 523 | await octokit.issues.createComment({ 524 | owner: event.repository.owner.login, 525 | repo: event.repository.name, 526 | issue_number: issueNumber, 527 | body: `✨ Updated PR title to: "${newTitle}"\n\nBased on the following changes:\n\`\`\`diff\n${fileChanges.join('\n')}\n\`\`\`` 528 | }); 529 | return; 530 | } 531 | 532 | // Handle other commands 533 | const completion = await openai.chat.completions.create({ 534 | model: "gpt-3.5-turbo", 535 | messages: [ 536 | { 537 | role: "system", 538 | content: "You are a helpful AI assistant that helps with GitHub repositories. You can suggest code changes, fix issues, and improve code quality." 539 | }, 540 | { 541 | role: "user", 542 | content: aiCommand 543 | } 544 | ], 545 | temperature: 0.7, 546 | max_tokens: 2000 547 | }); 548 | 549 | const response = completion.choices[0].message.content; 550 | 551 | // If response contains code changes, create a new branch and PR 552 | if (response.includes('```')) { 553 | const branchName = `ai-bot/fix-${issueNumber}`; 554 | 555 | // Create new branch 556 | const defaultBranch = event.repository.default_branch; 557 | const ref = await octokit.git.getRef({ 558 | owner: event.repository.owner.login, 559 | repo: event.repository.name, 560 | ref: `heads/${defaultBranch}` 561 | }); 562 | 563 | await octokit.git.createRef({ 564 | owner: event.repository.owner.login, 565 | repo: event.repository.name, 566 | ref: `refs/heads/${branchName}`, 567 | sha: ref.data.object.sha 568 | }); 569 | 570 | // Extract code changes and file paths from response 571 | const codeBlocks = response.match(/```[\s\S]*?```/g); 572 | for (const block of codeBlocks) { 573 | const [_, filePath, ...codeLines] = block.split('\n'); 574 | const content = Buffer.from(codeLines.join('\n')).toString('base64'); 575 | 576 | await octokit.repos.createOrUpdateFileContents({ 577 | owner: event.repository.owner.login, 578 | repo: event.repository.name, 579 | path: filePath, 580 | message: `AI Bot: Apply suggested changes for #${issueNumber}`, 581 | content, 582 | branch: branchName 583 | }); 584 | } 585 | 586 | // Create PR 587 | await octokit.pulls.create({ 588 | owner: event.repository.owner.login, 589 | repo: event.repository.name, 590 | title: `AI Bot: Fix for #${issueNumber}`, 591 | body: `This PR was automatically generated in response to #${issueNumber}\n\nChanges proposed:\n${response}`, 592 | head: branchName, 593 | base: defaultBranch 594 | }); 595 | } 596 | 597 | // Add comment with response 598 | await octokit.issues.createComment({ 599 | owner: event.repository.owner.login, 600 | repo: event.repository.name, 601 | issue_number: issueNumber, 602 | body: `AI Bot Response:\n\n${response}` 603 | }); 604 | } 605 | 606 | main().catch(error => { 607 | console.error('Error:', error); 608 | process.exit(1); 609 | }); 610 | EOF 611 | 612 | - name: Handle errors 613 | if: failure() 614 | uses: actions/github-script@v6 615 | with: 616 | script: | 617 | const issueNumber = context.issue.number || context.payload.pull_request.number; 618 | await github.rest.issues.createComment({ 619 | owner: context.repo.owner, 620 | repo: context.repo.repo, 621 | issue_number: issueNumber, 622 | body: '❌ Sorry, there was an error processing your command. Please try again or contact the repository maintainers.' 623 | }); 624 | -------------------------------------------------------------------------------- /.github/workflows/auto_commands.yml: -------------------------------------------------------------------------------- 1 | name: Auto AI Commands 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, synchronize] 6 | pull_request_target: 7 | types: [opened, reopened, synchronize] 8 | 9 | jobs: 10 | check-and-comment: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | pull-requests: write 14 | issues: write 15 | 16 | steps: 17 | - name: Check PR status and comment 18 | uses: actions/github-script@v6 19 | with: 20 | script: | 21 | const pr = context.payload.pull_request; 22 | 23 | // Check if PR has conflicts 24 | if (pr.mergeable === false) { 25 | console.log('PR has conflicts, commenting /ai resolve'); 26 | await github.rest.issues.createComment({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | issue_number: pr.number, 30 | body: '/ai resolve' 31 | }); 32 | } 33 | 34 | // Check if PR title starts with "updated" 35 | if (pr.title.toLowerCase().startsWith('updated')) { 36 | console.log('PR title starts with "updated", commenting /ai suggest title'); 37 | await github.rest.issues.createComment({ 38 | owner: context.repo.owner, 39 | repo: context.repo.repo, 40 | issue_number: pr.number, 41 | body: '/ai suggest title' 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/csv_linter.yml: -------------------------------------------------------------------------------- 1 | name: CSV Linter and Trailing Whitespaces 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | lint_and_check_trailing_whitespaces: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: "3.8" 17 | 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | python -m pip install csvkit 22 | 23 | - name: Validate CSV structure 24 | run: | 25 | echo "Checking CSV structure..." 26 | if ! csvclean -n prompts.csv 2>&1 | tee /tmp/csv_errors.log; then 27 | echo "::error::CSV validation failed" 28 | cat /tmp/csv_errors.log 29 | exit 1 30 | fi 31 | 32 | - name: Check CSV format 33 | run: | 34 | echo "Checking CSV format..." 35 | if ! python -c ' 36 | import csv 37 | with open("prompts.csv", "r", encoding="utf-8") as f: 38 | reader = csv.reader(f) 39 | headers = next(reader) 40 | if headers != ["act", "prompt"]: 41 | print("Error: CSV headers must be exactly [act, prompt]") 42 | exit(1) 43 | for row_num, row in enumerate(reader, 2): 44 | if len(row) != 2: 45 | print(f"Error: Row {row_num} has {len(row)} columns, expected 2") 46 | exit(1) 47 | if not row[0] or not row[1]: 48 | print(f"Error: Row {row_num} has empty values") 49 | exit(1) 50 | '; then 51 | echo "::error::CSV format check failed" 52 | exit 1 53 | fi 54 | 55 | - name: Check Trailing Whitespaces 56 | run: | 57 | echo "Checking for trailing whitespaces..." 58 | if grep -q "[[:space:]]$" prompts.csv; then 59 | echo "::error::Found trailing whitespaces in prompts.csv" 60 | grep -n "[[:space:]]$" prompts.csv | while read -r line; do 61 | echo "Line with trailing whitespace: $line" 62 | done 63 | exit 1 64 | fi 65 | echo "No trailing whitespaces found" 66 | 67 | - name: Check for UTF-8 BOM and line endings 68 | run: | 69 | echo "Checking for UTF-8 BOM and line endings..." 70 | if file prompts.csv | grep -q "with BOM"; then 71 | echo "::error::File contains UTF-8 BOM marker" 72 | exit 1 73 | fi 74 | if file prompts.csv | grep -q "CRLF"; then 75 | echo "::error::File contains Windows-style (CRLF) line endings" 76 | exit 1 77 | fi 78 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: "3.2" 20 | bundler-cache: true # This will cache dependencies 21 | 22 | - name: Update Bundler 23 | run: | 24 | gem update --system 25 | bundle update --bundler 26 | 27 | - name: Install dependencies 28 | run: | 29 | rm -f Gemfile.lock # Remove existing lockfile 30 | bundle install 31 | 32 | - name: Build site 33 | run: bundle exec jekyll build 34 | 35 | - name: Deploy to GitHub Pages 36 | uses: peaceiris/actions-gh-pages@v3 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: ./_site 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | Gemfile.lock 3 | .DS_Store 4 | /.idea 5 | -------------------------------------------------------------------------------- /.windsurfrules: -------------------------------------------------------------------------------- 1 | .cursorrules -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | prompts.chat -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Before contributing to this repository, please ensure you are adhering to the 4 | following general guidelines. Further, if you are submitting a new prompt to the 5 | repository, be sure you are also following the prompt-specific guidelines. These 6 | checks will ensure that your contributions can be easily integrated into the 7 | main repository, without any headache for the owners. 8 | 9 | ## General Guidelines 10 | 11 | The following guidelines should be followed when making any open-source 12 | contributions: 13 | 14 | - [ ] Contributions should be made via a pull request to the main repository 15 | from a personal fork. 16 | - [ ] Pull requests should be accompanied by a descriptive title and detailed 17 | explanation. 18 | - [ ] Submit all pull requests to the repository's main branch. 19 | - [ ] Before submitting a pull request, ensure additions/edits are aligned with 20 | the overall repo organization. 21 | - [ ] Be sure changes are compatible with the repository's license. 22 | - [ ] In case of conflicts, provide helpful explanations regarding your proposed 23 | changes so that they can be approved by repo owners. 24 | 25 | ## New Prompt Guidelines 26 | 27 | To add a new prompt to this repository, a contributor should take the following 28 | steps (in their personal fork): 29 | 30 | 1. Create and test the new prompt. 31 | - See the 32 | [README](https://github.com/f/awesome-chatgpt-prompts/blob/main/README.md) 33 | for guidance on how to write effective prompts. 34 | - Ensure prompts generate intended results and can be used by other users to 35 | replicate those results. 36 | 2. Add the prompt to `README.md` using the following markdown template: 37 | 38 | `## Prompt Title` 39 | 40 | `Contributed by: [@github_username](https://github.com/github_profile)` 41 | 42 | `> prompt content` 43 | 44 | - Note: If your prompt was generated by ChatGPT, append 45 | `Generated by ChatGPT` to the "Contributed by" line. 46 | 3. Add the prompt to `prompts.csv`. 47 | - Put the prompt title in the `act` column, and the prompt itself in the 48 | `prompt` column. 49 | 4. Submit a pull request on the repository's main branch. 50 | - If possible, provide some documentation of how you tested your prompt and 51 | the kinds of results you received. 52 | - Be sure to include a detailed title and description. 53 | 54 | ### New Prompt Checklist: 55 | 56 | - [ ] I've confirmed the prompt works well 57 | - [ ] I've added 58 | `Contributed by: [@yourusername](https://github.com/yourusername)` 59 | - [ ] I've added to the README.md 60 | - [ ] I've added to the `prompts.csv` 61 | - [ ] Escaped quotes by double-quoting them 62 | - [ ] No spaces after commas after double quotes. e.g. `"act","prompt"` not 63 | `"act", "prompt"` 64 | - [ ] Removed "Act as" from the title on CSV 65 | 66 | Please ensure these requirements are met before submitting a pull request. 67 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "jekyll" 4 | gem "github-pages", group: :jekyll_plugins -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | name: prompts.chat 2 | title: prompts.chat 3 | subtitle: World's First & Most Famous Prompts Directory 4 | 5 | github_pages: 6 | url: "https://prompts.chat" 7 | branch: "main" 8 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |{{ page.subtitle | default: site.subtitle }} 38 | {% if page.subpage != true %} 39 | 40 | New: Try Vibe Coding Mode! 41 | 42 | {% endif %} 43 |
44 | {% if page.hide_platform_selector != true %} 45 |Choose your AI platform
47 |502 | Share your creative prompts with the community! Submit a pull request to add your prompts to the collection. 503 |
504 | Contribute Now 505 | 506 | `; 507 | promptsGrid.appendChild(contributeCard); 508 | 509 | // Fetch prompts.csv to get for_devs information 510 | fetch("/prompts.csv") 511 | .then((response) => response.text()) 512 | .then((csvText) => { 513 | const prompts = parseCSV(csvText); 514 | const isDevMode = document.getElementById("audienceSelect").value === "developers"; 515 | 516 | const promptElements = document.querySelectorAll( 517 | "h2[id^=act] + p + blockquote" 518 | ); 519 | 520 | promptElements.forEach((blockquote) => { 521 | const title = 522 | blockquote.previousElementSibling.previousElementSibling.textContent.trim(); 523 | const content = blockquote.textContent.trim(); 524 | 525 | // Find matching prompt in CSV 526 | const matchingPrompt = prompts.find((p) => { 527 | const csvTitle = p.act 528 | .replace(/\s+/g, " ") 529 | .replace(/[\n\r]/g, "") 530 | .trim(); 531 | const elementTitle = title 532 | .replace(/\s+/g, " ") 533 | .replace(/[\n\r]/g, "") 534 | .trim(); 535 | return ( 536 | csvTitle.toLowerCase() === elementTitle.toLowerCase() || 537 | csvTitle.toLowerCase().includes(elementTitle.toLowerCase()) || 538 | elementTitle.toLowerCase().includes(csvTitle.toLowerCase()) 539 | ); 540 | }); 541 | 542 | // Extract contributor from the paragraph element 543 | const contributorParagraph = blockquote.previousElementSibling; 544 | const contributorText = contributorParagraph.textContent; 545 | let contributor = null; 546 | 547 | // Try different contributor formats 548 | const formats = [ 549 | /Contributed by: \[([^\]]+)\]/i, 550 | /Contributed by \[([^\]]+)\]/i, 551 | /Contributed by: @([^\s]+)/i, 552 | /Contributed by @([^\s]+)/i, 553 | /Contributed by: \[@([^\]]+)\]/i, 554 | /Contributed by \[@([^\]]+)\]/i, 555 | ]; 556 | 557 | for (const format of formats) { 558 | const match = contributorText.match(format); 559 | if (match) { 560 | contributor = match[1]; 561 | // Remove @ if it exists at the start 562 | contributor = contributor.replace(/^@/, ""); 563 | break; 564 | } 565 | } 566 | 567 | // Set default contributor to 'f' if none found 568 | if (!contributor) { 569 | contributor = "f"; 570 | } 571 | 572 | const card = document.createElement("div"); 573 | card.className = "prompt-card"; 574 | 575 | // Set initial visibility based on dev mode 576 | if (isDevMode && (!matchingPrompt || !matchingPrompt.for_devs)) { 577 | card.style.display = "none"; 578 | } 579 | 580 | card.innerHTML = ` 581 |${updatePromptPreview(content)}
615 | @${contributor} 616 | `; 617 | 618 | // Add click event for showing modal 619 | card.addEventListener("click", (e) => { 620 | if ( 621 | !e.target.closest(".copy-button") && 622 | !e.target.closest(".contributor-badge") && 623 | !e.target.closest(".yaml-button") 624 | ) { 625 | showModal(title, content); 626 | } 627 | }); 628 | 629 | const copyButton = card.querySelector(".copy-button"); 630 | copyButton.addEventListener("click", async (e) => { 631 | e.stopPropagation(); 632 | try { 633 | await navigator.clipboard.writeText(updatePromptPreview(content)); 634 | copyButton.innerHTML = ` 635 | 638 | `; 639 | setTimeout(() => { 640 | copyButton.innerHTML = ` 641 | 645 | `; 646 | }, 2000); 647 | } catch (err) { 648 | alert("Failed to copy prompt to clipboard"); 649 | } 650 | }); 651 | 652 | promptsGrid.appendChild(card); 653 | }); 654 | 655 | container.innerHTML = ""; 656 | container.appendChild(promptsGrid); 657 | 658 | // Initialize modal event listeners 659 | initializeModalListeners(); 660 | }) 661 | .catch((error) => { 662 | console.error("Error loading prompts:", error); 663 | }); 664 | } 665 | 666 | function initializeModalListeners() { 667 | const modalOverlay = document.getElementById("modalOverlay"); 668 | const modalClose = document.querySelector(".modal-close"); 669 | 670 | if (!modalOverlay || !modalClose) return; 671 | 672 | modalClose.addEventListener("click", hideModal); 673 | modalOverlay.addEventListener("click", (e) => { 674 | if (e.target === modalOverlay) { 675 | hideModal(); 676 | } 677 | }); 678 | } 679 | 680 | // Add global event listener for Escape key 681 | document.addEventListener("keydown", (e) => { 682 | if (e.key === "Escape") { 683 | hideModal(); 684 | 685 | // Also hide YAML modal if it exists 686 | const yamlModal = document.getElementById("yamlModalOverlay"); 687 | if (yamlModal) { 688 | yamlModal.style.display = "none"; 689 | document.body.style.overflow = ""; 690 | yamlModal.remove(); 691 | } 692 | } 693 | }); 694 | 695 | function createModal() { 696 | const modalHTML = ` 697 | 736 | `; 737 | document.body.insertAdjacentHTML("beforeend", modalHTML); 738 | initializeModalListeners(); 739 | } 740 | 741 | // Modify the existing showModal function 742 | function showModal(title, content) { 743 | let modalOverlay = document.getElementById("modalOverlay"); 744 | if (!modalOverlay) { 745 | createModal(); 746 | modalOverlay = document.getElementById("modalOverlay"); 747 | } 748 | 749 | const modalTitle = modalOverlay.querySelector(".modal-title"); 750 | const modalContent = modalOverlay.querySelector(".modal-content"); 751 | 752 | // Extract variables from content 753 | const variables = extractVariables(content); 754 | 755 | // Create variable inputs container if variables exist 756 | if (variables.length > 0) { 757 | const variableContainer = document.createElement("div"); 758 | variableContainer.className = "variable-container"; 759 | 760 | const form = createVariableInputs(variables, variableContainer); 761 | 762 | // Initialize the modal content with updated prompt preview if variables exist 763 | const previewText = updatePromptPreview(content, form); 764 | modalContent.innerHTML = previewText; 765 | 766 | // Add event listeners for real-time updates 767 | form.addEventListener("input", () => { 768 | const previewText = updatePromptPreview(content, form); 769 | modalContent.innerHTML = previewText; 770 | 771 | // Update chat button data 772 | const modalChatButton = modalOverlay.querySelector(".modal-chat-button"); 773 | if (modalChatButton) { 774 | modalChatButton.dataset.content = previewText; 775 | } 776 | }); 777 | 778 | // Insert variable container before content 779 | modalContent.parentElement.insertBefore(variableContainer, modalContent); 780 | } else { 781 | modalTitle.textContent = title; 782 | modalContent.textContent = content; 783 | } 784 | 785 | const modalCopyButton = modalOverlay.querySelector(".modal-copy-button"); 786 | const modalContributor = modalOverlay.querySelector(".modal-contributor"); 787 | const modalChatButton = modalOverlay.querySelector(".modal-chat-button"); 788 | 789 | if (!modalTitle || !modalContent) return; 790 | 791 | // Update chat button text with platform name and handle visibility 792 | const platform = document.querySelector(".platform-tag.active"); 793 | const isDevMode = document.getElementById("audienceSelect").value === "developers"; 794 | 795 | if (platform) { 796 | const shouldHideChat = ["gemini", "llama"].includes( 797 | platform.dataset.platform 798 | ); 799 | modalChatButton.style.display = shouldHideChat ? "none" : "flex"; 800 | 801 | if (!shouldHideChat) { 802 | const chatIcon = modalChatButton.querySelector(".chat-icon"); 803 | const terminalIcon = modalChatButton.querySelector(".terminal-icon"); 804 | 805 | if (chatIcon && terminalIcon) { 806 | chatIcon.style.display = isDevMode ? "none" : "block"; 807 | terminalIcon.style.display = isDevMode ? "block" : "none"; 808 | } 809 | 810 | modalChatButton.innerHTML = ` 811 | 816 | 822 | Chat with ${platform.textContent} 823 | `; 824 | } 825 | } 826 | 827 | // Store content for chat button 828 | modalChatButton.dataset.content = modalContent.textContent; 829 | 830 | // Find the contributor for this prompt 831 | const promptCard = Array.from(document.querySelectorAll(".prompt-card")).find( 832 | (card) => 833 | card.querySelector(".prompt-title").textContent.trim() === title.trim() 834 | ); 835 | 836 | if (promptCard) { 837 | const contributorBadge = promptCard.querySelector(".contributor-badge"); 838 | if (contributorBadge) { 839 | modalContributor.href = contributorBadge.href; 840 | modalContributor.textContent = `Contributed by ${contributorBadge.textContent}`; 841 | } 842 | } 843 | 844 | // Add copy functionality 845 | modalCopyButton.addEventListener("click", async () => { 846 | try { 847 | copyPrompt(modalCopyButton, modalContent.textContent); 848 | modalCopyButton.innerHTML = ` 849 | 852 | `; 853 | setTimeout(() => { 854 | modalCopyButton.innerHTML = ` 855 | 859 | `; 860 | }, 2000); 861 | } catch (err) { 862 | alert("Failed to copy prompt to clipboard"); 863 | } 864 | }); 865 | 866 | modalOverlay.style.display = "block"; 867 | document.body.style.overflow = "hidden"; 868 | } 869 | 870 | function hideModal() { 871 | const modalOverlay = document.getElementById("modalOverlay"); 872 | if (!modalOverlay) return; 873 | 874 | modalOverlay.style.display = "none"; 875 | document.body.style.overflow = ""; 876 | 877 | // Optional: Remove modal from DOM when hidden 878 | modalOverlay.remove(); 879 | } 880 | 881 | let selectedPlatform = 882 | localStorage.getItem("selected-platform") || "github-copilot"; // Get from localStorage or default to github 883 | 884 | // Platform toggle functionality 885 | document.querySelectorAll(".platform-tag").forEach((button) => { 886 | button.addEventListener("click", () => { 887 | document 888 | .querySelectorAll(".platform-tag") 889 | .forEach((btn) => btn.classList.remove("active")); 890 | button.classList.add("active"); 891 | selectedPlatform = button.dataset.platform; 892 | localStorage.setItem("selected-platform", selectedPlatform); 893 | 894 | // Hide/show chat buttons based on platform 895 | const chatButtons = document.querySelectorAll( 896 | ".chat-button, .modal-chat-button" 897 | ); 898 | const shouldHideChat = ["gemini", "llama"].includes(selectedPlatform); 899 | chatButtons.forEach((btn) => { 900 | btn.style.display = shouldHideChat ? "none" : "flex"; 901 | }); 902 | 903 | // Auto-select technical tone and developers audience for GitHub Copilot 904 | if (selectedPlatform === "github-copilot") { 905 | const toneSelect = document.getElementById('toneSelect'); 906 | const audienceSelect = document.getElementById('audienceSelect'); 907 | 908 | // Set tone to technical 909 | toneSelect.value = 'technical'; 910 | localStorage.setItem('selected-tone', 'technical'); 911 | 912 | // Set audience to developers 913 | audienceSelect.value = 'developers'; 914 | localStorage.setItem('audience', 'developers'); 915 | 916 | // Update dev mode class on body 917 | document.body.classList.add('dev-mode'); 918 | 919 | // Update chat button icons 920 | updateChatButtonIcons(true); 921 | 922 | // Trigger prompt filtering for dev mode 923 | filterPrompts(); 924 | } 925 | }); 926 | }); 927 | 928 | // Set active platform from localStorage and handle initial button visibility 929 | const platformToActivate = 930 | document.querySelector(`[data-platform="${selectedPlatform}"]`) || 931 | document.querySelector('[data-platform="github-copilot"]'); 932 | platformToActivate.classList.add("active"); 933 | 934 | // Set initial chat button visibility 935 | const shouldHideChat = ["gemini", "llama"].includes(selectedPlatform); 936 | document.querySelectorAll(".chat-button, .modal-chat-button").forEach((btn) => { 937 | btn.style.display = shouldHideChat ? "none" : "flex"; 938 | }); 939 | 940 | // Function to open prompt in selected AI chat platform 941 | function openInChat(button, encodedPrompt) { 942 | const promptText = buildPrompt(encodedPrompt); 943 | const platform = document.querySelector(".platform-tag.active"); 944 | 945 | if (!platform) return; 946 | 947 | const baseUrl = platform.dataset.url; 948 | console.log(baseUrl); 949 | let url; 950 | 951 | switch (platform.dataset.platform) { 952 | case "github-copilot": 953 | url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`; 954 | break; 955 | case "chatgpt": 956 | url = `${baseUrl}?prompt=${encodeURIComponent(promptText)}`; 957 | break; 958 | case "grok": 959 | url = `${baseUrl}&q=${encodeURIComponent(promptText)}`; 960 | break; 961 | case "claude": 962 | url = `${baseUrl}?q=${encodeURIComponent(promptText)}`; 963 | break; 964 | case "perplexity": 965 | url = `${baseUrl}/search?q=${encodeURIComponent(promptText)}`; 966 | break; 967 | case "mistral": 968 | url = `${baseUrl}?q=${encodeURIComponent(promptText)}`; 969 | break; 970 | default: 971 | url = `${baseUrl}?q=${encodeURIComponent(promptText)}`; 972 | } 973 | 974 | window.open(url, "_blank"); 975 | } 976 | 977 | function buildPrompt(encodedPrompt) { 978 | let promptText = decodeURIComponent(encodedPrompt); 979 | 980 | // If there's a modal open, use the current state of variables 981 | const modalContent = document.querySelector(".modal-content"); 982 | if (modalContent) { 983 | // Get all variable inputs 984 | const form = document.querySelector(".variable-form"); 985 | if (form) { 986 | const inputs = form.querySelectorAll(".variable-input"); 987 | inputs.forEach((input) => { 988 | const value = input.value.trim(); 989 | const variable = input.dataset.variable; 990 | const defaultValue = input.dataset.default; 991 | const pattern = new RegExp(`\\$\{${variable}[^}]*\}`, "g"); 992 | 993 | // Use value or default value 994 | const replacement = value || defaultValue || variable; 995 | promptText = promptText.replace(pattern, replacement); 996 | }); 997 | } 998 | } 999 | 1000 | // Clean up newlines and normalize whitespace 1001 | promptText = promptText.replace(/\s+/g, ' ').trim(); 1002 | 1003 | // Get language, tone and audience preferences 1004 | const languageSelect = document.getElementById('languageSelect'); 1005 | const customLanguage = document.getElementById('customLanguage'); 1006 | const toneSelect = document.getElementById('toneSelect'); 1007 | const customTone = document.getElementById('customTone'); 1008 | const audienceSelect = document.getElementById('audienceSelect'); 1009 | 1010 | const language = languageSelect.value === 'custom' ? customLanguage.value : languageSelect.value; 1011 | const tone = toneSelect.value === 'custom' ? customTone.value : toneSelect.value; 1012 | const audience = audienceSelect.value; 1013 | 1014 | // Append preferences as a new line 1015 | promptText += ` Reply in ${language} using ${tone} tone for ${audience}.`; 1016 | 1017 | return promptText; 1018 | } 1019 | 1020 | // Existing copy function 1021 | async function copyPrompt(button, encodedPrompt) { 1022 | try { 1023 | const promptText = buildPrompt(encodedPrompt); 1024 | await navigator.clipboard.writeText(promptText); 1025 | const originalHTML = button.innerHTML; 1026 | button.innerHTML = ` 1027 | 1030 | `; 1031 | setTimeout(() => { 1032 | button.innerHTML = originalHTML; 1033 | }, 2000); 1034 | } catch (err) { 1035 | console.error("Failed to copy text: ", err); 1036 | } 1037 | } 1038 | 1039 | // Function to handle chat button click in modal 1040 | function openModalChat() { 1041 | const modalContent = document.querySelector(".modal-content"); 1042 | if (modalContent) { 1043 | const content = modalContent.textContent; 1044 | openInChat(null, encodeURIComponent(content.trim())); 1045 | } 1046 | } 1047 | 1048 | // Add these functions before the closing script tag 1049 | function showCopilotSuggestion() { 1050 | const modal = document.getElementById("copilotSuggestionModal"); 1051 | const backdrop = document.querySelector(".copilot-suggestion-backdrop"); 1052 | if (modal) { 1053 | if (!backdrop) { 1054 | const backdropDiv = document.createElement("div"); 1055 | backdropDiv.className = "copilot-suggestion-backdrop"; 1056 | document.body.appendChild(backdropDiv); 1057 | } 1058 | modal.style.display = "block"; 1059 | backdrop.style.display = "block"; 1060 | document.body.style.overflow = "hidden"; 1061 | } 1062 | } 1063 | 1064 | function hideCopilotSuggestion(switchToCopilot) { 1065 | const modal = document.getElementById("copilotSuggestionModal"); 1066 | const backdrop = document.querySelector(".copilot-suggestion-backdrop"); 1067 | const doNotShowCheckbox = document.getElementById("doNotShowAgain"); 1068 | 1069 | if (doNotShowCheckbox && doNotShowCheckbox.checked) { 1070 | localStorage.setItem("copilot-suggestion-hidden", "true"); 1071 | } 1072 | 1073 | if (switchToCopilot) { 1074 | const copilotButton = document.querySelector( 1075 | '[data-platform="github-copilot"]' 1076 | ); 1077 | if (copilotButton) { 1078 | copilotButton.click(); 1079 | } 1080 | } 1081 | 1082 | if (modal) { 1083 | modal.style.display = "none"; 1084 | if (backdrop) { 1085 | backdrop.style.display = "none"; 1086 | } 1087 | document.body.style.overflow = ""; 1088 | } 1089 | } 1090 | 1091 | // Function to update chat button icons based on dev mode 1092 | function updateChatButtonIcons(isDevMode) { 1093 | document 1094 | .querySelectorAll(".chat-button, .modal-chat-button") 1095 | .forEach((button) => { 1096 | const chatIcon = button.querySelector(".chat-icon"); 1097 | const terminalIcon = button.querySelector(".terminal-icon"); 1098 | if (chatIcon && terminalIcon) { 1099 | chatIcon.style.display = isDevMode ? "none" : "block"; 1100 | terminalIcon.style.display = isDevMode ? "block" : "none"; 1101 | } 1102 | }); 1103 | } 1104 | 1105 | // Language and Tone Selection 1106 | function initializeLanguageAndTone() { 1107 | const languageSelect = document.getElementById('languageSelect'); 1108 | const customLanguage = document.getElementById('customLanguage'); 1109 | const toneSelect = document.getElementById('toneSelect'); 1110 | const customTone = document.getElementById('customTone'); 1111 | 1112 | // Load saved preferences 1113 | const savedLanguage = localStorage.getItem('selected-language'); 1114 | const savedCustomLanguage = localStorage.getItem('custom-language'); 1115 | const savedTone = localStorage.getItem('selected-tone'); 1116 | const savedCustomTone = localStorage.getItem('custom-tone'); 1117 | 1118 | if (savedLanguage) { 1119 | languageSelect.value = savedLanguage; 1120 | if (savedLanguage === 'custom' && savedCustomLanguage) { 1121 | customLanguage.value = savedCustomLanguage; 1122 | customLanguage.style.display = 'inline-block'; 1123 | } 1124 | } 1125 | 1126 | if (savedTone) { 1127 | toneSelect.value = savedTone; 1128 | if (savedTone === 'custom' && savedCustomTone) { 1129 | customTone.value = savedCustomTone; 1130 | customTone.style.display = 'inline-block'; 1131 | } 1132 | } 1133 | 1134 | // Language select handler 1135 | languageSelect.addEventListener('change', (e) => { 1136 | const isCustom = e.target.value === 'custom'; 1137 | customLanguage.style.display = isCustom ? 'inline-block' : 'none'; 1138 | localStorage.setItem('selected-language', e.target.value); 1139 | 1140 | if (!isCustom) { 1141 | customLanguage.value = ''; 1142 | localStorage.removeItem('custom-language'); 1143 | } 1144 | }); 1145 | 1146 | // Custom language input handler 1147 | customLanguage.addEventListener('input', (e) => { 1148 | localStorage.setItem('custom-language', e.target.value); 1149 | }); 1150 | 1151 | // Tone select handler 1152 | toneSelect.addEventListener('change', (e) => { 1153 | const isCustom = e.target.value === 'custom'; 1154 | customTone.style.display = isCustom ? 'inline-block' : 'none'; 1155 | localStorage.setItem('selected-tone', e.target.value); 1156 | 1157 | if (!isCustom) { 1158 | customTone.value = ''; 1159 | localStorage.removeItem('custom-tone'); 1160 | } 1161 | }); 1162 | 1163 | // Custom tone input handler 1164 | customTone.addEventListener('input', (e) => { 1165 | localStorage.setItem('custom-tone', e.target.value); 1166 | }); 1167 | } 1168 | 1169 | // Function to show a modal with YAML format and GitHub create button 1170 | function showYamlModal(event, title, content) { 1171 | event.stopPropagation(); 1172 | let modalOverlay = document.getElementById("yamlModalOverlay"); 1173 | if (!modalOverlay) { 1174 | const modalHTML = ` 1175 | 1221 | `; 1222 | document.body.insertAdjacentHTML("beforeend", modalHTML); 1223 | modalOverlay = document.getElementById("yamlModalOverlay"); 1224 | 1225 | // Add event listeners 1226 | const modalClose = modalOverlay.querySelector(".modal-close"); 1227 | modalClose.addEventListener("click", () => { 1228 | modalOverlay.style.display = "none"; 1229 | document.body.style.overflow = ""; 1230 | modalOverlay.remove(); 1231 | }); 1232 | 1233 | modalOverlay.addEventListener("click", (e) => { 1234 | if (e.target === modalOverlay) { 1235 | modalOverlay.style.display = "none"; 1236 | document.body.style.overflow = ""; 1237 | modalOverlay.remove(); 1238 | } 1239 | }); 1240 | } 1241 | 1242 | const yamlContent = modalOverlay.querySelector(".yaml-content"); 1243 | const modalCopyButton = modalOverlay.querySelector(".modal-copy-button"); 1244 | const createYamlButton = modalOverlay.querySelector(".create-yaml-button"); 1245 | 1246 | // Create the YAML content 1247 | const cleanTitle = decodeURIComponent(title).trim().replace(/^Act as a?n?\s*/ig, ''); 1248 | const cleanContent = decodeURIComponent(content).trim().replace(/\n+/g, ' '); 1249 | 1250 | // Convert variables from ${Variable: Default} format to {{Variable}} format 1251 | const convertedContent = cleanContent.replace(/\${([^:}]+)(?::[^}]*)?}/g, (match, varName) => { 1252 | return `{{${varName.trim()}}}`; 1253 | }); 1254 | 1255 | const yamlText = `name: ${cleanTitle} 1256 | model: gpt-4o-mini 1257 | modelParameters: 1258 | temperature: 0.5 1259 | messages: 1260 | - role: system 1261 | content: | 1262 | ${convertedContent.replace(/\n/g, '\n ')}`; 1263 | 1264 | // Apply basic syntax highlighting 1265 | const highlightedYaml = yamlText 1266 | .replace(/(name|model|modelParameters|temperature|messages|role|content):/g, '$1:') 1267 | .replace(/(gpt-4o-mini)/g, '$1') 1268 | .replace(/([0-9]\.?[0-9]*)/g, '$1') 1269 | .replace(/(true|false)/g, '$1') 1270 | .replace(/(\{\{[^}]+\}\})/g, '$1'); // Highlight the new variable format 1271 | 1272 | yamlContent.innerHTML = highlightedYaml; 1273 | 1274 | // Add copy functionality - use the plain text version for clipboard 1275 | modalCopyButton.addEventListener("click", async () => { 1276 | try { 1277 | await navigator.clipboard.writeText(yamlText); 1278 | modalCopyButton.innerHTML = ` 1279 | 1282 | `; 1283 | setTimeout(() => { 1284 | modalCopyButton.innerHTML = ` 1285 | 1289 | `; 1290 | }, 2000); 1291 | } catch (err) { 1292 | alert("Failed to copy YAML to clipboard"); 1293 | } 1294 | }); 1295 | 1296 | // Add create functionality 1297 | createYamlButton.addEventListener("click", () => { 1298 | const orgName = document.getElementById('github-org').value.trim(); 1299 | const repoName = document.getElementById('github-repo').value.trim(); 1300 | const branchName = document.getElementById('github-branch').value.trim(); 1301 | const filename = cleanTitle.toLocaleLowerCase().replace(/\s+/g, '-') + '.prompt.yml'; 1302 | const encodedYaml = encodeURIComponent(yamlText); 1303 | const githubUrl = `https://github.com/${orgName}/${repoName}/new/${branchName}?filename=${filename}&value=${encodedYaml}`; 1304 | window.open(githubUrl, '_blank'); 1305 | }); 1306 | 1307 | modalOverlay.style.display = "block"; 1308 | document.body.style.overflow = "hidden"; 1309 | } 1310 | -------------------------------------------------------------------------------- /scripts/find-prompt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | curl -s https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv | tail +2 | ruby -rcsv -e 'CSV.parse(STDIN.read) {|row| puts row.join("\t")}' | fzf -1 -q "$1" --with-nth 1 --delimiter "\t" --preview 'echo {2} | fold -s -w $(tput cols)' | cut -d" " -f2 4 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --bg-color-light: #ffffff; 3 | --bg-color-dark: #1a1a1a; 4 | --text-color-light: #000000; 5 | --text-color-dark: #ffffff; 6 | --header-height: 145px; 7 | --footer-height: 145px; 8 | --sidebar-width: 300px; 9 | --accent-color: #10b981; 10 | --accent-color-hover: #059669; 11 | --tech-badge-bg: #f3f4f6; 12 | --tech-badge-text: #4b5563; 13 | --tech-badge-bg-dark: #374151; 14 | --tech-badge-text-dark: #d1d5db; 15 | --background-color: var(--bg-color-light); 16 | --background-color-dark: var(--bg-color-dark); 17 | --border-color: #e1e4e8; 18 | --border-color-dark: #2d2d2d; 19 | } 20 | 21 | body { 22 | background-color: var(--bg-color-light); 23 | color: var(--text-color-light); 24 | margin: 0; 25 | padding: 0; 26 | overflow-y: hidden; 27 | } 28 | 29 | body.vibe { 30 | font-family: 'SF Mono', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | overflow-x: hidden; 34 | } 35 | 36 | @media (max-width: 768px) { 37 | body { 38 | overflow-y: auto !important; 39 | } 40 | } 41 | 42 | body.dark-mode { 43 | background-color: var(--bg-color-dark); 44 | color: var(--text-color-dark); 45 | } 46 | 47 | .dark-mode-toggle { 48 | background-color: transparent; 49 | border: none; 50 | color: var(--accent-color); 51 | cursor: pointer; 52 | padding: 6px; 53 | display: flex; 54 | align-items: center; 55 | justify-content: center; 56 | opacity: 0.8; 57 | transition: opacity 0.2s ease; 58 | } 59 | 60 | .dark-mode-toggle:hover { 61 | opacity: 1; 62 | } 63 | 64 | .mode-icon { 65 | width: 20px; 66 | height: 20px; 67 | stroke: currentColor; 68 | } 69 | 70 | .site-header { 71 | padding: 1rem 2rem; 72 | text-align: left; 73 | border-bottom: 1px solid #e1e4e8; 74 | background-color: var(--bg-color-light); 75 | position: sticky; 76 | top: 0; 77 | z-index: 100; 78 | display: flex; 79 | justify-content: space-between; 80 | align-items: center; 81 | height: var(--header-height); 82 | } 83 | 84 | .dark-mode .site-header { 85 | background-color: var(--bg-color-dark); 86 | border-bottom-color: #2d2d2d; 87 | } 88 | 89 | .header-left { 90 | display: flex; 91 | flex-direction: column; 92 | justify-content: center; 93 | gap: 0; 94 | } 95 | 96 | .header-right { 97 | display: flex; 98 | align-items: center; 99 | justify-content: center; 100 | gap: 12px; 101 | } 102 | 103 | .star-count { 104 | display: flex; 105 | align-items: center; 106 | gap: 4px; 107 | color: var(--accent-color); 108 | font-size: 0.9rem; 109 | opacity: 0.8; 110 | text-decoration: none; 111 | transition: opacity 0.2s ease; 112 | } 113 | 114 | .star-count:hover { 115 | opacity: 1; 116 | } 117 | 118 | .star-count svg { 119 | width: 16px; 120 | height: 16px; 121 | } 122 | 123 | .social-links { 124 | display: flex; 125 | align-items: center; 126 | justify-content: center; 127 | gap: 12px; 128 | margin-right: 8px; 129 | } 130 | 131 | .social-link { 132 | color: var(--accent-color); 133 | opacity: 0.8; 134 | transition: opacity 0.2s ease; 135 | display: flex; 136 | align-items: center; 137 | justify-content: center; 138 | } 139 | 140 | .social-link:hover { 141 | opacity: 1; 142 | } 143 | 144 | .social-link svg { 145 | width: 24px; 146 | height: 24px; 147 | } 148 | 149 | .techstack-badges { 150 | display: flex; 151 | flex-wrap: wrap; 152 | gap: 0.5rem; 153 | margin-right: 0.5rem; 154 | } 155 | 156 | .tech-badge { 157 | background: var(--tech-badge-bg); 158 | color: var(--tech-badge-text); 159 | padding: 0.1rem 0.2rem; 160 | border-radius: 0.25rem; 161 | font-size: 0.7rem; 162 | font-weight: 500; 163 | white-space: nowrap; 164 | } 165 | 166 | .dark-mode .tech-badge { 167 | background: var(--tech-badge-bg-dark); 168 | color: var(--tech-badge-text-dark); 169 | } 170 | 171 | .card-footer { 172 | display: flex; 173 | justify-content: space-between; 174 | align-items: center; 175 | position: absolute; 176 | bottom: 12px; 177 | left: 12px; 178 | right: 0; 179 | } 180 | 181 | .card-footer .contributor-badge { 182 | bottom: 0; 183 | } 184 | 185 | .site-footer { 186 | padding: 12px 20px; 187 | text-align: center; 188 | border-top: 1px solid #e1e4e8; 189 | background: var(--bg-color-light); 190 | position: fixed; 191 | bottom: 0; 192 | left: 0; 193 | right: 0; 194 | z-index: 100; 195 | height: var(--footer-height); 196 | } 197 | 198 | .dark-mode .site-footer { 199 | background: var(--bg-color-dark); 200 | border-color: #2d2d2d; 201 | } 202 | 203 | .footer-content { 204 | max-width: none; 205 | margin: 0; 206 | display: grid; 207 | grid-template-columns: repeat(4, 1fr); 208 | gap: 0 30px; 209 | text-align: left; 210 | font-size: 0.7rem; 211 | padding: 0 1rem; 212 | } 213 | 214 | .footer-section { 215 | display: flex; 216 | flex-direction: column; 217 | gap: 6px; 218 | } 219 | 220 | .footer-section h3 { 221 | font-size: 0.8rem; 222 | margin: 0; 223 | opacity: 0.8; 224 | } 225 | 226 | .dark-mode .footer-section h3 { 227 | color: var(--text-color-dark); 228 | } 229 | 230 | .footer-section p { 231 | font-size: inherit; 232 | margin: 0; 233 | line-height: 1.3; 234 | opacity: 0.6; 235 | color: var(--text-color-light); 236 | } 237 | 238 | .dark-mode .footer-section p { 239 | color: var(--text-color-dark); 240 | } 241 | 242 | .book-links { 243 | display: flex; 244 | flex-direction: column; 245 | gap: 4px; 246 | } 247 | 248 | .book-link svg { 249 | width: 14px; 250 | height: 14px; 251 | } 252 | 253 | /* Add padding to main content to prevent overlap with fixed footer */ 254 | .layout-wrapper { 255 | padding-bottom: 100px; 256 | } 257 | 258 | @media (max-width: 768px) { 259 | .footer-content { 260 | grid-template-columns: 1fr; 261 | gap: 12px; 262 | text-align: left; 263 | padding: 0 1rem; 264 | } 265 | 266 | .footer-section { 267 | text-align: left; 268 | } 269 | 270 | .footer-section p { 271 | text-align: left; 272 | } 273 | 274 | .book-links { 275 | align-items: flex-start; 276 | } 277 | 278 | .site-footer { 279 | position: static; 280 | text-align: left; 281 | } 282 | 283 | .layout-wrapper { 284 | padding-bottom: 0; 285 | } 286 | 287 | .site-description { 288 | display: none !important; 289 | } 290 | 291 | .site-header { 292 | padding: 0.75rem 1rem; 293 | flex-direction: column; 294 | align-items: stretch; 295 | gap: 8px; 296 | position: relative; 297 | } 298 | 299 | body.vibe .site-header { 300 | height: auto !important; 301 | } 302 | 303 | body.vibe .sidebar { 304 | display: none !important; 305 | } 306 | 307 | .header-right { 308 | position: absolute; 309 | top: 1.3rem; 310 | right: 0.75rem; 311 | gap: 8px; 312 | display: flex; 313 | align-items: center; 314 | } 315 | 316 | .star-count { 317 | display: flex; 318 | font-size: 0.8rem; 319 | } 320 | 321 | .star-count svg { 322 | width: 14px; 323 | height: 14px; 324 | } 325 | 326 | .dark-mode-toggle { 327 | padding: 4px; 328 | } 329 | 330 | .mode-icon { 331 | width: 18px; 332 | height: 18px; 333 | } 334 | 335 | .site-slogan { 336 | display: none; 337 | } 338 | 339 | .site-description { 340 | display: flex !important; 341 | margin-top: 4px; 342 | } 343 | 344 | .platform-hint { 345 | font-size: 0.7rem; 346 | } 347 | 348 | .platform-tag { 349 | padding: 1px 6px; 350 | font-size: 0.75rem; 351 | } 352 | 353 | .github-link { 354 | display: none; 355 | } 356 | 357 | .github-link span { 358 | display: none; 359 | } 360 | 361 | .search-container { 362 | padding-bottom: 0 !important; 363 | margin-bottom: 0 !important; 364 | } 365 | } 366 | 367 | .site-title { 368 | font-size: 1.8rem; 369 | font-weight: 700; 370 | margin: 0; 371 | background: linear-gradient(45deg, var(--accent-color), #3b82f6); 372 | -webkit-background-clip: text; 373 | background-clip: text; 374 | -webkit-text-fill-color: transparent; 375 | display: inline-block; 376 | position: relative; 377 | transition: transform 0.3s ease; 378 | } 379 | 380 | /* Blinking cursor animation for development mode */ 381 | @keyframes blink-cursor { 382 | 0%, 100% { opacity: 1; } 383 | 50% { opacity: 0; } 384 | } 385 | 386 | .site-slogan { 387 | font-size: 0.9rem; 388 | opacity: 0.7; 389 | margin: 0 0 0.8rem 0; 390 | line-height: 1; 391 | } 392 | 393 | @media (max-width: 768px) { 394 | .site-slogan { 395 | display: none; 396 | } 397 | 398 | .site-description { 399 | display: flex !important; 400 | margin-top: 4px; 401 | } 402 | 403 | .platform-hint { 404 | font-size: 0.7rem; 405 | } 406 | 407 | .platform-tag { 408 | padding: 1px 6px; 409 | font-size: 0.75rem; 410 | } 411 | 412 | .header-right { 413 | gap: 8px; 414 | } 415 | 416 | .mode-text { 417 | display: none; 418 | } 419 | 420 | .dark-mode-toggle { 421 | padding: 6px; 422 | } 423 | 424 | .github-link { 425 | padding: 6px; 426 | } 427 | 428 | .github-link span { 429 | display: none; 430 | } 431 | } 432 | 433 | .site-title:hover { 434 | transform: translateY(-2px); 435 | } 436 | 437 | .site-title::before { 438 | content: ''; 439 | position: absolute; 440 | width: 100%; 441 | height: 100%; 442 | background: linear-gradient(45deg, var(--accent-color), #3b82f6); 443 | filter: blur(20px); 444 | opacity: 0; 445 | transition: opacity 0.3s ease; 446 | z-index: -1; 447 | } 448 | 449 | .site-title:hover::before { 450 | opacity: 0.15; 451 | } 452 | 453 | .site-description { 454 | color: var(--text-color-light); 455 | margin: 0; 456 | font-size: 0.95rem; 457 | opacity: 0.8; 458 | display: flex; 459 | flex-direction: column; 460 | gap: 0.3rem; 461 | } 462 | 463 | .platform-hint { 464 | font-size: 0.75rem; 465 | opacity: 0.6; 466 | margin: 0; 467 | } 468 | 469 | .platform-pills { 470 | display: flex; 471 | gap: 0.5rem; 472 | align-items: center; 473 | flex-wrap: wrap; 474 | } 475 | 476 | .platform-tag { 477 | display: inline-flex; 478 | align-items: center; 479 | padding: 2px 8px; 480 | border-radius: 12px; 481 | font-size: 0.85rem; 482 | background: rgba(16, 185, 129, 0.1); 483 | color: var(--accent-color); 484 | border: 1px solid var(--accent-color); 485 | text-decoration: none; 486 | cursor: pointer; 487 | transition: all 0.2s ease; 488 | } 489 | 490 | .platform-tag:hover { 491 | background: rgba(16, 185, 129, 0.2); 492 | transform: translateY(-1px); 493 | } 494 | 495 | .platform-tag.active { 496 | background: var(--accent-color); 497 | color: white; 498 | transform: translateY(-1px); 499 | } 500 | 501 | .dark-mode .platform-tag { 502 | background: rgba(16, 185, 129, 0.2); 503 | } 504 | 505 | .dark-mode .platform-tag:hover { 506 | background: rgba(16, 185, 129, 0.25); 507 | } 508 | 509 | .dark-mode .platform-tag.active { 510 | background: var(--accent-color); 511 | } 512 | 513 | .dark-mode .site-description { 514 | color: var(--text-color-dark); 515 | } 516 | 517 | .action-buttons { 518 | display: flex; 519 | gap: 8px; 520 | align-items: center; 521 | } 522 | 523 | .chat-button { 524 | background: transparent; 525 | border: none; 526 | cursor: pointer; 527 | padding: 2px; 528 | color: var(--accent-color); 529 | opacity: 0.8; 530 | transition: opacity 0.2s ease; 531 | z-index: 2; 532 | flex-shrink: 0; 533 | } 534 | 535 | .chat-button:hover { 536 | opacity: 1; 537 | } 538 | 539 | .chat-button svg { 540 | width: 16px; 541 | height: 16px; 542 | } 543 | 544 | .yaml-button { 545 | background: transparent; 546 | border: none; 547 | cursor: pointer; 548 | padding: 2px; 549 | color: var(--accent-color); 550 | opacity: 0.8; 551 | transition: opacity 0.2s ease; 552 | z-index: 2; 553 | flex-shrink: 0; 554 | } 555 | 556 | .yaml-button:hover { 557 | opacity: 1; 558 | } 559 | 560 | .yaml-button svg { 561 | width: 16px; 562 | height: 16px; 563 | } 564 | 565 | .create-yaml-button { 566 | display: flex; 567 | align-items: center; 568 | justify-content: center; 569 | gap: 6px; 570 | background-color: var(--accent-color); 571 | color: white; 572 | border: none; 573 | padding: 6px 12px; 574 | border-radius: 4px; 575 | font-size: 13px; 576 | font-weight: 500; 577 | cursor: pointer; 578 | transition: all 0.2s ease; 579 | white-space: nowrap; 580 | flex-shrink: 0; 581 | } 582 | 583 | .create-yaml-button:hover { 584 | background-color: #0e9f71; 585 | } 586 | 587 | .create-yaml-button svg { 588 | width: 14px; 589 | height: 14px; 590 | } 591 | 592 | .dark-mode .create-yaml-button { 593 | background-color: var(--accent-color); 594 | color: white; 595 | } 596 | 597 | .dark-mode .create-yaml-button:hover { 598 | background-color: #0e9f71; 599 | } 600 | 601 | .yaml-content { 602 | background-color: #f5f5f5; 603 | padding: 12px; 604 | border-radius: 6px; 605 | color: #333; 606 | font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", monospace; 607 | font-size: 13px; 608 | line-height: 1.5; 609 | overflow: auto; 610 | white-space: pre-wrap; 611 | margin: 0; 612 | } 613 | 614 | .dark-mode .yaml-content { 615 | background-color: #2d2d2d; 616 | color: #f5f5f5; 617 | } 618 | 619 | /* YAML syntax highlighting */ 620 | .yaml-content .key { 621 | color: #0550ae; 622 | font-weight: 500; 623 | } 624 | 625 | .dark-mode .yaml-content .key { 626 | color: #4d9eee; 627 | } 628 | 629 | .yaml-content .string { 630 | color: #22863a; 631 | } 632 | 633 | .yaml-content .string .number { 634 | color: #56b366 !important; 635 | } 636 | 637 | .dark-mode .yaml-content .string { 638 | color: #56b366; 639 | } 640 | 641 | .yaml-content .number { 642 | color: #905; 643 | } 644 | 645 | .dark-mode .yaml-content .number { 646 | color: #e76086; 647 | } 648 | 649 | .yaml-content .boolean { 650 | color: #0550ae; 651 | font-weight: 500; 652 | } 653 | 654 | .dark-mode .yaml-content .boolean { 655 | color: #4d9eee; 656 | } 657 | 658 | /* Style title as code in the yml modal */ 659 | #yamlModalOverlay .modal-title { 660 | font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", monospace; 661 | font-size: 1.2rem; 662 | } 663 | 664 | .layout-wrapper { 665 | display: flex; 666 | flex-direction: column; 667 | min-height: 100vh; 668 | } 669 | 670 | .content-wrapper { 671 | display: flex; 672 | flex: 1; 673 | } 674 | 675 | .sidebar { 676 | width: var(--sidebar-width); 677 | padding: 20px; 678 | border-right: 1px solid #e1e4e8; 679 | height: calc(100vh - var(--header-height) - var(--footer-height)); 680 | overflow-y: auto; 681 | background-color: var(--bg-color-light); 682 | } 683 | 684 | .dark-mode .sidebar { 685 | background-color: var(--bg-color-dark); 686 | border-right-color: #2d2d2d; 687 | scrollbar-color: #2d2d2d transparent; 688 | } 689 | 690 | .main-content { 691 | flex: 1; 692 | height: calc(100vh - var(--header-height) - var(--footer-height)); 693 | overflow-y: auto; 694 | padding: 20px; 695 | padding-top: 0; 696 | position: relative; 697 | } 698 | 699 | .dark-mode .main-content { 700 | scrollbar-color: #2d2d2d transparent; 701 | } 702 | 703 | .main-content-header { 704 | position: sticky; 705 | top: 0; 706 | background-color: var(--bg-color-light); 707 | padding: 6px 38px; 708 | border-bottom: 1px solid #e1e4e8; 709 | z-index: 10; 710 | margin: -20px -20px 20px -20px; 711 | backdrop-filter: blur(8px); 712 | -webkit-backdrop-filter: blur(8px); 713 | } 714 | 715 | .dark-mode .main-content-header { 716 | background-color: var(--bg-color-dark); 717 | border-color: #2d2d2d; 718 | } 719 | 720 | .main-content-header .header-content { 721 | display: flex; 722 | align-items: center; 723 | justify-content: end; 724 | margin: 0 auto; 725 | gap: 16px; 726 | } 727 | 728 | .main-content-header .header-description { 729 | font-size: 0.85rem; 730 | color: var(--text-color-light); 731 | opacity: 0.8; 732 | white-space: nowrap; 733 | } 734 | 735 | .dark-mode .main-content-header .header-description { 736 | color: var(--text-color-dark); 737 | } 738 | 739 | .main-content-header .platform-selectors { 740 | display: flex; 741 | align-items: center; 742 | gap: 6px; 743 | font-size: 14px; 744 | color: var(--text-color-light); 745 | opacity: 0.9; 746 | flex-wrap: wrap; 747 | margin-right: 0 !important; 748 | } 749 | 750 | .dark-mode .main-content-header .platform-selectors { 751 | color: var(--text-color-dark); 752 | } 753 | 754 | .main-content-header .dev-mode-toggle { 755 | display: flex; 756 | align-items: center; 757 | justify-content: space-between; 758 | padding: 4px 8px; 759 | border: 1px solid #e1e4e8; 760 | border-radius: 6px; 761 | background-color: var(--bg-color-light); 762 | min-width: 120px; 763 | margin-left: auto; 764 | } 765 | 766 | .dark-mode .main-content-header .dev-mode-toggle { 767 | background-color: var(--bg-color-dark); 768 | border-color: #2d2d2d; 769 | } 770 | 771 | .main-content-header .dev-mode-toggle:hover { 772 | border-color: var(--accent-color); 773 | } 774 | 775 | .main-content-header .dev-mode-label { 776 | font-size: 13px; 777 | color: var(--text-color-light); 778 | white-space: nowrap; 779 | order: 1; 780 | } 781 | 782 | .dark-mode .main-content-header .dev-mode-label { 783 | color: var(--text-color-dark); 784 | } 785 | 786 | .main-content-header .switch { 787 | order: 2; 788 | } 789 | 790 | @media (max-width: 768px) { 791 | .main-content-header { 792 | padding: 12px 40px; 793 | margin: -16px -16px 16px -16px; 794 | } 795 | 796 | .main-content-header .header-content { 797 | flex-direction: column; 798 | align-items: stretch; 799 | gap: 12px; 800 | } 801 | 802 | .main-content-header .header-description { 803 | text-align: left; 804 | white-space: normal; 805 | } 806 | 807 | .main-content-header .platform-selectors { 808 | flex-direction: row; 809 | flex-wrap: wrap; 810 | width: 100%; 811 | align-items: center; 812 | text-align: left; 813 | gap: 4px; 814 | } 815 | 816 | .main-content-header .custom-select, 817 | .main-content-header .custom-input { 818 | width: auto; 819 | min-width: 0; 820 | flex: 0 1 auto; 821 | } 822 | 823 | .main-content-header .custom-input { 824 | width: 100%; 825 | } 826 | 827 | /* Show custom inputs on new line when visible */ 828 | .main-content-header .custom-input:not([style*="display: none"]) { 829 | margin-top: 4px; 830 | margin-bottom: 4px; 831 | } 832 | } 833 | 834 | .main-content-header .custom-select { 835 | padding: 2px 20px 2px 4px; 836 | font-size: inherit; 837 | border: none; 838 | border-radius: 4px; 839 | background-color: transparent; 840 | color: var(--accent-color); 841 | cursor: pointer; 842 | min-width: 0; 843 | font-weight: 500; 844 | background-position: right 4px center; 845 | background-size: 12px; 846 | text-decoration: underline; 847 | text-underline-offset: 2px; 848 | } 849 | 850 | .main-content-header .custom-select:hover { 851 | opacity: 0.8; 852 | } 853 | 854 | .main-content-header .custom-input { 855 | padding: 2px 4px; 856 | font-size: inherit; 857 | border: none; 858 | border-bottom: 1px dashed var(--accent-color); 859 | border-radius: 0; 860 | background-color: transparent; 861 | color: var(--accent-color); 862 | min-width: 100px; 863 | font-weight: 500; 864 | } 865 | 866 | .main-content-header .custom-input:focus { 867 | outline: none; 868 | box-shadow: none; 869 | border-bottom-style: solid; 870 | } 871 | 872 | .prompts-grid { 873 | display: grid; 874 | grid-template-columns: repeat(4, 1fr); 875 | gap: 16px; 876 | max-width: 100%; 877 | margin: 0; 878 | padding: 16px; 879 | } 880 | 881 | .container-lg.markdown-body { 882 | padding: 0; 883 | max-width: none; 884 | } 885 | 886 | @media (max-width: 1600px) { 887 | .prompts-grid { 888 | grid-template-columns: repeat(3, 1fr); 889 | } 890 | } 891 | 892 | @media (max-width: 1200px) { 893 | .prompts-grid { 894 | grid-template-columns: repeat(2, 1fr); 895 | } 896 | } 897 | 898 | @media (max-width: 768px) { 899 | .prompts-grid { 900 | grid-template-columns: 1fr; 901 | gap: 15px; 902 | } 903 | 904 | .card-footer { 905 | flex-direction: column; 906 | align-items: flex-start; 907 | gap: 0.75rem; 908 | } 909 | 910 | .contributor-badge { 911 | margin-left: 0; 912 | } 913 | } 914 | 915 | .prompt-card { 916 | background: var(--bg-color-light); 917 | border: 1px solid #e1e4e8; 918 | border-radius: 12px; 919 | padding: 16px; 920 | position: relative; 921 | transition: transform 0.2s ease; 922 | display: flex; 923 | flex-direction: column; 924 | min-height: 200px; 925 | } 926 | 927 | .dark-mode .prompt-card { 928 | background: var(--bg-color-dark); 929 | border-color: #2d2d2d; 930 | } 931 | 932 | .prompt-card:hover { 933 | transform: translateY(-2px); 934 | } 935 | 936 | .prompt-title { 937 | font-size: 1rem; 938 | font-weight: 600; 939 | margin: 0 0 12px 0; 940 | color: var(--text-color-light); 941 | line-height: 1.4; 942 | display: flex; 943 | justify-content: space-between; 944 | align-items: center; 945 | } 946 | 947 | .dark-mode .prompt-title { 948 | color: var(--text-color-dark); 949 | } 950 | 951 | .prompt-content { 952 | font-size: 0.9rem; 953 | line-height: 1.5; 954 | color: var(--text-color-light); 955 | opacity: 0.8; 956 | display: -webkit-box; 957 | -webkit-line-clamp: 4; 958 | -webkit-box-orient: vertical; 959 | overflow: hidden; 960 | margin-bottom: 40px; 961 | } 962 | 963 | .dark-mode .prompt-content { 964 | color: var(--text-color-dark); 965 | } 966 | 967 | .copy-button { 968 | position: static; 969 | background: transparent; 970 | border: none; 971 | cursor: pointer; 972 | padding: 2px; 973 | color: var(--accent-color); 974 | opacity: 0.8; 975 | transition: opacity 0.2s ease; 976 | z-index: 2; 977 | margin-left: 8px; 978 | flex-shrink: 0; 979 | } 980 | 981 | .copy-button:hover { 982 | opacity: 1; 983 | } 984 | 985 | .copy-button svg { 986 | width: 16px; 987 | height: 16px; 988 | } 989 | 990 | .search-container { 991 | margin-bottom: 20px; 992 | } 993 | 994 | .prompt-count { 995 | margin-bottom: 12px; 996 | padding: 6px 10px; 997 | border-radius: 6px; 998 | background: rgba(16, 185, 129, 0.1); 999 | color: var(--accent-color); 1000 | font-size: 0.8rem; 1001 | font-weight: 500; 1002 | display: flex; 1003 | align-items: center; 1004 | justify-content: space-between; 1005 | } 1006 | 1007 | .dark-mode .prompt-count { 1008 | background: rgba(16, 185, 129, 0.15); 1009 | } 1010 | 1011 | .prompt-count .count-number { 1012 | font-weight: 600; 1013 | font-size: 0.9rem; 1014 | } 1015 | 1016 | .prompt-count.filtered { 1017 | background: rgba(59, 130, 246, 0.1); 1018 | color: #3b82f6; 1019 | } 1020 | 1021 | .dark-mode .prompt-count.filtered { 1022 | background: rgba(59, 130, 246, 0.15); 1023 | } 1024 | 1025 | #searchInput { 1026 | width: 100%; 1027 | padding: 8px; 1028 | border: 1px solid #e1e4e8; 1029 | border-radius: 4px; 1030 | margin-bottom: 10px; 1031 | background-color: var(--bg-color-light); 1032 | color: var(--text-color-light); 1033 | } 1034 | 1035 | .dark-mode #searchInput { 1036 | background-color: var(--bg-color-dark); 1037 | color: var(--text-color-dark); 1038 | border-color: #2d2d2d; 1039 | } 1040 | 1041 | #searchResults { 1042 | list-style: none; 1043 | padding: 0; 1044 | margin: 0; 1045 | } 1046 | 1047 | .search-result-item { 1048 | padding: 8px 12px; 1049 | cursor: pointer; 1050 | transition: all 0.2s ease; 1051 | border-radius: 6px; 1052 | } 1053 | 1054 | .search-result-item:hover { 1055 | background-color: rgba(16, 185, 129, 0.1); 1056 | border-radius: 6px; 1057 | transform: translateX(4px); 1058 | } 1059 | 1060 | .dark-mode .search-result-item:hover { 1061 | background-color: rgba(16, 185, 129, 0.2); 1062 | } 1063 | 1064 | .search-result-item.active { 1065 | background-color: var(--accent-color); 1066 | color: white; 1067 | } 1068 | 1069 | @media (max-width: 768px) { 1070 | .content-wrapper { 1071 | flex-direction: column; 1072 | } 1073 | 1074 | .sidebar { 1075 | width: 100%; 1076 | height: auto; 1077 | border-right: none; 1078 | border-bottom: 1px solid #e1e4e8; 1079 | } 1080 | 1081 | .dark-mode .sidebar { 1082 | border-bottom-color: #2d2d2d; 1083 | } 1084 | 1085 | .main-content { 1086 | height: auto; 1087 | } 1088 | 1089 | .header-right { 1090 | gap: 8px; 1091 | } 1092 | 1093 | .mode-text { 1094 | display: none; 1095 | } 1096 | 1097 | .dark-mode-toggle { 1098 | padding: 6px; 1099 | } 1100 | 1101 | .github-link { 1102 | padding: 6px; 1103 | } 1104 | 1105 | .github-link span { 1106 | display: none; 1107 | } 1108 | } 1109 | 1110 | /* Add modal styles */ 1111 | .modal-overlay { 1112 | display: none; 1113 | position: fixed; 1114 | top: 0; 1115 | left: 0; 1116 | right: 0; 1117 | bottom: 0; 1118 | background: rgba(0, 0, 0, 0.7); 1119 | z-index: 1000; 1120 | backdrop-filter: blur(4px); 1121 | } 1122 | 1123 | .modal { 1124 | position: fixed; 1125 | top: 50%; 1126 | left: 50%; 1127 | transform: translate(-50%, -50%); 1128 | background: var(--bg-color-light); 1129 | border-radius: 12px; 1130 | padding: 24px; 1131 | width: 90%; 1132 | max-width: 800px; 1133 | max-height: 90vh; 1134 | overflow-y: auto; 1135 | z-index: 1001; 1136 | box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); 1137 | } 1138 | 1139 | .dark-mode .modal { 1140 | background: var(--bg-color-dark); 1141 | } 1142 | 1143 | .modal-header { 1144 | display: flex; 1145 | justify-content: space-between; 1146 | align-items: flex-start; 1147 | margin-bottom: 16px; 1148 | } 1149 | 1150 | .modal-title { 1151 | font-size: 1.4rem; 1152 | font-weight: 600; 1153 | margin: 0; 1154 | padding-right: 80px; 1155 | color: var(--text-color-light); 1156 | flex: 1; 1157 | } 1158 | 1159 | .modal-copy-button { 1160 | background: transparent; 1161 | border: none; 1162 | cursor: pointer; 1163 | padding: 4px; 1164 | color: var(--accent-color); 1165 | opacity: 0.8; 1166 | transition: opacity 0.2s ease; 1167 | width: 32px; 1168 | height: 32px; 1169 | display: flex; 1170 | align-items: center; 1171 | justify-content: center; 1172 | } 1173 | 1174 | .modal-close { 1175 | position: static; 1176 | background: transparent; 1177 | border: none; 1178 | cursor: pointer; 1179 | color: var(--text-color-light); 1180 | padding: 4px; 1181 | opacity: 0.7; 1182 | transition: opacity 0.2s ease; 1183 | width: 32px; 1184 | height: 32px; 1185 | display: flex; 1186 | align-items: center; 1187 | justify-content: center; 1188 | } 1189 | 1190 | .dark-mode .modal-close { 1191 | color: var(--text-color-dark); 1192 | } 1193 | 1194 | .modal-close:hover { 1195 | opacity: 1; 1196 | } 1197 | 1198 | .prompt-card { 1199 | cursor: pointer; 1200 | } 1201 | 1202 | /* Add contributor badge styles */ 1203 | .contributor-badge { 1204 | position: absolute; 1205 | bottom: 8px; 1206 | right: 12px; 1207 | font-size: 0.65rem; 1208 | color: var(--accent-color); 1209 | text-decoration: none; 1210 | opacity: 0.7; 1211 | transition: all 0.2s ease; 1212 | background: rgba(16, 185, 129, 0.1); 1213 | padding: 1px 6px; 1214 | border-radius: 8px; 1215 | } 1216 | 1217 | .dark-mode .contributor-badge { 1218 | background: rgba(16, 185, 129, 0.15); 1219 | } 1220 | 1221 | .contributor-badge:hover { 1222 | opacity: 1; 1223 | transform: translateY(-1px); 1224 | background: var(--accent-color); 1225 | color: white; 1226 | } 1227 | 1228 | .dark-mode .contributor-badge:hover { 1229 | color: var(--bg-color-dark); 1230 | } 1231 | 1232 | .prompt-content { 1233 | font-size: 0.9rem; 1234 | line-height: 1.5; 1235 | color: var(--text-color-light); 1236 | opacity: 0.8; 1237 | display: -webkit-box; 1238 | -webkit-line-clamp: 4; 1239 | -webkit-box-orient: vertical; 1240 | overflow: hidden; 1241 | margin-bottom: 40px; 1242 | } 1243 | 1244 | /* Add modal header and footer styles */ 1245 | .modal-footer { 1246 | margin-top: 24px; 1247 | padding-top: 16px; 1248 | border-top: 1px solid #e1e4e8; 1249 | display: flex; 1250 | flex-direction: row; 1251 | justify-content: space-between; 1252 | align-items: center; 1253 | gap: 16px; 1254 | } 1255 | 1256 | .modal-footer-left { 1257 | display: flex; 1258 | flex-direction: column; 1259 | align-items: flex-start; 1260 | margin-bottom: 0; 1261 | max-width: 50%; 1262 | } 1263 | 1264 | .modal-footer-right { 1265 | display: flex; 1266 | align-items: center; 1267 | justify-content: flex-end; 1268 | flex: 0 0 auto; 1269 | white-space: nowrap; 1270 | } 1271 | 1272 | .modal-chat-button { 1273 | display: flex; 1274 | align-items: center; 1275 | gap: 6px; 1276 | padding: 6px 12px; 1277 | border-radius: 6px; 1278 | font-size: 0.9rem; 1279 | background: var(--accent-color); 1280 | color: white; 1281 | border: none; 1282 | cursor: pointer; 1283 | transition: all 0.2s ease; 1284 | } 1285 | 1286 | .modal-chat-button:hover { 1287 | background: var(--accent-color-hover); 1288 | } 1289 | 1290 | .modal-chat-button svg { 1291 | width: 16px; 1292 | height: 16px; 1293 | } 1294 | 1295 | .modal-contributor { 1296 | font-size: 0.8rem; 1297 | color: var(--accent-color); 1298 | text-decoration: none; 1299 | opacity: 0.8; 1300 | transition: opacity 0.2s ease; 1301 | } 1302 | 1303 | .modal-contributor:hover { 1304 | opacity: 1; 1305 | } 1306 | 1307 | .dark-mode .modal-title { 1308 | color: var(--text-color-dark); 1309 | } 1310 | 1311 | .modal-content { 1312 | font-size: 1rem; 1313 | line-height: 1.6; 1314 | color: var(--text-color-light); 1315 | } 1316 | 1317 | .dark-mode .modal-content { 1318 | color: var(--text-color-dark); 1319 | } 1320 | 1321 | .modal-copy-button:hover { 1322 | opacity: 1; 1323 | } 1324 | 1325 | .modal-copy-button svg { 1326 | width: 20px; 1327 | height: 20px; 1328 | } 1329 | 1330 | .modal-close svg { 1331 | width: 20px; 1332 | height: 20px; 1333 | } 1334 | 1335 | .dark-mode .modal-close { 1336 | color: var(--text-color-dark); 1337 | } 1338 | 1339 | .footer-section a { 1340 | color: var(--accent-color); 1341 | text-decoration: none; 1342 | } 1343 | 1344 | .footer-section a:hover { 1345 | opacity: 1; 1346 | } 1347 | 1348 | .book-link { 1349 | color: var(--accent-color) !important; 1350 | padding: 1px 0; 1351 | display: flex; 1352 | align-items: center; 1353 | gap: 8px; 1354 | opacity: 0.7; 1355 | transition: opacity 0.2s ease; 1356 | } 1357 | 1358 | .dark-mode .book-link { 1359 | color: var(--accent-color) !important; 1360 | } 1361 | 1362 | .book-link:hover { 1363 | opacity: 1; 1364 | } 1365 | 1366 | .social-footer-link { 1367 | color: var(--accent-color); 1368 | opacity: 0.8; 1369 | transition: opacity 0.2s ease; 1370 | } 1371 | 1372 | .dark-mode .social-footer-link { 1373 | color: var(--accent-color); 1374 | } 1375 | 1376 | .contribute-card { 1377 | border: 2px dashed var(--accent-color); 1378 | background: rgba(16, 185, 129, 0.05); 1379 | transition: all 0.2s ease; 1380 | } 1381 | 1382 | .contribute-card:hover { 1383 | background: rgba(16, 185, 129, 0.1); 1384 | transform: translateY(-2px); 1385 | } 1386 | 1387 | .dark-mode .contribute-card { 1388 | background: rgba(16, 185, 129, 0.1); 1389 | } 1390 | 1391 | .dark-mode .contribute-card:hover { 1392 | background: rgba(16, 185, 129, 0.15); 1393 | } 1394 | 1395 | .search-result-item.add-prompt { 1396 | color: var(--accent-color); 1397 | border: 1px dashed var(--accent-color); 1398 | background: rgba(16, 185, 129, 0.05); 1399 | transition: all 0.2s ease; 1400 | } 1401 | 1402 | .search-result-item.add-prompt:hover { 1403 | background: rgba(16, 185, 129, 0.1); 1404 | transform: translateY(-1px); 1405 | } 1406 | 1407 | .dark-mode .search-result-item.add-prompt { 1408 | background: rgba(16, 185, 129, 0.1); 1409 | } 1410 | 1411 | .dark-mode .search-result-item.add-prompt:hover { 1412 | background: rgba(16, 185, 129, 0.15); 1413 | } 1414 | 1415 | .star-count { 1416 | display: flex; 1417 | align-items: center; 1418 | gap: 4px; 1419 | color: var(--accent-color); 1420 | font-size: 0.9rem; 1421 | opacity: 0.8; 1422 | text-decoration: none; 1423 | transition: opacity 0.2s ease; 1424 | } 1425 | 1426 | .star-count:hover { 1427 | opacity: 1; 1428 | } 1429 | 1430 | .star-count svg { 1431 | width: 16px; 1432 | height: 16px; 1433 | } 1434 | 1435 | @media (max-width: 768px) { 1436 | .star-count { 1437 | display: flex; 1438 | font-size: 0.8rem; 1439 | } 1440 | 1441 | .star-count svg { 1442 | width: 14px; 1443 | height: 14px; 1444 | } 1445 | } 1446 | 1447 | /* Dev Mode Toggle Switch Styles */ 1448 | .dev-mode-toggle { 1449 | display: flex; 1450 | align-items: center; 1451 | justify-content: space-between; 1452 | padding: 6px 12px; 1453 | border: 1px solid #e1e4e8; 1454 | border-radius: 6px; 1455 | background-color: var(--bg-color-light); 1456 | min-width: 120px; 1457 | flex: 1; 1458 | } 1459 | 1460 | .dev-mode-label { 1461 | font-size: 14px; 1462 | color: var(--text-color-light); 1463 | white-space: nowrap; 1464 | order: 1; 1465 | } 1466 | 1467 | .switch { 1468 | position: relative; 1469 | display: inline-block; 1470 | width: 40px; 1471 | height: 20px; 1472 | } 1473 | 1474 | .switch input { 1475 | opacity: 0; 1476 | width: 0; 1477 | height: 0; 1478 | } 1479 | 1480 | .slider { 1481 | position: absolute; 1482 | cursor: pointer; 1483 | top: 0; 1484 | left: 0; 1485 | right: 0; 1486 | bottom: 0; 1487 | background-color: #ccc; 1488 | transition: .4s; 1489 | } 1490 | 1491 | .dark-mode .slider { 1492 | background-color: #555; 1493 | } 1494 | 1495 | .slider:before { 1496 | position: absolute; 1497 | content: ""; 1498 | height: 16px; 1499 | width: 16px; 1500 | left: 2px; 1501 | bottom: 2px; 1502 | background-color: white; 1503 | transition: .4s; 1504 | } 1505 | 1506 | .dark-mode .slider:before { 1507 | background-color: #e1e1e1; 1508 | } 1509 | 1510 | input:checked + .slider { 1511 | background-color: var(--accent-color); 1512 | } 1513 | 1514 | .dark-mode input:checked + .slider { 1515 | background-color: var(--accent-color); 1516 | } 1517 | 1518 | input:focus + .slider { 1519 | box-shadow: 0 0 1px var(--accent-color); 1520 | } 1521 | 1522 | .dark-mode input:focus + .slider { 1523 | box-shadow: 0 0 1px var(--accent-color); 1524 | } 1525 | 1526 | input:checked + .slider:before { 1527 | transform: translateX(20px); 1528 | } 1529 | 1530 | .slider.round { 1531 | border-radius: 34px; 1532 | } 1533 | 1534 | .slider.round:before { 1535 | border-radius: 50%; 1536 | } 1537 | 1538 | .main-content-header .dev-mode-toggle { 1539 | display: flex; 1540 | align-items: center; 1541 | justify-content: space-between; 1542 | padding: 6px 12px; 1543 | border: 1px solid #e1e4e8; 1544 | border-radius: 6px; 1545 | background-color: var(--bg-color-light); 1546 | min-width: 160px; 1547 | flex: 1; 1548 | } 1549 | 1550 | .dark-mode .main-content-header .dev-mode-toggle { 1551 | background-color: var(--bg-color-dark); 1552 | border-color: #2d2d2d; 1553 | } 1554 | 1555 | .main-content-header .dev-mode-toggle:hover { 1556 | border-color: var(--accent-color); 1557 | } 1558 | 1559 | .main-content-header .dev-mode-label { 1560 | font-size: 14px; 1561 | color: var(--text-color-light); 1562 | white-space: nowrap; 1563 | order: 1; 1564 | } 1565 | 1566 | .dark-mode .main-content-header .dev-mode-label { 1567 | color: var(--text-color-dark); 1568 | } 1569 | 1570 | .main-content-header .switch { 1571 | order: 2; 1572 | } 1573 | 1574 | @media (max-width: 768px) { 1575 | .main-content-header .dev-mode-toggle { 1576 | width: 100%; 1577 | order: -1; 1578 | } 1579 | 1580 | .main-content-header .dev-mode-label { 1581 | display: inline-block; 1582 | } 1583 | 1584 | .main-content-header .platform-selectors { 1585 | flex-direction: column; 1586 | width: 100%; 1587 | } 1588 | } 1589 | 1590 | /* Copilot Suggestion Modal Styles */ 1591 | .copilot-suggestion-backdrop { 1592 | position: fixed; 1593 | top: 0; 1594 | left: 0; 1595 | right: 0; 1596 | bottom: 0; 1597 | background: rgba(0, 0, 0, 0.5); 1598 | backdrop-filter: blur(4px); 1599 | -webkit-backdrop-filter: blur(4px); 1600 | z-index: 1001; 1601 | display: none; 1602 | } 1603 | 1604 | .dark-mode .copilot-suggestion-backdrop { 1605 | background: rgba(0, 0, 0, 0.7); 1606 | } 1607 | 1608 | .copilot-suggestion-modal { 1609 | position: fixed; 1610 | top: 50%; 1611 | left: 50%; 1612 | transform: translate(-50%, -50%); 1613 | background: var(--bg-color-light); 1614 | border-radius: 12px; 1615 | padding: 32px; 1616 | width: 90%; 1617 | max-width: 520px; 1618 | z-index: 1002; 1619 | box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); 1620 | display: none; 1621 | } 1622 | 1623 | .dark-mode .copilot-suggestion-modal { 1624 | background: var(--bg-color-dark); 1625 | box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); 1626 | } 1627 | 1628 | .prompts-count-label { 1629 | display: none; 1630 | } 1631 | 1632 | .dev-mode .count-label { 1633 | display: none; 1634 | } 1635 | 1636 | .dev-mode .prompts-count-label { 1637 | display: block; 1638 | } 1639 | 1640 | .copilot-suggestion-content { 1641 | margin-bottom: 24px; 1642 | color: var(--text-color-light); 1643 | font-size: 1.1rem; 1644 | line-height: 1.5; 1645 | } 1646 | 1647 | .dark-mode .copilot-suggestion-content { 1648 | color: var(--text-color-dark); 1649 | } 1650 | 1651 | .copilot-suggestion-actions { 1652 | display: flex; 1653 | flex-direction: column; 1654 | gap: 16px; 1655 | } 1656 | 1657 | .copilot-suggestion-checkbox { 1658 | display: flex; 1659 | align-items: center; 1660 | gap: 8px; 1661 | color: var(--text-color-light); 1662 | font-size: 0.9rem; 1663 | opacity: 0.8; 1664 | order: 2; 1665 | margin-top: 8px; 1666 | } 1667 | 1668 | .dark-mode .copilot-suggestion-checkbox { 1669 | color: var(--text-color-dark); 1670 | } 1671 | 1672 | .copilot-suggestion-checkbox input[type="checkbox"] { 1673 | width: 16px; 1674 | height: 16px; 1675 | accent-color: var(--accent-color); 1676 | cursor: pointer; 1677 | margin: 0; 1678 | } 1679 | 1680 | .copilot-suggestion-checkbox:hover { 1681 | opacity: 1; 1682 | } 1683 | 1684 | .copilot-suggestion-buttons { 1685 | display: flex; 1686 | flex-direction: column; 1687 | gap: 12px; 1688 | order: 1; 1689 | } 1690 | 1691 | .copilot-suggestion-button { 1692 | padding: 12px 16px; 1693 | border-radius: 8px; 1694 | font-size: 0.95rem; 1695 | border: none; 1696 | cursor: pointer; 1697 | transition: all 0.2s ease; 1698 | font-weight: 500; 1699 | width: 100%; 1700 | } 1701 | 1702 | .copilot-suggestion-button.primary { 1703 | background: var(--accent-color); 1704 | color: white; 1705 | } 1706 | 1707 | .copilot-suggestion-button.primary:hover { 1708 | background: var(--accent-color-hover); 1709 | transform: translateY(-1px); 1710 | } 1711 | 1712 | .copilot-suggestion-button.secondary { 1713 | background: transparent; 1714 | color: var(--text-color-light); 1715 | border: 1px solid #e1e4e8; 1716 | } 1717 | 1718 | .dark-mode .copilot-suggestion-button.secondary { 1719 | color: var(--text-color-dark); 1720 | border-color: #2d2d2d; 1721 | } 1722 | 1723 | .copilot-suggestion-button.secondary:hover { 1724 | background: rgba(0, 0, 0, 0.05); 1725 | transform: translateY(-1px); 1726 | } 1727 | 1728 | .dark-mode .copilot-suggestion-button.secondary:hover { 1729 | background: rgba(255, 255, 255, 0.05); 1730 | } 1731 | 1732 | @media (max-width: 480px) { 1733 | .copilot-suggestion-modal { 1734 | padding: 24px; 1735 | max-width: 100%; 1736 | margin: 0 16px; 1737 | } 1738 | 1739 | .copilot-suggestion-content { 1740 | font-size: 1rem; 1741 | margin-bottom: 20px; 1742 | } 1743 | 1744 | .copilot-suggestion-button { 1745 | padding: 10px 16px; 1746 | font-size: 0.9rem; 1747 | } 1748 | } 1749 | 1750 | /* Variable Input Field Styles */ 1751 | .variable-container { 1752 | margin-bottom: 24px; 1753 | padding: 16px; 1754 | background: rgba(16, 185, 129, 0.05); 1755 | border-radius: 8px; 1756 | border: 1px solid var(--accent-color); 1757 | } 1758 | 1759 | .variable-form { 1760 | display: grid; 1761 | gap: 12px; 1762 | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 1763 | } 1764 | 1765 | .variable-input-wrapper { 1766 | display: flex; 1767 | flex-direction: column; 1768 | gap: 4px; 1769 | } 1770 | 1771 | .variable-input-wrapper label { 1772 | font-size: 0.8rem; 1773 | color: var(--accent-color); 1774 | font-weight: 500; 1775 | } 1776 | 1777 | .variable-input { 1778 | padding: 8px; 1779 | border: 1px solid #e1e4e8; 1780 | border-radius: 4px; 1781 | font-size: 0.9rem; 1782 | background: var(--bg-color-light); 1783 | color: var(--text-color-light); 1784 | transition: all 0.2s ease; 1785 | } 1786 | 1787 | .dark-mode .variable-input { 1788 | background: var(--bg-color-dark); 1789 | color: var(--text-color-dark); 1790 | border-color: #2d2d2d; 1791 | } 1792 | 1793 | .variable-input:focus { 1794 | outline: none; 1795 | border-color: var(--accent-color); 1796 | box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.1); 1797 | } 1798 | 1799 | .modal-content { 1800 | /* white-space: pre-wrap; */ 1801 | } 1802 | 1803 | .chrome-ext-link { 1804 | display: flex; 1805 | align-items: center; 1806 | gap: 4px; 1807 | color: var(--accent-color); 1808 | font-size: 0.9rem; 1809 | opacity: 0.8; 1810 | text-decoration: none; 1811 | transition: opacity 0.2s ease; 1812 | } 1813 | 1814 | .chrome-ext-link:hover { 1815 | opacity: 1; 1816 | } 1817 | 1818 | .chrome-ext-link svg { 1819 | width: 20px; 1820 | height: 20px; 1821 | } 1822 | 1823 | .platform-selectors { 1824 | display: flex; 1825 | align-items: center; 1826 | gap: 8px; 1827 | margin-right: 16px; 1828 | } 1829 | 1830 | .custom-select { 1831 | padding: 6px 12px; 1832 | font-size: 14px; 1833 | border: 1px solid #e1e4e8; 1834 | border-radius: 6px; 1835 | background-color: var(--bg-color-light); 1836 | color: var(--text-color-light); 1837 | cursor: pointer; 1838 | transition: all 0.2s ease; 1839 | min-width: 120px; 1840 | -webkit-appearance: none; 1841 | -moz-appearance: none; 1842 | appearance: none; 1843 | background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2310b981' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); 1844 | background-repeat: no-repeat; 1845 | background-position: right 8px center; 1846 | background-size: 16px; 1847 | padding-right: 32px; 1848 | } 1849 | 1850 | .dark-mode .custom-select { 1851 | background-color: var(--bg-color-dark); 1852 | color: var(--text-color-dark); 1853 | border-color: #2d2d2d; 1854 | } 1855 | 1856 | .custom-select:hover { 1857 | border-color: var(--accent-color); 1858 | } 1859 | 1860 | .custom-select:focus { 1861 | outline: none; 1862 | border-color: var(--accent-color); 1863 | box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2); 1864 | } 1865 | 1866 | .custom-input { 1867 | padding: 6px 12px; 1868 | font-size: 14px; 1869 | border: 1px solid #e1e4e8; 1870 | border-radius: 6px; 1871 | background-color: var(--bg-color-light); 1872 | color: var(--text-color-light); 1873 | transition: all 0.2s ease; 1874 | min-width: 150px; 1875 | } 1876 | 1877 | .dark-mode .custom-input { 1878 | background-color: var(--bg-color-dark); 1879 | color: var(--text-color-dark); 1880 | border-color: #2d2d2d; 1881 | } 1882 | 1883 | .custom-input:hover { 1884 | border-color: var(--accent-color); 1885 | } 1886 | 1887 | .custom-input:focus { 1888 | outline: none; 1889 | border-color: var(--accent-color); 1890 | box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2); 1891 | } 1892 | 1893 | .custom-input::placeholder { 1894 | color: #6a737d; 1895 | opacity: 0.8; 1896 | } 1897 | 1898 | .dark-mode .custom-input::placeholder { 1899 | color: #8b949e; 1900 | } 1901 | 1902 | @media (max-width: 768px) { 1903 | .platform-selectors { 1904 | flex-direction: column; 1905 | align-items: stretch; 1906 | gap: 8px; 1907 | margin-bottom: 12px; 1908 | width: 100%; 1909 | } 1910 | 1911 | .custom-select, 1912 | .custom-input { 1913 | width: 100%; 1914 | } 1915 | } 1916 | 1917 | .main-content-header .custom-select { 1918 | min-width: 0; 1919 | } 1920 | 1921 | .main-content-header .custom-input { 1922 | flex: 1; 1923 | min-width: 0; 1924 | } 1925 | 1926 | @media (max-width: 768px) { 1927 | .main-content-header { 1928 | padding: 12px 40px; 1929 | margin: -16px -16px 16px -16px; 1930 | } 1931 | 1932 | .main-content-header .header-content { 1933 | flex-direction: column; 1934 | align-items: stretch; 1935 | gap: 12px; 1936 | } 1937 | 1938 | .main-content-header .header-description { 1939 | text-align: left; 1940 | white-space: normal; 1941 | } 1942 | 1943 | .main-content-header .platform-selectors { 1944 | flex-direction: row; 1945 | flex-wrap: wrap; 1946 | width: 100%; 1947 | align-items: center; 1948 | text-align: left; 1949 | gap: 4px; 1950 | } 1951 | 1952 | .main-content-header .custom-select, 1953 | .main-content-header .custom-input { 1954 | width: auto; 1955 | min-width: 0; 1956 | flex: 0 1 auto; 1957 | } 1958 | 1959 | .main-content-header .custom-input { 1960 | width: 100%; 1961 | } 1962 | 1963 | /* Show custom inputs on new line when visible */ 1964 | .main-content-header .custom-input:not([style*="display: none"]) { 1965 | margin-top: 4px; 1966 | margin-bottom: 4px; 1967 | } 1968 | } 1969 | 1970 | .platform-tag-container { 1971 | position: relative; 1972 | display: inline-block; 1973 | } 1974 | 1975 | .grok-mode-dropdown { 1976 | position: absolute; 1977 | top: 100%; 1978 | left: 0; 1979 | z-index: 1000; 1980 | min-width: 160px; 1981 | margin-top: 4px; 1982 | background: var(--bg-color-light); 1983 | border: 1px solid var(--accent-color); 1984 | border-radius: 6px; 1985 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 1986 | font-size: 0.8rem; 1987 | overflow: hidden; 1988 | padding: 4px; 1989 | } 1990 | 1991 | .dark-mode .grok-mode-dropdown { 1992 | background: var(--bg-color-dark); 1993 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); 1994 | } 1995 | 1996 | .grok-mode-option { 1997 | padding: 4px 8px; 1998 | cursor: pointer; 1999 | color: var(--text-color-light); 2000 | transition: all 0.2s ease; 2001 | border-radius: 4px; 2002 | white-space: nowrap; 2003 | } 2004 | 2005 | .dark-mode .grok-mode-option { 2006 | color: var(--text-color-dark); 2007 | } 2008 | 2009 | .grok-mode-option:hover { 2010 | background: rgba(16, 185, 129, 0.1); 2011 | color: var(--accent-color); 2012 | } 2013 | 2014 | .dark-mode .grok-mode-option:hover { 2015 | background: rgba(16, 185, 129, 0.2); 2016 | } 2017 | 2018 | .cursor-logo { 2019 | display: flex; 2020 | align-items: center; 2021 | gap: 6px; 2022 | color: var(--accent-color); 2023 | opacity: 0.8; 2024 | text-decoration: none; 2025 | transition: all 0.2s ease; 2026 | font-size: 0.85rem; 2027 | margin-right: 8px; 2028 | } 2029 | 2030 | .cursor-logo:hover { 2031 | opacity: 1; 2032 | transform: translateY(-1px); 2033 | } 2034 | 2035 | .cursor-logo svg { 2036 | width: 16px; 2037 | height: 16px; 2038 | } 2039 | 2040 | /* Style for the Cursor AI logo paths in dark mode */ 2041 | .cursor-logo path[fill="#fff"] { 2042 | transition: fill 0.2s ease; 2043 | } 2044 | 2045 | .dark-mode .cursor-logo path[fill="#fff"] { 2046 | fill: #2d2d2d; 2047 | } 2048 | 2049 | @media (max-width: 768px) { 2050 | .cursor-logo span { 2051 | display: none; 2052 | } 2053 | } 2054 | 2055 | .modal-actions { 2056 | display: flex; 2057 | gap: 0.5rem; 2058 | } 2059 | 2060 | .modal-hint { 2061 | margin-bottom: 0; 2062 | font-size: 0.75rem; 2063 | color: var(--text-color-light); 2064 | opacity: 0.7; 2065 | display: block; 2066 | line-height: 1.4; 2067 | max-width: 300px; 2068 | } 2069 | 2070 | .dark-mode .modal-hint { 2071 | color: var(--text-color-dark); 2072 | } 2073 | 2074 | .content-well { 2075 | background-color: var(--background-color); 2076 | border: 1px solid var(--border-color); 2077 | border-radius: 8px; 2078 | overflow: hidden; 2079 | } 2080 | 2081 | .dark-mode .content-well { 2082 | background-color: var(--background-color-dark); 2083 | border-color: var(--border-color-dark) !important; 2084 | } 2085 | 2086 | .modal-content pre { 2087 | margin: 0; 2088 | padding: 1rem; 2089 | background-color: var(--code-background); 2090 | border-radius: 0; 2091 | overflow-x: auto; 2092 | } 2093 | 2094 | .modal-content code { 2095 | font-family: 'Fira Code', monospace; 2096 | font-size: 0.9rem; 2097 | line-height: 1.5; 2098 | color: var(--code-text); 2099 | white-space: pre-wrap; 2100 | word-break: break-word; 2101 | } 2102 | 2103 | /* Dark mode adjustments */ 2104 | 2105 | .dark-mode .modal-content pre { 2106 | background-color: var(--dark-code-background); 2107 | } 2108 | 2109 | .dark-mode .modal-content code { 2110 | color: var(--dark-code-text); 2111 | } 2112 | 2113 | .dark-mode .modal-hint { 2114 | background-color: var(--dark-hover-color); 2115 | } 2116 | 2117 | .dark-mode .content-well { 2118 | border-color: var(--dark-border-color); 2119 | background-color: var(--dark-background); 2120 | } 2121 | 2122 | .github-form { 2123 | display: flex; 2124 | align-items: center; 2125 | gap: 8px; 2126 | margin-top: 0; 2127 | justify-content: flex-end; 2128 | flex-wrap: nowrap; 2129 | } 2130 | 2131 | .github-inputs { 2132 | display: flex; 2133 | align-items: center; 2134 | gap: 2px; 2135 | font-size: 13px; 2136 | color: var(--text-color-light); 2137 | } 2138 | 2139 | .github-inputs span { 2140 | opacity: 0.7; 2141 | font-size: 12px; 2142 | } 2143 | 2144 | .github-input { 2145 | border: 1px solid #e1e4e8; 2146 | border-radius: 4px; 2147 | padding: 4px 6px; 2148 | font-size: 12px; 2149 | width: 100px; 2150 | background-color: var(--bg-color-light); 2151 | color: var(--text-color-light); 2152 | transition: all 0.2s ease; 2153 | } 2154 | 2155 | #github-branch { 2156 | width: 80px; 2157 | } 2158 | 2159 | @media (max-width: 768px) { 2160 | .modal-footer { 2161 | flex-direction: column; 2162 | align-items: flex-start; 2163 | gap: 16px; 2164 | } 2165 | 2166 | .modal-footer-left { 2167 | max-width: 100%; 2168 | margin-bottom: 0; 2169 | } 2170 | 2171 | .modal-footer-right { 2172 | width: 100%; 2173 | } 2174 | 2175 | .github-form { 2176 | flex-direction: column; 2177 | align-items: stretch; 2178 | gap: 16px; 2179 | width: 100%; 2180 | } 2181 | 2182 | .github-inputs { 2183 | width: 100%; 2184 | justify-content: flex-start; 2185 | flex-wrap: wrap; 2186 | gap: 6px; 2187 | } 2188 | 2189 | .github-inputs span { 2190 | margin: 0 2px; 2191 | } 2192 | 2193 | .github-input { 2194 | flex: 1; 2195 | min-width: 60px; 2196 | } 2197 | 2198 | .create-yaml-button { 2199 | width: 100%; 2200 | } 2201 | } 2202 | 2203 | .dark-mode .modal-footer { 2204 | border-color: #2d2d2d; 2205 | } 2206 | 2207 | .github-input:focus { 2208 | outline: none; 2209 | border-color: var(--accent-color); 2210 | box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.1); 2211 | } 2212 | 2213 | .dark-mode .github-inputs { 2214 | color: var(--text-color-dark); 2215 | } 2216 | 2217 | .dark-mode .github-input { 2218 | background-color: var(--bg-color-dark); 2219 | color: var(--text-color-dark); 2220 | border-color: #2d2d2d; 2221 | } 2222 | 2223 | .create-yaml-button { 2224 | display: flex; 2225 | align-items: center; 2226 | justify-content: center; 2227 | gap: 6px; 2228 | background-color: var(--accent-color); 2229 | color: white; 2230 | border: none; 2231 | padding: 6px 12px; 2232 | border-radius: 4px; 2233 | font-size: 13px; 2234 | font-weight: 500; 2235 | cursor: pointer; 2236 | transition: all 0.2s ease; 2237 | white-space: nowrap; 2238 | flex-shrink: 0; 2239 | } 2240 | 2241 | .create-yaml-button:hover { 2242 | background-color: #0e9f71; 2243 | } -------------------------------------------------------------------------------- /vibe/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /vibe 3 | subtitle: awesome vibe coding prompts to help you build simple apps 4 | hide_platform_selector: true 5 | hide_extension_link: true 6 | hide_tone_selector: true 7 | subpage: true 8 | body_class: vibe 9 | --- 10 | -------------------------------------------------------------------------------- /vibe/script.js: -------------------------------------------------------------------------------- 1 | function parseCSV(csv) { 2 | const lines = csv.split("\n"); 3 | const headers = lines[0] 4 | .split(",") 5 | .map((header) => header.replace(/"/g, "").trim()); 6 | 7 | return lines 8 | .slice(1) 9 | .map((line) => { 10 | const values = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || []; 11 | const entry = {}; 12 | 13 | headers.forEach((header, index) => { 14 | let value = values[index] ? values[index].replace(/"/g, "").trim() : ""; 15 | // Remove backticks from the act/title 16 | if (header === "app") { 17 | value = value.replace(/`/g, ""); 18 | } 19 | entry[header] = value; 20 | }); 21 | 22 | return entry; 23 | }) 24 | .filter((entry) => entry.app && entry.prompt); 25 | } 26 | 27 | // Load prompts from CSV 28 | async function loadPrompts() { 29 | const response = await fetch('/vibeprompts.csv'); 30 | const text = await response.text(); 31 | return parseCSV(text); 32 | } 33 | 34 | // Update prompt count 35 | function updatePromptCount(filteredCount, totalCount) { 36 | const countElement = document.getElementById('promptCount'); 37 | const countNumber = countElement.getElementsByClassName('count-number')[0]; 38 | if (countElement) { 39 | countNumber.textContent = `${filteredCount}`; 40 | } 41 | } 42 | 43 | // Render prompts in the main content area 44 | async function renderMainPrompts() { 45 | const prompts = await loadPrompts(); 46 | const container = document.querySelector('#promptContent'); 47 | if (container) { 48 | container.innerHTML = `${prompt.replace(/\\n/g, '
')}