Skip to content

Commit 6813c11

Browse files
Merge pull request #132 from ThisIs-Developer/perf/enterprise-performance-optimization
perf: enterprise performance optimization (14 fixes across 6 files)
2 parents 31235f0 + c6177a3 commit 6813c11

16 files changed

Lines changed: 930 additions & 735 deletions

.dockerignore

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,16 @@
1-
# Git
1+
# PERF-019: Exclude unnecessary files from Docker build context
22
.git
3+
.github
34
.gitignore
4-
.gitattributes
5-
6-
# Documentation
7-
README.md
8-
LICENSE
9-
10-
# CI/CD
11-
.github/
12-
13-
# Docker
14-
Dockerfile
155
.dockerignore
6+
desktop-app
7+
wiki
8+
node_modules
9+
*.md
10+
LICENSE
1611
docker-compose.yml
12+
Dockerfile
1713

18-
# IDE and editor files
19-
.vscode/
20-
.idea/
21-
*.swp
22-
*.swo
23-
*~
24-
25-
# OS generated files
26-
.DS_Store
27-
.DS_Store?
28-
._*
29-
.Spotlight-V100
30-
.Trashes
31-
ehthumbs.db
32-
Thumbs.db
33-
34-
# Logs
35-
*.log
36-
logs/
37-
38-
# Temporary files
39-
tmp/
40-
temp/
41-
42-
# Desktop app (NeutralinoJS) - not needed in web container
43-
desktop-app/
44-
14+
# Exclude large demo assets not needed at runtime
15+
assets/live-peview.gif
16+
assets/*.gif

.github/workflows/desktop-build.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ jobs:
2121
with:
2222
node-version: "lts/*"
2323

24+
# PERF-022: Cache Neutralino binaries and downloaded CDN libraries
25+
- name: Cache Neutralino binaries
26+
uses: actions/cache@v4
27+
with:
28+
path: |
29+
desktop-app/bin
30+
desktop-app/resources/libs
31+
key: neutralino-${{ hashFiles('desktop-app/neutralino.config.json') }}
32+
2433
- name: Setup Neutralinojs binaries
2534
working-directory: desktop-app
2635
run: npm run setup

Dockerfile

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
# Use nginx as the base image for serving static files
22
FROM nginx:alpine
33

4-
# Copy the static files to the nginx html directory
5-
COPY . /usr/share/nginx/html/
4+
# PERF-019: Only copy necessary web files (exclude .git, desktop-app, wiki, etc.)
5+
COPY index.html /usr/share/nginx/html/
6+
COPY script.js /usr/share/nginx/html/
7+
COPY styles.css /usr/share/nginx/html/
8+
COPY sw.js /usr/share/nginx/html/
9+
COPY manifest.json /usr/share/nginx/html/
10+
COPY robots.txt /usr/share/nginx/html/
11+
COPY sitemap.xml /usr/share/nginx/html/
12+
COPY assets/icon.jpg /usr/share/nginx/html/assets/
613

7-
# Create a custom nginx configuration for SPA routing
14+
# Create a custom nginx configuration with compression and security
15+
# PERF-020: Added gzip compression for text-based assets
816
RUN echo 'server { \
917
listen 80; \
1018
server_name localhost; \
1119
root /usr/share/nginx/html; \
1220
index index.html; \
1321
\
22+
# Enable gzip compression (PERF-020) \
23+
gzip on; \
24+
gzip_vary on; \
25+
gzip_proxied any; \
26+
gzip_comp_level 6; \
27+
gzip_min_length 256; \
28+
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml; \
29+
\
1430
# Handle client-side routing for SPA \
1531
location / { \
1632
try_files $uri $uri/ /index.html; \
@@ -27,6 +43,8 @@ RUN echo 'server { \
2743
add_header X-Content-Type-Options "nosniff" always; \
2844
add_header X-XSS-Protection "1; mode=block" always; \
2945
add_header Referrer-Policy "strict-origin-when-cross-origin" always; \
46+
# PERF-029: Content Security Policy for defense-in-depth \
47+
add_header Content-Security-Policy "default-src '"'"'self'"'"'; script-src '"'"'self'"'"' cdnjs.cloudflare.com cdn.jsdelivr.net '"'"'unsafe-inline'"'"'; style-src '"'"'self'"'"' cdnjs.cloudflare.com cdn.jsdelivr.net '"'"'unsafe-inline'"'"'; img-src '"'"'self'"'"' https: data: blob:; font-src '"'"'self'"'"' cdn.jsdelivr.net; connect-src '"'"'self'"'"' api.github.com raw.githubusercontent.com;" always; \
3048
}' > /etc/nginx/conf.d/default.conf
3149

3250
# Expose port 80

desktop-app/neutralino.config.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
"modes": {
2828
"window": {
2929
"title": "Markdown Viewer",
30-
"width": 800,
31-
"height": 500,
30+
"width": 1280,
31+
"height": 720,
3232
"minWidth": 400,
3333
"minHeight": 200,
3434
"center": true,
@@ -51,8 +51,8 @@
5151
"nativeAllowList": ["app.*"]
5252
},
5353
"chrome": {
54-
"width": 800,
55-
"height": 500,
54+
"width": 1280,
55+
"height": 720,
5656
"args": "--user-agent=\"Neutralinojs chrome mode\"",
5757
"nativeBlockList": ["filesystem.*", "os.*"]
5858
}

desktop-app/prepare.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ const LIBS_DIR = path.join(RESOURCES_DIR, "libs");
2323
fs.mkdirSync(jsDest, { recursive: true });
2424
fs.mkdirSync(LIBS_DIR, { recursive: true });
2525

26-
function copyDirSync(src, dest) {
26+
function copyDirSync(src, dest, excludePatterns) {
27+
if (!excludePatterns) excludePatterns = [];
2728
fs.mkdirSync(dest, { recursive: true });
2829
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
2930
const srcPath = path.join(src, entry.name);
3031
const destPath = path.join(dest, entry.name);
32+
// PERF-027: Skip files matching exclusion patterns (e.g., large demo GIFs)
33+
if (excludePatterns.some(p => entry.name.match(p))) {
34+
console.log(` ⊘ Skipped ${entry.name} (excluded from desktop build)`);
35+
continue;
36+
}
3137
if (entry.isDirectory()) {
32-
copyDirSync(srcPath, destPath);
38+
copyDirSync(srcPath, destPath, excludePatterns);
3339
} else {
3440
fs.copyFileSync(srcPath, destPath);
3541
}
@@ -43,8 +49,9 @@ console.log("✓ Copied script.js → resources/js/script.js");
4349
fs.copyFileSync(path.join(ROOT_DIR, "styles.css"), path.join(RESOURCES_DIR, "styles.css"));
4450
console.log("✓ Copied styles.css → resources/styles.css");
4551

46-
copyDirSync(path.join(ROOT_DIR, "assets"), path.join(RESOURCES_DIR, "assets"));
47-
console.log("✓ Copied assets/ → resources/assets/");
52+
// PERF-027: Exclude large demo assets (GIFs) from desktop build to reduce binary size
53+
copyDirSync(path.join(ROOT_DIR, "assets"), path.join(RESOURCES_DIR, "assets"), [/\.gif$/i]);
54+
console.log("✓ Copied assets/ → resources/assets/ (excluding GIF demos)");
4855

4956
/**
5057
* Validates the cryptographic integrity of a file against an expected SHA-384 hash.
@@ -195,6 +202,13 @@ async function prepareOfflineDependencies() {
195202
html = html.replace(/href="assets\//g, 'href="/assets/');
196203
html = html.replace(/href="styles\.css"/g, 'href="/styles.css"');
197204

205+
// PERF-034: Strip web-specific SEO tags, canonical, hreflang, preconnect, manifest and JSON-LD structured data for desktop build
206+
html = html.replace(/<!-- DNS Prefetch & Preconnect CDN Origins to Warm Up Latency -->[\s\S]*?<!-- PERF-015:/i, '<!-- PERF-015:');
207+
html = html.replace(/<!-- Canonical Link -->[\s\S]*?<!-- PWA Web Manifest -->/i, '<!-- PWA Web Manifest -->');
208+
html = html.replace(/<link rel="manifest" href="manifest\.json">/i, '');
209+
html = html.replace(/<!-- Primary Meta Tags -->[\s\S]*?<!-- JSON-LD Structured Data Schema/i, '<!-- JSON-LD Structured Data Schema');
210+
html = html.replace(/<script type="application\/ld\+json">[\s\S]*?<\/script>/i, '');
211+
198212
// Inject Neutralino script tags
199213
html = html.replace(
200214
/<script\s+src="script\.js"\s*><\/script>/i,

desktop-app/resources/index.html

Lines changed: 7 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,76 +3,15 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<!-- DNS Prefetch & Preconnect CDN Origins to Warm Up Latency -->
7-
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
8-
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
9-
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
10-
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
11-
12-
<!-- Canonical Link -->
13-
<link rel="canonical" href="https://markdownviewer.pages.dev/">
14-
15-
<!-- Multilingual Hreflang Tags for Search Crawlers -->
16-
<link rel="alternate" hreflang="x-default" href="https://markdownviewer.pages.dev/" />
17-
<link rel="alternate" hreflang="en" href="https://markdownviewer.pages.dev/?lang=en" />
18-
<link rel="alternate" hreflang="zh-Hans" href="https://markdownviewer.pages.dev/?lang=zh" />
19-
<link rel="alternate" hreflang="ja" href="https://markdownviewer.pages.dev/?lang=ja" />
20-
<link rel="alternate" hreflang="ko" href="https://markdownviewer.pages.dev/?lang=ko" />
21-
<link rel="alternate" hreflang="pt-BR" href="https://markdownviewer.pages.dev/?lang=pt" />
22-
<!-- Asian Search Engine Verification Meta Tags -->
23-
<meta name="baidu-site-verification" content="baidu_verification_code_placeholder" />
24-
<meta name="naver-site-verification" content="naver_verification_code_placeholder" />
6+
<!-- PERF-015: Preload critical-path resources for faster discovery -->
7+
<link rel="preload" href="/styles.css" as="style">
8+
<link rel="preload" href="script.js" as="script">
9+
2510
<!-- PWA Web Manifest -->
26-
<link rel="manifest" href="manifest.json">
27-
28-
<!-- Primary Meta Tags -->
29-
<meta name="title" content="Markdown Viewer">
30-
<meta name="description" content="Markdown Viewer is a powerful GitHub-style Markdown rendering tool with live preview, LaTeX math, Mermaid diagrams, syntax highlighting, dark mode, and export options to PDF, HTML, and MD—all fully client-side and secure.">
31-
<meta name="keywords" content="Markdown Viewer, GitHub-style markdown, live preview, markdown editor, LaTeX support, Mermaid diagrams, PDF export, syntax highlighting, markdown to HTML, secure markdown tool, client-side markdown viewer, ThisIs-Developer, advanced markdown parser, future markdown viewer, next-gen markdown tool">
32-
<meta name="author" content="ThisIs-Developer">
33-
<meta name="robots" content="index, follow">
34-
<meta name="language" content="English">
35-
<meta name="distribution" content="global">
36-
<meta name="rating" content="general">
37-
38-
<!-- Open Graph / Facebook -->
39-
<meta property="og:type" content="website">
40-
<meta property="og:url" content="https://markdownviewer.pages.dev/">
41-
<meta property="og:title" content="Markdown Viewer">
42-
<meta property="og:description" content="Markdown Viewer is a powerful GitHub-style Markdown rendering tool with live preview, LaTeX math, Mermaid diagrams, syntax highlighting, dark mode, and export options to PDF, HTML, and MD—all fully client-side and secure.">
43-
<meta property="og:image" content="https://markdownviewer.pages.dev/assets/icon.jpg">
44-
45-
<!-- Twitter -->
46-
<meta property="twitter:card" content="summary_large_image">
47-
<meta property="twitter:url" content="https://markdownviewer.pages.dev/">
48-
<meta property="twitter:title" content="Markdown Viewer">
49-
<meta property="twitter:description" content="Markdown Viewer is a powerful GitHub-style Markdown rendering tool with live preview, LaTeX math, Mermaid diagrams, syntax highlighting, dark mode, and export options to PDF, HTML, and MD—all fully client-side and secure.">
50-
<meta property="twitter:image" content="https://markdownviewer.pages.dev/assets/icon.jpg">
11+
5112

5213
<!-- JSON-LD Structured Data Schema for Search Rich Snippets -->
53-
<script type="application/ld+json">
54-
{
55-
"@context": "https://schema.org",
56-
"@type": "WebApplication",
57-
"name": "Markdown Viewer",
58-
"url": "https://markdownviewer.pages.dev/",
59-
"image": "https://markdownviewer.pages.dev/assets/icon.jpg",
60-
"description": "A powerful GitHub-style Markdown rendering tool with live preview, LaTeX, Mermaid, syntax highlighting, and PDF export.",
61-
"applicationCategory": "DeveloperApplication",
62-
"operatingSystem": "All",
63-
"browserRequirements": "Requires HTML5 compatible browser",
64-
"author": {
65-
"@type": "Organization",
66-
"name": "ThisIs-Developer",
67-
"url": "https://github.com/ThisIs-Developer"
68-
},
69-
"offers": {
70-
"@type": "Offer",
71-
"price": "0.00",
72-
"priceCurrency": "USD"
73-
}
74-
}
75-
</script>
14+
7615

7716
<title>Markdown Viewer</title>
7817
<link href="/assets/icon.jpg" rel="icon" type="image/jpg">
@@ -88,23 +27,7 @@
8827
<script src="/libs/highlight.min.js" integrity="sha384-F/bZzf7p3Joyp5psL90p/p89AZJsndkSoGwRpXcZhleCWhd8SnRuoYo4d0yirjJp" crossorigin="anonymous" defer></script>
8928
<script src="/libs/purify.min.js" integrity="sha384-3HPB1XT51W3gGRxAmZ+qbZwRpRlFQL632y8x+adAqCr4Wp3TaWwCLSTAJJKbyWEK" crossorigin="anonymous" defer></script>
9029
<script src="/libs/FileSaver.min.js" integrity="sha384-PlRSzpewlarQuj5alIadXwjNUX+2eNMKwr0f07ShWYLy8B6TjEbm7ZlcN/ScSbwy" crossorigin="anonymous" defer></script>
91-
<script>
92-
window.MathJax = {
93-
loader: { load: ['[tex]/ams', '[tex]/boldsymbol'] },
94-
tex: {
95-
inlineMath: [['$', '$'], ['\\(', '\\)']],
96-
displayMath: [['$$', '$$'], ['\\[', '\\]']],
97-
processEscapes: true,
98-
packages: { '[+]': ['ams', 'boldsymbol'] }
99-
}
100-
};
101-
</script>
102-
<script src="/libs/tex-mml-chtml.min.js" integrity="sha384-M5jmNxKC9EVnuqeMwRHvFuYUE8Hhp0TgBruj/GZRkYtiMrCRgH7yvv5KY+Owi7TW" crossorigin="anonymous" defer></script>
103-
<script src="/libs/mermaid.min.js" integrity="sha384-zkWMJO4sgpPUzyuOgDx8HB/K55glbAwajEpk1Go2NWRuPkPA/wIhoEJTuSkmOYrV" crossorigin="anonymous" defer></script>
104-
<script src="/libs/joypixels.min.js" integrity="sha384-1+n1eMmP5I08CibRJ6JmycJ0hP3G6C0fuUtTb4bEuQgl9uFdS9pnPePfpmrXl9ll" crossorigin="anonymous" defer></script>
105-
<script src="/libs/jspdf.umd.min.js" integrity="sha384-JcnsjUPPylna1s1fvi1u12X5qjY5OL56iySh75FdtrwhO/SWXgMjoVqcKyIIWOLk" crossorigin="anonymous" defer></script>
106-
<script src="/libs/html2canvas.min.js" integrity="sha384-ZZ1pncU3bQe8y31yfZdMFdSpttDoPmOZg2wguVK9almUodir1PghgT0eY7Mrty8H" crossorigin="anonymous" defer></script>
107-
<script src="/libs/pako.min.js" integrity="sha384-rNlaE5fs9dGIjmxWDALQh/RBAaGRYT5ChrzHo6tRfgrZ36iRFAiquP5g41Jsv+0j" crossorigin="anonymous" defer></script>
30+
<!-- PERF-002: MathJax, Mermaid, JoyPixels, jsPDF, html2canvas, pako are now lazy-loaded by script.js on first use -->
10831
<script src="/libs/js-yaml.min.js" integrity="sha384-+pxiN6T7yvpryuJmE1gM9PX7yQit15auDb+ZwwvJOd/4be2Cie5/IuVXgQb/S9du" crossorigin="anonymous" defer></script>
10932
</head>
11033
<body>

desktop-app/resources/js/main.js

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,5 @@
1-
// This is just a sample app. You can structure your Neutralinojs app code as you wish.
2-
// This example app is written with vanilla JavaScript and HTML.
3-
// Feel free to use any frontend framework you like :)
4-
// See more details: https://neutralino.js.org/docs/how-to/use-a-frontend-library
5-
6-
/*
7-
Function to display information about the Neutralino app.
8-
This function updates the content of the 'info' element in the HTML
9-
with details regarding the running Neutralino application, including
10-
its ID, port, operating system, and version information.
11-
*/
12-
function showInfo() {
13-
return `
14-
${NL_APPID} is running on port ${NL_PORT} inside ${NL_OS}
15-
<br/><br/>
16-
<span>server: v${NL_VERSION} . client: v${NL_CVERSION}</span>
17-
`;
18-
}
19-
20-
/*
21-
Function to open the official Neutralino documentation in the default web browser.
22-
*/
23-
function openDocs() {
24-
Neutralino.os.open("https://neutralino.js.org/docs");
25-
}
26-
27-
/*
28-
Function to open a tutorial video on Neutralino's official YouTube channel in the default web browser.
29-
*/
30-
function openTutorial() {
31-
Neutralino.os.open("https://www.youtube.com/c/CodeZri");
32-
}
1+
// Markdown Viewer Desktop — Neutralino.js integration layer
2+
// Handles system tray, window close confirmation, and file association
333

344
/*
355
Function to set up a system tray menu with options specific to the window mode.

0 commit comments

Comments
 (0)