Skip to content

Commit 60d4c00

Browse files
Merge pull request #47 from ThisIs-Developer/copilot/update-import-button-dropdown
Improve GitHub import modal readability and usability for desktop/mobile
2 parents b2c3c85 + a207b2f commit 60d4c00

3 files changed

Lines changed: 261 additions & 52 deletions

File tree

index.html

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,15 @@ <h1 class="h4 mb-0 me-2">Markdown Viewer</h1>
9999
<button id="toggle-sync" class="tool-button sync-enabled border-primary" title="Toggle Sync Scrolling">
100100
<i class="bi bi-link"></i> Sync Off
101101
</button>
102-
<button id="import-button" class="tool-button" title="Import Markdown">
103-
<i class="bi bi-upload"></i> Import
104-
</button>
105-
<button id="import-github-button" class="tool-button" title="Import Markdown from GitHub URL">
106-
<i class="bi bi-github"></i> GitHub Import
107-
</button>
102+
<div class="dropdown">
103+
<button class="tool-button dropdown-toggle" type="button" id="importDropdown" data-bs-toggle="dropdown" aria-expanded="false" title="Import Markdown">
104+
<i class="bi bi-upload"></i> Import
105+
</button>
106+
<ul class="dropdown-menu" aria-labelledby="importDropdown">
107+
<li><a class="dropdown-item" href="#" id="import-from-file">From files</a></li>
108+
<li><a class="dropdown-item" href="#" id="import-from-github">From GitHub</a></li>
109+
</ul>
110+
</div>
108111
<input type="file" id="file-input" class="file-input" accept=".md,.markdown,text/markdown">
109112

110113
<div class="dropdown">
@@ -194,11 +197,11 @@ <h5>Menu</h5>
194197
<i class="bi bi-link"></i> Sync Off
195198
</button>
196199

197-
<button id="mobile-import-button" class="mobile-menu-item" title="Import Markdown">
198-
<i class="bi bi-upload me-2"></i> Import Markdown
200+
<button id="mobile-import-button" class="mobile-menu-item" title="Import from files">
201+
<i class="bi bi-upload me-2"></i> Import from files
199202
</button>
200203

201-
<button id="mobile-import-github-button" class="mobile-menu-item" title="Import Markdown from GitHub URL">
204+
<button id="mobile-import-github-button" class="mobile-menu-item" title="Import from GitHub">
202205
<i class="bi bi-github me-2"></i> Import from GitHub
203206
</button>
204207

@@ -264,6 +267,20 @@ <h5>Menu</h5>
264267
</div>
265268
</div>
266269

270+
<!-- GitHub Import Modal -->
271+
<div id="github-import-modal" class="reset-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="github-import-title" style="display:none;">
272+
<div class="reset-modal-box">
273+
<p id="github-import-title" class="reset-modal-message">Import Markdown from GitHub</p>
274+
<input type="url" id="github-import-url" class="rename-modal-input" placeholder="https://github.com/owner/repo/blob/main/README.md" />
275+
<select id="github-import-file-select" class="rename-modal-input" style="display:none;"></select>
276+
<p id="github-import-error" class="github-import-error" style="display:none;"></p>
277+
<div class="reset-modal-actions">
278+
<button class="reset-modal-btn reset-modal-cancel" id="github-import-cancel">Cancel</button>
279+
<button class="reset-modal-btn" id="github-import-submit">Import</button>
280+
</div>
281+
</div>
282+
</div>
283+
267284
<div id="dropzone" class="dropzone container-fluid mt-2">
268285
<button id="close-dropzone" class="close-btn" title="Close dropzone">
269286
<i class="bi bi-x-lg"></i>

script.js

