Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.

Commit 9509df0

Browse files
committed
export to html
1 parent 667edae commit 9509df0

5 files changed

Lines changed: 150 additions & 3 deletions

File tree

neutralino.config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"nativeAllowList": [
1717
"app.*",
1818
"os.*",
19+
"filesystem.*",
1920
"debug.log"
2021
],
2122
"globalVariables": {
@@ -80,4 +81,4 @@
8081
"binaryVersion": "6.5.0",
8182
"clientVersion": "6.5.0"
8283
}
83-
}
84+
}

resources/index.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
<div class="app" id="appRoot">
1313
<header class="topbar">
1414
<h1>LiteMark</h1>
15-
<p id="fileLabel">No file loaded</p>
15+
<div class="topbarActions">
16+
<p id="fileLabel">No file loaded</p>
17+
<button id="exportHtmlButton" type="button">Export HTML</button>
18+
</div>
1619
</header>
1720

1821
<div class="workspace">
@@ -33,10 +36,12 @@ <h2 class="panelTitle">Preview</h2>
3336
</div>
3437

3538
<!-- scripts -->
39+
<script src="/neutralino.js"></script>
3640
<script src="/js/core/appContext.js"></script>
3741
<script src="/js/core/markdownParser.js"></script>
3842
<script src="/js/features/syncScroll.js"></script>
3943
<script src="/js/features/editorPreview.js"></script>
44+
<script src="/js/features/exportHtml.js"></script>
4045
<script src="/js/features/dragDrop.js"></script>
4146
</body>
4247

resources/js/core/appContext.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
(function () {
2+
if (
3+
window.Neutralino &&
4+
typeof window.Neutralino.init === "function" &&
5+
typeof window.NL_PORT === "number" &&
6+
typeof window.NL_TOKEN === "string"
7+
) {
8+
window.Neutralino.init();
9+
}
10+
211
const app = window.LiteMark || (window.LiteMark = {});
312

413
app.dom = {
514
editor: document.getElementById("editor"),
615
preview: document.getElementById("markdownPreview"),
716
dropZone: document.getElementById("dropZone"),
8-
fileLabel: document.getElementById("fileLabel")
17+
fileLabel: document.getElementById("fileLabel"),
18+
exportHtmlButton: document.getElementById("exportHtmlButton")
919
};
1020

1121
app.state = app.state || {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(function () {
2+
const app = window.LiteMark || (window.LiteMark = {});
3+
const dom = app.dom || {};
4+
const preview = dom.preview;
5+
const fileLabel = dom.fileLabel;
6+
const exportButton = dom.exportHtmlButton;
7+
8+
if (!preview || !exportButton) {
9+
return;
10+
}
11+
12+
function stripExtension(fileName) {
13+
if (typeof fileName !== "string" || fileName.trim().length === 0) {
14+
return "litemark-export";
15+
}
16+
17+
const lastDotIndex = fileName.lastIndexOf(".");
18+
if (lastDotIndex <= 0) {
19+
return fileName;
20+
}
21+
22+
return fileName.slice(0, lastDotIndex);
23+
}
24+
25+
function sanitizeFileName(fileName) {
26+
return fileName
27+
.trim()
28+
.replace(/[<>:"/\\|?*\u0000-\u001F]/g, "-")
29+
.replace(/\s+/g, "-")
30+
.replace(/-+/g, "-")
31+
.replace(/^-|-$/g, "")
32+
.toLowerCase() || "litemark-export";
33+
}
34+
35+
function escapeHtml(value) {
36+
return value
37+
.replace(/&/g, "&amp;")
38+
.replace(/</g, "&lt;")
39+
.replace(/>/g, "&gt;")
40+
.replace(/\"/g, "&quot;")
41+
.replace(/'/g, "&#039;");
42+
}
43+
44+
function buildDocument(title, contentHtml) {
45+
return `<!DOCTYPE html>
46+
<html lang="en">
47+
<head>
48+
<meta charset="UTF-8">
49+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
50+
<title>${escapeHtml(title)}</title>
51+
</head>
52+
<body>
53+
${contentHtml}
54+
</body>
55+
</html>
56+
`;
57+
}
58+
59+
async function exportPreviewHtml() {
60+
const api = window.Neutralino;
61+
if (!api || !api.os || !api.filesystem) {
62+
if (fileLabel) {
63+
fileLabel.textContent = "Export unavailable outside Neutralino";
64+
}
65+
return;
66+
}
67+
68+
exportButton.disabled = true;
69+
70+
try {
71+
const baseName = sanitizeFileName(stripExtension(app.state && app.state.fileName ? app.state.fileName : ""));
72+
const outputName = `${baseName}.html`;
73+
const downloadsPath = await api.os.getPath("downloads");
74+
const outputPath = await api.filesystem.getJoinedPath(downloadsPath, outputName);
75+
const documentHtml = buildDocument(baseName, preview.innerHTML);
76+
77+
await api.filesystem.writeFile(outputPath, documentHtml);
78+
79+
if (fileLabel) {
80+
fileLabel.textContent = `Saved to Downloads: ${outputName}`;
81+
}
82+
}
83+
catch (error) {
84+
if (fileLabel) {
85+
fileLabel.textContent = "Failed to export HTML";
86+
}
87+
console.error("Failed to export HTML", error);
88+
}
89+
finally {
90+
exportButton.disabled = false;
91+
}
92+
}
93+
94+
exportButton.addEventListener("click", exportPreviewHtml);
95+
app.actions.exportPreviewHtml = exportPreviewHtml;
96+
})();

resources/styles.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*::before,
2222
*::after {
2323
box-sizing: border-box;
24+
scrollbar-width: none;
2425
}
2526

2627
html,
@@ -64,6 +65,31 @@ body {
6465
color: var(--mutedTextColor);
6566
}
6667

68+
.topbarActions {
69+
display: flex;
70+
align-items: center;
71+
gap: 0.75rem;
72+
}
73+
74+
#exportHtmlButton {
75+
border: 1px solid var(--outlineColor);
76+
background: #151a1f;
77+
color: var(--textColor);
78+
border-radius: 6px;
79+
padding: 0.4rem 0.7rem;
80+
font-size: 0.8rem;
81+
cursor: pointer;
82+
}
83+
84+
#exportHtmlButton:hover:enabled {
85+
border-color: var(--accentColor);
86+
}
87+
88+
#exportHtmlButton:disabled {
89+
opacity: 0.65;
90+
cursor: wait;
91+
}
92+
6793
.workspace {
6894
display: grid;
6995
grid-template-columns: 1fr 1fr;
@@ -198,6 +224,15 @@ body {
198224
}
199225

200226
@media (max-width: 900px) {
227+
.topbar {
228+
flex-wrap: wrap;
229+
}
230+
231+
.topbarActions {
232+
width: 100%;
233+
justify-content: space-between;
234+
}
235+
201236
.workspace {
202237
grid-template-columns: 1fr;
203238
grid-template-rows: 1fr 1fr;

0 commit comments

Comments
 (0)