Compare commits
11 Commits
36a6d72586
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
eda9adda03
|
|||
|
dd97d4f64a
|
|||
|
35875165b0
|
|||
|
ca31b346f3
|
|||
|
2a93332653
|
|||
|
010a886003
|
|||
|
854b8f7c95
|
|||
|
511db76391
|
|||
|
aeefb375fb
|
|||
|
3901944b05
|
|||
|
f1de42f897
|
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./icon-192.png" />
|
<img src="https://pascar.run/icon-192.png" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -31,12 +31,11 @@ runs entirely in your browser - no server, no internet.
|
|||||||
## Portability
|
## Portability
|
||||||
Pascar is a self-contained single HTML file.
|
Pascar is a self-contained single HTML file.
|
||||||
|
|
||||||
If you want this to be portable in your USB stick, you just need to download this single file [pascar.html](https://github.com/potentzero/pascar/releases/latest/download/pascar.html) and use it.
|
If you want this to be portable in your USB stick, you just need to download this single file [pascar.html](https://git.sr.ht/~mysticmode/pascar/blob/main/index.html) and use it.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This software is license under **MIT License**. Please refer the [LICENSE](./LICENSE) text for more detail.
|
This software is license under **MIT License**. Please refer the [LICENSE](./LICENSE) text for more detail.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Feel free to contribute by creating an issue or diving into the code and PRs.
|
I do not accept external contributions. For feature requests and bug reports,
|
||||||
|
please get in touch with me at my email - tildezero@gmail.com
|
||||||
I do not have a Code of Conduct, but a simple rule - be nice when you deal with others.
|
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+131
-4
@@ -185,6 +185,84 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Textarea */
|
/* Textarea */
|
||||||
|
#editor-panel {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview-panel {
|
||||||
|
display: none;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: auto;
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
background: #fff;
|
||||||
|
color: #222;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview-panel.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview h1,
|
||||||
|
#preview h2,
|
||||||
|
#preview h3,
|
||||||
|
#preview h4,
|
||||||
|
#preview h5,
|
||||||
|
#preview h6 {
|
||||||
|
color: #222222;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview p,
|
||||||
|
#preview li,
|
||||||
|
#preview pre,
|
||||||
|
#preview code,
|
||||||
|
#preview blockquote {
|
||||||
|
color: #222222;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview pre {
|
||||||
|
background: #111111;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview code {
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview a {
|
||||||
|
color: #7db4ff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview blockquote {
|
||||||
|
border-left: 4px solid #555555;
|
||||||
|
padding-left: 12px;
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview blockquote p {
|
||||||
|
color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
#editor {
|
#editor {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
@@ -522,6 +600,7 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -647,8 +726,13 @@
|
|||||||
|
|
||||||
<!-- Main Editor Area -->
|
<!-- Main Editor Area -->
|
||||||
<div id="editor-area">
|
<div id="editor-area">
|
||||||
|
<div id="editor-panel">
|
||||||
<div id="line-numbers"></div>
|
<div id="line-numbers"></div>
|
||||||
<textarea id="editor" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea>
|
<textarea id="editor" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea>
|
||||||
|
</div>
|
||||||
|
<div id="preview-panel" class="preview-panel">
|
||||||
|
<div id="preview"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal Overlay -->
|
<!-- Modal Overlay -->
|
||||||
<div id="dialog-overlay">
|
<div id="dialog-overlay">
|
||||||
@@ -952,6 +1036,8 @@
|
|||||||
const gotoBar = document.getElementById("goto-bar");
|
const gotoBar = document.getElementById("goto-bar");
|
||||||
const gotoInput = document.getElementById("goto-input");
|
const gotoInput = document.getElementById("goto-input");
|
||||||
const menuBar = document.getElementById("menu-bar");
|
const menuBar = document.getElementById("menu-bar");
|
||||||
|
const previewPanel = document.getElementById("preview-panel");
|
||||||
|
const preview = document.getElementById("preview");
|
||||||
|
|
||||||
// -- State
|
// -- State
|
||||||
let fileName = "Untitled";
|
let fileName = "Untitled";
|
||||||
@@ -964,13 +1050,14 @@
|
|||||||
let lastReplace = "";
|
let lastReplace = "";
|
||||||
let notifTimer = null;
|
let notifTimer = null;
|
||||||
let replaceMode = false;
|
let replaceMode = false;
|
||||||
|
let markdownPreview = false;
|
||||||
|
|
||||||
// -- Initial Content
|
// -- Initial Content
|
||||||
editor.value = [
|
editor.value = [
|
||||||
"Pascar",
|
"Pascar",
|
||||||
"======",
|
"======",
|
||||||
"",
|
"",
|
||||||
"A faithful offline recreation of the classic MS-DOS Editor",
|
"A browser-based offline recreation of the classic MS-DOS Editor for the modern needs of today.",
|
||||||
"Runs entirely in your browser - no server, no internet.",
|
"Runs entirely in your browser - no server, no internet.",
|
||||||
"",
|
"",
|
||||||
"QUICK START",
|
"QUICK START",
|
||||||
@@ -1037,6 +1124,36 @@
|
|||||||
(isModified ? "* " : "") + fileName + " - Pascar";
|
(isModified ? "* " : "") + fileName + " - Pascar";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMarkdownFile(name) {
|
||||||
|
return /\.md$/i.test(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMarkdownPreview(active) {
|
||||||
|
markdownPreview = active;
|
||||||
|
previewPanel.classList.toggle("active", active);
|
||||||
|
if (active) {
|
||||||
|
renderMarkdownPreview();
|
||||||
|
} else {
|
||||||
|
preview.innerHTML = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdownPreview() {
|
||||||
|
if (!markdownPreview) return;
|
||||||
|
if (typeof marked === "undefined") {
|
||||||
|
preview.innerHTML =
|
||||||
|
"<p><em>Marked.js failed to load. Markdown preview is unavailable.</em></p>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
preview.innerHTML = marked.parse(editor.value || "");
|
||||||
|
} catch (err) {
|
||||||
|
preview.innerHTML =
|
||||||
|
`<p><em>Error rendering markdown:</em></p><pre>${escHtml(err.message)}</pre>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -- Notifications
|
// -- Notifications
|
||||||
function showNotif(msg, dur = 2500) {
|
function showNotif(msg, dur = 2500) {
|
||||||
notif.textContent = msg;
|
notif.textContent = msg;
|
||||||
@@ -1049,6 +1166,7 @@
|
|||||||
editor.addEventListener("input", () => {
|
editor.addEventListener("input", () => {
|
||||||
isModified = true;
|
isModified = true;
|
||||||
updateAll();
|
updateAll();
|
||||||
|
if (markdownPreview) renderMarkdownPreview();
|
||||||
});
|
});
|
||||||
editor.addEventListener("keyup", updateStatus);
|
editor.addEventListener("keyup", updateStatus);
|
||||||
editor.addEventListener("click", updateStatus);
|
editor.addEventListener("click", updateStatus);
|
||||||
@@ -1196,7 +1314,13 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
if (e.target.closest("#menu-bar")) closeAll();
|
// If the click is inside the menu-bar, do not close all
|
||||||
|
if (e.target.closest("#menu-bar")) return;
|
||||||
|
|
||||||
|
// If any menu-item or dropdown is open, close all
|
||||||
|
if (document.querySelectorAll('.menu-item.active, .dropdown.open').length > 0) {
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// -- Global Keyboard Shortcuts
|
// -- Global Keyboard Shortcuts
|
||||||
@@ -1444,6 +1568,7 @@
|
|||||||
fileName = "Untitled";
|
fileName = "Untitled";
|
||||||
fileHandle = null;
|
fileHandle = null;
|
||||||
isModified = false;
|
isModified = false;
|
||||||
|
setMarkdownPreview(false);
|
||||||
updateAll();
|
updateAll();
|
||||||
editor.focus();
|
editor.focus();
|
||||||
}
|
}
|
||||||
@@ -1493,6 +1618,7 @@
|
|||||||
fileName = file.name;
|
fileName = file.name;
|
||||||
editor.value = await file.text();
|
editor.value = await file.text();
|
||||||
isModified = false;
|
isModified = false;
|
||||||
|
setMarkdownPreview(isMarkdownFile(fileName));
|
||||||
updateAll();
|
updateAll();
|
||||||
editor.focus();
|
editor.focus();
|
||||||
showNotif(`Opened: ${fileName}`);
|
showNotif(`Opened: ${fileName}`);
|
||||||
@@ -1513,6 +1639,7 @@
|
|||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
editor.value = e.target.result;
|
editor.value = e.target.result;
|
||||||
isModified = false;
|
isModified = false;
|
||||||
|
setMarkdownPreview(isMarkdownFile(fileName));
|
||||||
updateAll();
|
updateAll();
|
||||||
editor.focus();
|
editor.focus();
|
||||||
showNotif(`Opened: ${fileName}`);
|
showNotif(`Opened: ${fileName}`);
|
||||||
@@ -1576,6 +1703,7 @@
|
|||||||
await writable.write(editor.value);
|
await writable.write(editor.value);
|
||||||
await writable.close();
|
await writable.close();
|
||||||
isModified = false;
|
isModified = false;
|
||||||
|
setMarkdownPreview(isMarkdownFile(fileName));
|
||||||
updateStatus();
|
updateStatus();
|
||||||
showNotif(`Saved: ${fileName}`);
|
showNotif(`Saved: ${fileName}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -1904,7 +2032,6 @@
|
|||||||
});
|
});
|
||||||
dialogOv.addEventListener("click", (e) => {
|
dialogOv.addEventListener("click", (e) => {
|
||||||
if (e.target.id == "source-code-link") {
|
if (e.target.id == "source-code-link") {
|
||||||
window.open("https://github.com/potentzero/pascar", "_blank");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -1927,7 +2054,7 @@
|
|||||||
Open & save files via your local filesystem.
|
Open & save files via your local filesystem.
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:14px;line-height:1.6;text-align:center;">
|
<div style="font-size:14px;line-height:1.6;text-align:center;">
|
||||||
<a id="source-code-link" style="color:#295991;" href="https://github.com/potentzero/pascar" target="_blank">Source code</a>
|
<a id="source-code-link" style="color:#295991;" href="https://git.ennt.io/mysticmode/pascar" target="_blank">Source code</a>
|
||||||
by Nirmal Kumar R.
|
by Nirmal Kumar R.
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-buttons"><button class="btn primary" id="d-about-ok">OK</button></div>
|
<div class="dialog-buttons"><button class="btn primary" id="d-about-ok">OK</button></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user