Lines changed: 172 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ document.addEventListener("DOMContentLoaded", function () {
1313
const markdownEditor = document.getElementById("markdown-editor");
1414
const markdownPreview = document.getElementById("markdown-preview");
1515
const themeToggle = document.getElementById("theme-toggle");
16-
const importButton = document.getElementById("import-button");
17-
const importGithubButton = document.getElementById("import-github-button");
16+
const importFromFileButton = document.getElementById("import-from-file");
17+
const importFromGithubButton = document.getElementById("import-from-github");
1818
const fileInput = document.getElementById("file-input");
1919
const exportMd = document.getElementById("export-md");
2020
const exportHtml = document.getElementById("export-html");
@@ -61,6 +61,13 @@ document.addEventListener("DOMContentLoaded", function () {
6161
const mobileThemeToggle = document.getElementById("mobile-theme-toggle");
6262
const shareButton = document.getElementById("share-button");
6363
const mobileShareButton = document.getElementById("mobile-share-button");
64+
const githubImportModal = document.getElementById("github-import-modal");
65+
const githubImportTitle = document.getElementById("github-import-title");
66+
const githubImportUrlInput = document.getElementById("github-import-url");
67+
const githubImportFileSelect = document.getElementById("github-import-file-select");
68+
const githubImportError = document.getElementById("github-import-error");
69+
const githubImportCancelBtn = document.getElementById("github-import-cancel");
70+
const githubImportSubmitBtn = document.getElementById("github-import-submit");
6471

6572
// Check dark mode preference first for proper initialization
6673
const prefersDarkMode =
@@ -918,71 +925,165 @@ This is a fully client-side application. Your content never leaves your browser
918925
}
919926

920927
function setGitHubImportLoading(isLoading) {
921-
[importGithubButton, mobileImportGithubBtn].forEach((btn) => {
922-
if (!btn) return;
923-
if (!btn.dataset.originalText) {
924-
btn.dataset.originalText = btn.innerHTML;
925-
}
926-
btn.disabled = isLoading;
927-
btn.innerHTML = isLoading ? '<i class="bi bi-hourglass-split"></i> Importing...' : btn.dataset.originalText;
928-
});
928+
if (!githubImportSubmitBtn) return;
929+
if (isLoading) {
930+
githubImportSubmitBtn.dataset.loadingText = githubImportSubmitBtn.textContent;
931+
githubImportSubmitBtn.textContent = "Importing...";
932+
} else if (githubImportSubmitBtn.dataset.loadingText) {
933+
githubImportSubmitBtn.textContent = githubImportSubmitBtn.dataset.loadingText;
934+
delete githubImportSubmitBtn.dataset.loadingText;
935+
}
936+
}
937+
938+
function setGitHubImportMessage(message, options = {}) {
939+
if (!githubImportError) return;
940+
const { isError = true } = options;
941+
githubImportError.classList.toggle("is-info", !isError);
942+
if (!message) {
943+
githubImportError.textContent = "";
944+
githubImportError.style.display = "none";
945+
return;
946+
}
947+
githubImportError.textContent = message;
948+
githubImportError.style.display = "block";
949+
}
950+
951+
function resetGitHubImportModal() {
952+
if (!githubImportUrlInput || !githubImportFileSelect || !githubImportSubmitBtn) return;
953+
if (githubImportTitle) {
954+
githubImportTitle.textContent = "Import Markdown from GitHub";
955+
}
956+
githubImportUrlInput.value = "";
957+
githubImportUrlInput.style.display = "block";
958+
githubImportUrlInput.disabled = false;
959+
githubImportFileSelect.innerHTML = "";
960+
githubImportFileSelect.style.display = "none";
961+
githubImportFileSelect.disabled = false;
962+
githubImportSubmitBtn.dataset.step = "url";
963+
delete githubImportSubmitBtn.dataset.owner;
964+
delete githubImportSubmitBtn.dataset.repo;
965+
delete githubImportSubmitBtn.dataset.ref;
966+
githubImportSubmitBtn.textContent = "Import";
967+
setGitHubImportMessage("");
968+
}
969+
970+
function openGitHubImportModal() {
971+
if (!githubImportModal || !githubImportUrlInput || !githubImportSubmitBtn) return;
972+
resetGitHubImportModal();
973+
githubImportModal.style.display = "flex";
974+
githubImportUrlInput.focus();
929975
}
930976

931-
async function importMarkdownFromGitHub() {
932-
const urlInput = prompt("Enter a GitHub repository, folder, or Markdown file URL:");
933-
if (!urlInput) return;
977+
function closeGitHubImportModal() {
978+
if (!githubImportModal) return;
979+
githubImportModal.style.display = "none";
980+
resetGitHubImportModal();
981+
}
982+
983+
async function handleGitHubImportSubmit() {
984+
if (!githubImportSubmitBtn || !githubImportUrlInput || !githubImportFileSelect) return;
985+
const setGitHubImportDialogDisabled = (disabled) => {
986+
githubImportSubmitBtn.disabled = disabled;
987+
if (githubImportCancelBtn) {
988+
githubImportCancelBtn.disabled = disabled;
989+
}
990+
};
991+
const step = githubImportSubmitBtn.dataset.step || "url";
992+
if (step === "select") {
993+
const selectedPath = githubImportFileSelect.value;
994+
const owner = githubImportSubmitBtn.dataset.owner;
995+
const repo = githubImportSubmitBtn.dataset.repo;
996+
const ref = githubImportSubmitBtn.dataset.ref;
997+
if (!owner || !repo || !ref || !selectedPath) {
998+
setGitHubImportMessage("Please select a file to import.");
999+
return;
1000+
}
1001+
setGitHubImportLoading(true);
1002+
setGitHubImportDialogDisabled(true);
1003+
try {
1004+
const markdown = await fetchTextContent(buildRawGitHubUrl(owner, repo, ref, selectedPath));
1005+
newTab(markdown, getFileName(selectedPath).replace(/\.(md|markdown)$/i, ""));
1006+
closeGitHubImportModal();
1007+
} catch (error) {
1008+
console.error("GitHub import failed:", error);
1009+
setGitHubImportMessage("GitHub import failed: " + error.message);
1010+
} finally {
1011+
setGitHubImportDialogDisabled(false);
1012+
setGitHubImportLoading(false);
1013+
}
1014+
return;
1015+
}
1016+
1017+
const urlInput = githubImportUrlInput.value.trim();
1018+
if (!urlInput) {
1019+
setGitHubImportMessage("Please enter a GitHub URL.");
1020+
return;
1021+
}
9341022

9351023
const parsed = parseGitHubImportUrl(urlInput);
9361024
if (!parsed || !parsed.owner || !parsed.repo) {
937-
alert("Please enter a valid GitHub URL.");
1025+
setGitHubImportMessage("Please enter a valid GitHub URL.");
9381026
return;
9391027
}
9401028

1029+
setGitHubImportMessage("");
9411030
setGitHubImportLoading(true);
1031+
setGitHubImportDialogDisabled(true);
9421032
try {
9431033
if (parsed.type === "file") {
9441034
if (!isMarkdownPath(parsed.filePath)) {
9451035
throw new Error("The provided URL does not point to a Markdown file.");
9461036
}
9471037
const markdown = await fetchTextContent(buildRawGitHubUrl(parsed.owner, parsed.repo, parsed.ref, parsed.filePath));
9481038
newTab(markdown, getFileName(parsed.filePath).replace(/\.(md|markdown)$/i, ""));
1039+
closeGitHubImportModal();
9491040
return;
9501041
}
9511042

9521043
const ref = parsed.ref || await getDefaultBranch(parsed.owner, parsed.repo);
9531044
const files = await listMarkdownFiles(parsed.owner, parsed.repo, ref, parsed.basePath || "");
9541045

9551046
if (!files.length) {
956-
alert("No Markdown files were found at that GitHub location.");
1047+
setGitHubImportMessage("No Markdown files were found at that GitHub location.");
9571048
return;
9581049
}
9591050

960-
let targetPath = files[0];
961-
if (files.length > 1) {
962-
const maxShown = Math.min(files.length, MAX_GITHUB_FILES_SHOWN);
963-
const choices = files
964-
.slice(0, maxShown)
965-
.map((file, index) => `${index + 1}. ${file}`)
966-
.join("\n");
967-
const choice = prompt(
968-
`Found ${files.length} Markdown files.\nEnter a number to import (showing first ${maxShown}):\n\n${choices}${files.length > maxShown ? "\n..." : ""}`,
969-
"1"
970-
);
971-
972-
if (choice === null) return;
973-
const selectedIndex = Number(choice) - 1;
974-
if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex >= maxShown) {
975-
throw new Error(`Please enter a number between 1 and ${maxShown} (from the displayed list).`);
976-
}
977-
targetPath = files[selectedIndex];
1051+
const shownFiles = files.slice(0, MAX_GITHUB_FILES_SHOWN);
1052+
if (files.length === 1) {
1053+
const targetPath = files[0];
1054+
const markdown = await fetchTextContent(buildRawGitHubUrl(parsed.owner, parsed.repo, ref, targetPath));
1055+
newTab(markdown, getFileName(targetPath).replace(/\.(md|markdown)$/i, ""));
1056+
closeGitHubImportModal();
1057+
return;
9781058
}
9791059

980-
const markdown = await fetchTextContent(buildRawGitHubUrl(parsed.owner, parsed.repo, ref, targetPath));
981-
newTab(markdown, getFileName(targetPath).replace(/\.(md|markdown)$/i, ""));
1060+
githubImportUrlInput.style.display = "none";
1061+
githubImportFileSelect.style.display = "block";
1062+
githubImportFileSelect.innerHTML = "";
1063+
shownFiles.forEach((filePath) => {
1064+
const option = document.createElement("option");
1065+
option.value = filePath;
1066+
option.textContent = filePath;
1067+
githubImportFileSelect.appendChild(option);
1068+
});
1069+
if (files.length > MAX_GITHUB_FILES_SHOWN) {
1070+
setGitHubImportMessage(`Showing first ${MAX_GITHUB_FILES_SHOWN} of ${files.length} Markdown files.`, { isError: false });
1071+
} else {
1072+
setGitHubImportMessage("");
1073+
}
1074+
if (githubImportTitle) {
1075+
githubImportTitle.textContent = "Select a Markdown file to import";
1076+
}
1077+
githubImportSubmitBtn.dataset.step = "select";
1078+
githubImportSubmitBtn.dataset.owner = parsed.owner;
1079+
githubImportSubmitBtn.dataset.repo = parsed.repo;
1080+
githubImportSubmitBtn.dataset.ref = ref;
1081+
githubImportSubmitBtn.textContent = "Import Selected";
9821082
} catch (error) {
9831083
console.error("GitHub import failed:", error);
984-
alert("GitHub import failed: " + error.message);
1084+
setGitHubImportMessage("GitHub import failed: " + error.message);
9851085
} finally {
1086+
setGitHubImportDialogDisabled(false);
9861087
setGitHubImportLoading(false);
9871088
}
9881089
}
@@ -1317,7 +1418,8 @@ This is a fully client-side application. Your content never leaves your browser
13171418
});
13181419
mobileImportBtn.addEventListener("click", () => fileInput.click());
13191420
mobileImportGithubBtn.addEventListener("click", () => {
1320-
importMarkdownFromGitHub().finally(closeMobileMenu);
1421+
closeMobileMenu();
1422+
openGitHubImportModal();
13211423
});
13221424
mobileExportMd.addEventListener("click", () => exportMd.click());
13231425
mobileExportHtml.addEventListener("click", () => exportHtml.click());
@@ -1417,13 +1519,40 @@ This is a fully client-side application. Your content never leaves your browser
14171519
renderMarkdown();
14181520
});
14191521

