Skip to content

Commit 8da9b90

Browse files
smoother prompt adding and buttons (fixes #35) (fixes#39) (#40)
1 parent 95946f3 commit 8da9b90

File tree

1 file changed

+111
-17
lines changed

1 file changed

+111
-17
lines changed

index.html

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
#list > ul { padding-left: 0; }
2929
.tree-dir { display: flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 10px; cursor: pointer; color: var(--text); }
3030
.tree-dir:hover { background: #141936; }
31+
.tree-dir.submenu-open { background: #151f3c; border: 1px solid var(--accent); }
3132
.tree-dir button { appearance: none; border: none; background: none; color: var(--muted); cursor: pointer; font-size: 12px; display: flex; align-items: center; justify-content: center; width: 18px; }
3233
.tree-dir button:focus { outline: 1px solid var(--accent); border-radius: 4px; }
33-
.tree-dir .folder-name { font-size: 13px; font-weight: 600; }
34-
.tree-dir .github-folder-icon { margin-left: 4px; cursor: pointer; opacity: 0.6; transition: opacity 0.2s; font-size: 12px; }
35-
.tree-dir .github-folder-icon:hover { opacity: 1; }
36-
.tree-dir .add-file-icon { margin-left: 4px; cursor: pointer; opacity: 0.6; transition: opacity 0.2s; font-size: 12px; color: #4ade80; }
37-
.tree-dir .add-file-icon:hover { opacity: 1; }
34+
.tree-dir .folder-name { font-size: 13px; font-weight: 600; flex: 1; }
35+
.tree-dir .folder-icons { display: flex; gap: 4px; margin-left: auto; }
36+
.tree-dir .folder-icons span { cursor: pointer; opacity: 0.6; transition: all 0.2s; padding: 4px; border-radius: 4px; min-width: 28px; min-height: 28px; display: flex; align-items: center; justify-content: center; }
37+
.tree-dir .folder-icons span:hover { opacity: 1; background: rgba(255, 255, 255, 0.05); }
38+
.tree-dir .add-file-icon { color: #4ade80; font-weight: bold; }
39+
.tree-dir .add-file-icon:hover { background: rgba(74, 222, 128, 0.1) !important; }
3840
.item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 10px; border-radius: 10px; border: 1px solid transparent; background: #12162a; cursor: pointer; }
3941
.item:hover { border-color: var(--border); background: #141936; }
4042
.item-title { font-size: 14px; font-weight: 600; }
@@ -57,7 +59,6 @@
5759
footer { max-width: 1100px; margin: 20px auto 40px; color: var(--muted); font-size: 13px; padding: 0 16px; }
5860
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 999px; border: 1px solid var(--border); background: #11152a; font-size: 12px; color: var(--muted); }
5961

60-
/* Branch dropdown styling */
6162
#branchSelect optgroup {
6263
font-weight: 600;
6364
color: var(--muted);
@@ -93,7 +94,6 @@
9394
</div>
9495
<div style="display:flex; align-items:center; gap:8px;">
9596
<div class="pill" id="repoPill">Loading repo...</div>
96-
<!-- Chevron + branch select -->
9797
<label style="display:flex; align-items:center; gap:4px;">
9898
<span title="Choose a branch"></span>
9999
<select id="branchSelect" class="btn" style="min-width:140px;">
@@ -170,6 +170,8 @@ <h1 id="title" style="display:none"></h1>
170170
let currentSlug = null;
171171
let expandedState = new Set();
172172
let expandedStateKey = null;
173+
let openSubmenus = new Set();
174+
let activeSubmenuHeaders = new Set();
173175

174176
const listEl = document.getElementById('list');
175177
const contentEl= document.getElementById('content');
@@ -194,12 +196,10 @@ <h1 id="title" style="display:none"></h1>
194196
const CODEX_URL_REGEX = /^https:\/\/chatgpt\.com\/s\/[a-f0-9_]+$/i;
195197

196198
async function resolveGistRawUrl(gistUrl) {
197-
// If it's already a raw URL, use it directly
198199
if (GIST_POINTER_REGEX.test(gistUrl)) {
199200
return gistUrl;
200201
}
201202

202-
// Parse regular gist URLs
203203
const match = gistUrl.match(/^https:\/\/gist\.github\.com\/([\w-]+)\/([a-f0-9]+)\/?(?:#file-([\w.-]+))?(?:\?file=([\w.-]+))?$/i);
204204
if (!match) {
205205
throw new Error('Invalid gist URL format');
@@ -209,10 +209,8 @@ <h1 id="title" style="display:none"></h1>
209209
const targetFile = fragmentFile || queryFile;
210210

211211
if (targetFile) {
212-
// Specific file requested
213212
return `https://gist.githubusercontent.com/${user}/${gistId}/raw/${targetFile}`;
214213
} else {
215-
// No specific file - fetch gist metadata to find the best file
216214
const apiUrl = `https://api.github.com/gists/${gistId}`;
217215
const res = await fetch(viaProxy(apiUrl));
218216
if (!res.ok) {
@@ -279,6 +277,17 @@ <h1 id="title" style="display:none"></h1>
279277
} catch {}
280278
}
281279

280+
function closeAllSubmenus() {
281+
openSubmenus.forEach(submenu => {
282+
submenu.style.display = 'none';
283+
});
284+
activeSubmenuHeaders.forEach(header => {
285+
header.classList.remove('submenu-open');
286+
});
287+
openSubmenus.clear();
288+
activeSubmenuHeaders.clear();
289+
}
290+
282291
function prettyTitle(name) {
283292
const base = name.replace(/\.md$/i, "");
284293
const addEmoji = (s) => {
@@ -478,6 +487,9 @@ <h1 id="title" style="display:none"></h1>
478487
label.className = 'folder-name';
479488
label.textContent = entry.name;
480489

490+
const iconsContainer = document.createElement('div');
491+
iconsContainer.className = 'folder-icons';
492+
481493
const ghIcon = document.createElement('span');
482494
ghIcon.className = 'github-folder-icon';
483495
ghIcon.textContent = '🗂️';
@@ -492,17 +504,99 @@ <h1 id="title" style="display:none"></h1>
492504
addIcon.className = 'add-file-icon';
493505
addIcon.textContent = '+';
494506
addIcon.title = 'Create new file in this directory';
495-
addIcon.addEventListener('click', (ev) => {
496-
ev.stopPropagation();
507+
508+
const submenu = document.createElement('div');
509+
submenu.style.position = 'absolute';
510+
submenu.style.background = 'var(--card)';
511+
submenu.style.border = '1px solid var(--border)';
512+
submenu.style.borderRadius = '8px';
513+
submenu.style.padding = '6px 0';
514+
submenu.style.boxShadow = '0 4px 10px rgba(0,0,0,0.3)';
515+
submenu.style.display = 'none';
516+
submenu.style.zIndex = '10';
517+
518+
const makeMenuItem = (label, emoji, onClick) => {
519+
const item = document.createElement('div');
520+
item.textContent = `${emoji} ${label}`;
521+
item.style.padding = '6px 14px';
522+
item.style.cursor = 'pointer';
523+
item.style.fontSize = '13px';
524+
item.style.color = 'var(--text)';
525+
item.addEventListener('mouseenter', () => item.style.background = '#1a1f35');
526+
item.addEventListener('mouseleave', () => item.style.background = 'transparent');
527+
item.addEventListener('click', (e) => {
528+
e.stopPropagation();
529+
submenu.style.display = 'none';
530+
onClick();
531+
});
532+
return item;
533+
};
534+
535+
submenu.appendChild(makeMenuItem("Prompt (blank)", "📝", () => {
497536
const newFilePath = entry.path ? `${entry.path}/new-prompt.md` : 'new-prompt.md';
498-
const ghUrl = `https://github.com/${currentOwner}/${currentRepo}/new/${currentBranch}?filename=${newFilePath}`;
537+
const ghUrl = `https://github.com/${currentOwner}/${currentRepo}/new/${currentBranch}?filename=${encodeURIComponent(newFilePath)}`;
499538
window.open(ghUrl, '_blank', 'noopener,noreferrer');
539+
}));
540+
541+
submenu.appendChild(makeMenuItem("Conversation (template)", "💬", () => {
542+
const template = `**Conversation Link (Codex, Jules, etc):** [https://chatgpt.com/s/...]\n\n### Prompt\n[paste your full prompt here]\n\n### Additional Info\n[context, notes, or follow-up thoughts]\n`;
543+
const encoded = encodeURIComponent(template);
544+
const newFilePath = entry.path ? `${entry.path}/new-conversation.md` : 'new-conversation.md';
545+
const ghUrl = `https://github.com/${currentOwner}/${currentRepo}/new/${currentBranch}?filename=${encodeURIComponent(newFilePath)}&value=${encoded}`;
546+
window.open(ghUrl, '_blank', 'noopener,noreferrer');
547+
}));
548+
549+
document.body.appendChild(submenu);
550+
551+
addIcon.addEventListener('click', (ev) => {
552+
ev.stopPropagation();
553+
554+
const wasOpen = submenu.style.display === 'block';
555+
closeAllSubmenus();
556+
557+
if (!wasOpen) {
558+
const rect = addIcon.getBoundingClientRect();
559+
560+
submenu.style.display = 'block';
561+
submenu.style.visibility = 'hidden';
562+
const submenuRect = submenu.getBoundingClientRect();
563+
564+
let left = rect.right;
565+
let top = rect.top;
566+
567+
if (left + submenuRect.width > window.innerWidth - 10) {
568+
left = rect.left - submenuRect.width;
569+
}
570+
571+
if (top + submenuRect.height > window.innerHeight - 10) {
572+
top = rect.bottom - submenuRect.height;
573+
}
574+
575+
if (left < 10) {
576+
left = 10;
577+
}
578+
579+
if (top < 10) {
580+
top = 10;
581+
}
582+
583+
submenu.style.left = left + 'px';
584+
submenu.style.top = top + 'px';
585+
submenu.style.visibility = 'visible';
586+
openSubmenus.add(submenu);
587+
header.classList.add('submenu-open');
588+
activeSubmenuHeaders.add(header);
589+
}
500590
});
591+
592+
document.addEventListener('click', () => closeAllSubmenus());
593+
594+
iconsContainer.appendChild(ghIcon);
595+
iconsContainer.appendChild(addIcon);
501596

502597
header.appendChild(toggle);
503598
header.appendChild(label);
504-
header.appendChild(ghIcon);
505-
header.appendChild(addIcon);
599+
header.appendChild(iconsContainer);
506600
li.appendChild(header);
507601

508602
const childList = document.createElement('ul');
@@ -903,7 +997,7 @@ <h1 id="title" style="display:none"></h1>
903997
}
904998

905999
if (userBranches.length > 0) {
906-
const showUsers = localStorage.getItem('showUserBranches') !== 'false'; // default: true
1000+
const showUsers = localStorage.getItem('showUserBranches') !== 'false';
9071001
const userGroup = document.createElement('optgroup');
9081002

9091003
const userHeaderOpt = document.createElement('option');

0 commit comments

Comments
 (0)