Organizador de Prompts - Constructor de Aplicaciones Web de Archivo Único
De Wikiprompt, la enciclopedia libre de prompts
Organizador de Prompts - Constructor de Aplicaciones Web de Archivo Único Un prompt detallado para generar una aplicación de organizador de prompts basada en navegador con persistencia en localStorage, búsqueda, filtrado y funciones de ordenamiento.
Contenido del PromptGuardar
🌐
Here is the single-file HTML code for a "Prompt Organizer" web application. It allows you to save, edit, delete, and categorize AI prompts directly in your browser, with all data stored locally.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prompt Organizer · local-first</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
body {
background: #f8fafc;
color: #1e293b;
line-height: 1.5;
padding: 1.5rem;
min-height: 100vh;
display: flex;
justify-content: center;
}
.app-wrapper {
max-width: 1200px;
width: 100%;
background: white;
border-radius: 24px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.05);
padding: 2rem 1.8rem;
}
header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
margin-bottom: 2rem;
gap: 1rem;
}
h1 {
font-size: 1.9rem;
font-weight: 600;
letter-spacing: -0.02em;
background: linear-gradient(145deg, #0f172a, #334155);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.subhead {
color: #64748b;
font-size: 0.95rem;
margin-top: 0.2rem;
}
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
align-items: center;
background: #f1f5f9;
padding: 0.8rem 1rem;
border-radius: 40px;
margin-bottom: 1.8rem;
}
.search-box {
flex: 2 1 240px;
display: flex;
align-items: center;
background: white;
border-radius: 40px;
padding: 0.3rem 0.8rem;
border: 1px solid #e2e8f0;
}
.search-box input {
border: none;
outline: none;
background: transparent;
padding: 0.5rem 0.4rem;
font-size: 0.95rem;
width: 100%;
}
.filter-tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
align-items: center;
}
.tag-filter-btn {
background: white;
border: 1px solid #cbd5e1;
padding: 0.3rem 0.9rem;
border-radius: 30px;
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
color: #334155;
}
.tag-filter-btn.active {
background: #0f172a;
color: white;
border-color: #0f172a;
}
.sort-select {
background: white;
border: 1px solid #cbd5e1;
border-radius: 30px;
padding: 0.4rem 1rem;
font-size: 0.85rem;
cursor: pointer;
color: #1e293b;
}
.btn {
background: white;
border: 1px solid #cbd5e1;
padding: 0.5rem 1.2rem;
border-radius: 40px;
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
transition: 0.15s;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
.btn-primary {
background: #0f172a;
color: white;
border: none;
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.15);
}
.btn-primary:hover {
background: #1e293b;
}
.btn-outline:hover {
background: #f1f5f9;
}
.prompt-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 1.2rem;
}
.prompt-card {
background: white;
border-radius: 18px;
border: 1px solid #e2e8f0;
padding: 1.2rem 1.2rem 1rem;
transition: 0.2s;
display: flex;
flex-direction: column;
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
}
.prompt-card:hover {
box-shadow: 0 12px 28px rgba(0,0,0,0.06);
border-color: #cbd5e1;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.6rem;
}
.card-title {
font-weight: 600;
font-size: 1.1rem;
color: #0f172a;
word-break: break-word;
}
.card-actions {
display: flex;
gap: 0.3rem;
flex-shrink: 0;
}
.icon-btn {
background: transparent;
border: none;
font-size: 1.1rem;
cursor: pointer;
color: #64748b;
padding: 0.2rem 0.4rem;
border-radius: 8px;
transition: 0.1s;
}
.icon-btn:hover {
background: #f1f5f9;
color: #0f172a;
}
.card-desc {
font-size: 0.9rem;
color: #475569;
margin-bottom: 0.8rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.prompt-text-preview {
background: #f8fafc;
border-radius: 12px;
padding: 0.7rem 0.9rem;
font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 0.8rem;
color: #1e293b;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 1rem;
border-left: 3px solid #94a3b8;
max-height: 100px;
overflow-y: auto;
}
.card-meta {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
font-size: 0.75rem;
color: #64748b;
border-top: 1px solid #eef2f6;
padding-top: 0.6rem;
margin-top: auto;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
}
.tag-badge {
background: #eef2ff;
color: #4338ca;
padding: 0.2rem 0.7rem;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 500;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #94a3b8;
font-size: 1rem;
}
/* modal */
.modal {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(15, 23, 42, 0.4);
backdrop-filter: blur(4px);
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal.open {
display: flex;
}
.modal-content {
background: white;
width: 90%;
max-width: 600px;
border-radius: 28px;
padding: 2rem 1.8rem;
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
max-height: 90vh;
overflow-y: auto;
}
.modal-content h2 {
font-size: 1.5rem;
margin-bottom: 1.2rem;
color: #0f172a;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
font-size: 0.85rem;
font-weight: 500;
margin-bottom: 0.3rem;
color: #334155;
}
.form-group input, .form-group textarea {
width: 100%;
padding: 0.7rem 1rem;
border: 1px solid #cbd5e1;
border-radius: 16px;
font-size: 0.9rem;
background: white;
transition: 0.15s;
}
.form-group input:focus, .form-group textarea:focus {
outline: none;
border-color: #0f172a;
box-shadow: 0 0 0 3px rgba(15,23,42,0.08);
}
.form-group textarea {
min-height: 100px;
resize: vertical;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 0.8rem;
margin-top: 1.5rem;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 160px;
background-color: #1e293b;
color: #fff;
text-align: center;
padding: 5px 8px;
border-radius: 8px;
font-size: 0.7rem;
position: absolute;
z-index: 1;
bottom: 130%;
left: 50%;
margin-left: -80px;
opacity: 0;
transition: opacity 0.2s;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
@media (max-width: 640px) {
.app-wrapper { padding: 1.2rem; }
.toolbar { flex-direction: column; align-items: stretch; }
.search-box { width: 100%; }
}
</style>
</head>
<body>
<div class="app-wrapper">
<header>
<div>
<h1>📋 Prompt Organizer</h1>
<div class="subhead">Save, edit & categorize AI prompts - stored locally</div>
</div>
<button class="btn btn-primary" id="newPromptBtn">+ New prompt</button>
</header>
<!-- Toolbar: search, tag filter, sort -->
<div class="toolbar">
<div class="search-box">
<span style="margin-right: 6px; opacity: 0.6;">🔍</span>
<input type="text" id="searchInput" placeholder="Search by title, description, prompt text..." autocomplete="off">
</div>
<div class="filter-tags" id="filterTagsContainer"></div>
<select id="sortSelect" class="sort-select" title="Sort by date">
<option value="created_desc">Created (newest)</option>
<option value="created_asc">Created (oldest)</option>
<option value="modified_desc">Modified (newest)</option>
<option value="modified_asc">Modified (oldest)</option>
</select>
</div>
<!-- Prompt grid -->
<div id="promptGrid" class="prompt-grid"></div>
<div id="emptyState" class="empty-state" style="display: none;">✨ No prompts match. Try adjusting filters.</div>
</div>
<!-- Modal for create/edit -->
<div id="modal" class="modal">
<div class="modal-content">
<h2 id="modalTitle">New prompt</h2>
<div class="form-group">
<label>Title *</label>
<input type="text" id="titleInput" placeholder="e.g. Summarize meeting notes">
</div>
<div class="form-group">
<label>Description</label>
<input type="text" id="descInput" placeholder="Short description (optional)">
</div>
<div class="form-group">
<label>Tags (comma separated)</label>
<input type="text" id="tagsInput" placeholder="writing, coding, creative">
</div>
<div class="form-group">
<label>Prompt text *</label>
<textarea id="promptTextInput" placeholder="Paste your full prompt here..."></textarea>
</div>
<div class="modal-actions">
<button class="btn btn-outline" id="cancelModalBtn">Cancel</button>
<button class="btn btn-primary" id="savePromptBtn">💾 Save prompt</button>
</div>
</div>
</div>
<script>
(function() {
// ---------- storage & seed ----------
const STORAGE_KEY = 'promptOrganizer.prompts.v1';
let prompts = [];
let currentFilterTag = 'all';
let editingId = null; // null = create mode
// ----- sample data (first load) -----
const seedPrompts = [
{ id: 'p1', title: 'Code review assistant', description: 'Ask for a thorough code review with security focus.', tags: ['coding', 'review'], promptText: 'Act as a senior developer. Review the following code for bugs, security issues, and performance. Suggest improvements.\n\n[code snippet]', createdAt: Date.now() - 86400000 * 2, updatedAt: Date.now() - 3600000 },
{ id: 'p2', title: 'Blog outline generator', description: 'Generate a structured outline for a tech blog post.', tags: ['writing', 'creative'], promptText: 'Create a detailed outline for a blog post about "local-first software". Include introduction, 5 main sections, and conclusion. Use engaging subheadings.', createdAt: Date.now() - 86400000 * 5, updatedAt: Date.now() - 86400000 },
{ id: 'p3', title: 'SQL query explainer', description: 'Explain complex SQL queries in simple terms.', tags: ['data', 'learning'], promptText: 'Explain the following SQL query step by step, what it does, and potential edge cases. Use simple language.\n\nSELECT * FROM orders WHERE total > 100 ORDER BY created_at DESC;', createdAt: Date.now() - 86400000 * 1, updatedAt: Date.now() - 7200000 },
{ id: 'p4', title: 'Meeting action items', description: 'Extract action items from meeting transcript.', tags: ['productivity', 'business'], promptText: 'From the transcript below, list all action items, owners, and deadlines. Format as a table.\n\n[transcript]', createdAt: Date.now() - 86400000 * 3, updatedAt: Date.now() - 86400000 * 3 }
];
// load or seed
function loadPrompts() {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
prompts = JSON.parse(stored);
if (!Array.isArray(prompts) || prompts.length === 0) {
prompts = [...seedPrompts];
persist();
}
} else {
prompts = [...seedPrompts];
persist();
}
} catch (e) {
prompts = [...seedPrompts];
persist();
}
}
function persist() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(prompts));
}
// ---------- helpers ----------
function formatDate(ts) {
if (!ts) return '';
const d = new Date(ts);
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
}
// get unique tags from prompts
function getAllTags() {
const tagSet = new Set(['all']);
prompts.forEach(p => {
(p.tags || []).forEach(t => tagSet.add(t));
});
return Array.from(tagSet).sort();
}
// ---------- rendering ----------
function renderFilterTags() {
const container = document.getElementById('filterTagsContainer');
const tags = getAllTags();
container.innerHTML = '';
tags.forEach(tag => {
const btn = document.createElement('button');
btn.className = 'tag-filter-btn' + (currentFilterTag === tag ? ' active' : '');
btn.textContent = tag;
btn.dataset.tag = tag;
btn.addEventListener('click', () => {
currentFilterTag = tag;
renderFilterTags();
renderPrompts();
});
container.appendChild(btn);
});
}
function getFilteredSortedPrompts() {
const searchTerm = document.getElementById('searchInput').value.trim().toLowerCase();
const sortBy = document.getElementById('sortSelect').value;
let filtered = prompts.filter(p => {
// tag filter
if (currentFilterTag !== 'all' && !(p.tags || []).includes(currentFilterTag)) return false;
// search
if (searchTerm) {
const haystack = `${p.title} ${p.description || ''} ${p.promptText} ${(p.tags || []).join(' ')}`.toLowerCase();
if (!haystack.includes(searchTerm)) return false;
}
return true;
});
// sort
filtered.sort((a, b) => {
const dateA = (sortBy.includes('created')) ? a.createdAt : a.updatedAt;
const dateB = (sortBy.includes('created')) ? b.createdAt : b.updatedAt;
if (sortBy.endsWith('asc')) return dateA - dateB;
else return dateB - dateA;
});
return filtered;
}
function renderPrompts() {
const grid = document.getElementById('promptGrid');
const empty = document.getElementById('emptyState');
const filtered = getFilteredSortedPrompts();
if (filtered.length === 0) {
grid.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
grid.innerHTML = '';
filtered.forEach(prompt => {
const card = document.createElement('div');
card.className = 'prompt-card';
const tagsHtml = (prompt.tags || []).map(t => `<span class="tag-badge">${escapeHtml(t)}</span>`).join('');
card.innerHTML = `
<div class="card-header">
<div class="card-title">${escapeHtml(prompt.title)}</div>
<div class="card-actions">
<button class="icon-btn edit-btn tooltip" data-id="${prompt.id}" title="Edit prompt">✏️</button>
<button class="icon-btn delete-btn tooltip" data-id="${prompt.id}" title="Delete prompt">🗑️</button>
</div>
</div>
<div class="card-desc">${escapeHtml(prompt.description || 'No description')}</div>
<div class="prompt-text-preview">${escapeHtml(prompt.promptText.length > 180 ? prompt.promptText.slice(0, 180) + '…' : prompt.promptText)}</div>
<div class="card-meta">
<div class="tag-list">${tagsHtml || '<span style="opacity:0.5">no tags</span>'}</div>
<span title="Created: ${formatDate(prompt.createdAt)} · Modified: ${formatDate(prompt.updatedAt)}">
🕒 ${formatDate(prompt.updatedAt)}
</span>
</div>
`;
// attach events
card.querySelector('.edit-btn').addEventListener('click', (e) => {
e.stopPropagation();
openModal(prompt.id);
});
card.querySelector('.delete-btn').addEventListener('click', (e) => {
e.stopPropagation();
if (confirm(`Delete prompt "${prompt.title}"?`)) {
prompts = prompts.filter(p => p.id !== prompt.id);
persist();
renderFilterTags();
renderPrompts();
}
});
grid.appendChild(card);
});
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>"']/g, (m) => ({
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
}[m]));
}
// ---------- modal logic ----------
const modal = document.getElementById('modal');
const modalTitle = document.getElementById('modalTitle');
const titleInput = document.getElementById('titleInput');
const descInput = document.getElementById('descInput');
const tagsInput = document.getElementById('tagsInput');
const promptTextInput = document.getElementById('promptTextInput');
function openModal(id = null) {
editingId = id;
modal.classList.add('open');
if (id) {
const p = prompts.find(x => x.id === id);
if (!p) return;
modalTitle.textContent = 'Edit prompt';
titleInput.value = p.title || '';
descInput.value = p.description || '';
tagsInput.value = (p.tags || []).join(', ');
promptTextInput.value = p.promptText || '';
} else {
modalTitle.textContent = 'New prompt';
titleInput.value = '';
descInput.value = '';
tagsInput.value = '';
promptTextInput.value = '';
}
setTimeout(() => titleInput.focus(), 50);
}
function closeModal() {
modal.classList.remove('open');
editingId = null;
}
function saveModal() {
const title = titleInput.value.trim();
const desc = descInput.value.trim();
const tags = tagsInput.value.split(',').map(t => t.trim().toLowerCase()).filter(t => t !== '');
const promptText = promptTextInput.value.trim();
if (!title || !promptText) {
alert('Title and prompt text are required.');
return;
}
if (editingId) {
const idx = prompts.findIndex(p => p.id === editingId);
if (idx !== -1) {
prompts[idx] = {
...prompts[idx],
title,
description: desc,
tags,
promptText,
updatedAt: Date.now()
};
}
} else {
const newId = 'p_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
prompts.push({
id: newId,
title,
description: desc,
tags,
promptText,
createdAt: Date.now(),
updatedAt: Date.now()
});
}
persist();
renderFilterTags();
renderPrompts();
closeModal();
}
// ---------- event listeners ----------
document.getElementById('newPromptBtn').addEventListener('click', () => openModal());
document.getElementById('cancelModalBtn').addEventListener('click', closeModal);
document.getElementById('savePromptBtn').addEventListener('click', saveModal);
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal();
});
document.getElementById('searchInput').addEventListener('input', renderPrompts);
document.getElementById('sortSelect').addEventListener('change', renderPrompts);
// keyboard: escape closes modal
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('open')) closeModal();
});
// ---------- init ----------
loadPrompts();
renderFilterTags();
renderPrompts();
})();
</script>
</body>
</html>
```
### Core Features
This tool is built around a simple, card-based workflow. You can quickly add new prompts, find existing ones, and keep everything organized with tags.
- **Prompt Management**: The main grid displays each prompt as a card. Use the "New prompt" button to add entries, and the edit (✏️) or delete (🗑️) icons on each card to modify or remove them.
- **Organization & Search**: The toolbar provides a keyword search box, clickable tag filters for quick categorization, and a dropdown to sort prompts by their creation or modification date.
- **Data Persistence**: All your prompts are saved automatically to your browser's local storage. The app is pre-loaded with a few example prompts so you can see how it works immediately.
Iniciá sesión para ver el prompt completo
Continuar con:
Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad
Uso
Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.
Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.
Referencias
- Categoría: Prompts de coding
- Fuente: https://x.com/itsPaulAi/status/1914402747106971846
Discusión
0 comentarios