1420-
importButton.addEventListener("click", function () {
1421-
fileInput.click();
1422-
});
1522+
if (importFromFileButton) {
1523+
importFromFileButton.addEventListener("click", function (e) {
1524+
e.preventDefault();
1525+
fileInput.click();
1526+
});
1527+
}
14231528

1424-
importGithubButton.addEventListener("click", function () {
1425-
importMarkdownFromGitHub();
1426-
});
1529+
if (importFromGithubButton) {
1530+
importFromGithubButton.addEventListener("click", function (e) {
1531+
e.preventDefault();
1532+
openGitHubImportModal();
1533+
});
1534+
}
1535+
1536+
if (githubImportSubmitBtn) {
1537+
githubImportSubmitBtn.addEventListener("click", handleGitHubImportSubmit);
1538+
}
1539+
if (githubImportCancelBtn) {
1540+
githubImportCancelBtn.addEventListener("click", closeGitHubImportModal);
1541+
}
1542+
const handleGitHubImportInputKeydown = function(e) {
1543+
if (e.key === "Enter") {
1544+
e.preventDefault();
1545+
handleGitHubImportSubmit();
1546+
} else if (e.key === "Escape") {
1547+
closeGitHubImportModal();
1548+
}
1549+
};
1550+
if (githubImportUrlInput) {
1551+
githubImportUrlInput.addEventListener("keydown", handleGitHubImportInputKeydown);
1552+
}
1553+
if (githubImportFileSelect) {
1554+
githubImportFileSelect.addEventListener("keydown", handleGitHubImportInputKeydown);
1555+
}
14271556

14281557
fileInput.addEventListener("change", function (e) {
14291558
const file = e.target.files[0];

0 commit comments

Comments
 (0)