Skip to content

Commit d2d62b5

Browse files
committed
Electron Desktop Application
1 parent 4e5d56c commit d2d62b5

File tree

13 files changed

+292
-32
lines changed

13 files changed

+292
-32
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ dist-ssr
2323
*.sln
2424
*.sw?
2525

26+
desktop/dist/*
27+
!desktop/dist/.gitkeep
28+
desktop/icon.png
29+
desktop/dist-electron/
30+
2631
src-tauri/target
2732

2833
server/node_modules

bun.lockb

108 KB
Binary file not shown.

desktop/build.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const distElectronPath = path.join(__dirname, 'dist-electron');
5+
if (fs.existsSync(distElectronPath)) {
6+
fs.rmSync(distElectronPath, { recursive: true });
7+
console.log('Cleaned up old electron builds');
8+
}
9+
10+
const distPath = path.join(__dirname, 'dist');
11+
if (fs.existsSync(distPath)) {
12+
fs.rmSync(distPath, { recursive: true });
13+
}
14+
fs.mkdirSync(distPath, { recursive: true });
15+
console.log('Created fresh dist directory');
16+
17+
const srcPath = path.join(__dirname, '..', 'dist');
18+
fs.cpSync(srcPath, distPath, { recursive: true });
19+
20+
const publicPath = path.join(__dirname, '..', 'public');
21+
const publicFiles = [
22+
'trusty.jpg',
23+
'apple-touch-icon.png',
24+
'favicon-16x16.png',
25+
'favicon-32x32.png',
26+
'site.webmanifest',
27+
'android-chrome-512x512.png'
28+
];
29+
30+
publicFiles.forEach(file => {
31+
const srcFile = path.join(publicPath, file);
32+
const destFile = path.join(distPath, file);
33+
34+
if (fs.existsSync(srcFile)) {
35+
fs.copyFileSync(srcFile, destFile);
36+
console.log(`Copied ${file} to desktop/dist`);
37+
} else {
38+
console.warn(`Warning: ${file} not found in public folder`);
39+
}
40+
41+
if (file === 'android-chrome-512x512.png') {
42+
const iconFile = path.join(__dirname, 'icon.png');
43+
fs.copyFileSync(srcFile, iconFile);
44+
console.log('Copied app icon to desktop folder');
45+
}
46+
});
47+
48+
const publicImagesPath = path.join(__dirname, '..', 'public', 'images');
49+
const distImagesPath = path.join(distPath, 'images');
50+
51+
if (fs.existsSync(publicImagesPath)) {
52+
if (!fs.existsSync(distImagesPath)) {
53+
fs.mkdirSync(distImagesPath, { recursive: true });
54+
}
55+
fs.cpSync(publicImagesPath, distImagesPath, { recursive: true });
56+
}

desktop/bun.lockb

127 KB
Binary file not shown.

desktop/main.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const { app, BrowserWindow, protocol } = require('electron')
2+
const path = require('path')
3+
const fs = require('fs')
4+
const url = require('url')
5+
6+
function createWindow() {
7+
const win = new BrowserWindow({
8+
width: 1280,
9+
height: 720,
10+
icon: path.join(__dirname, 'icon.png'),
11+
webPreferences: {
12+
preload: path.join(__dirname, 'preload.js'),
13+
nodeIntegration: false,
14+
contextIsolation: true,
15+
webSecurity: true,
16+
sandbox: false
17+
}
18+
})
19+
20+
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
21+
callback({
22+
responseHeaders: {
23+
...details.responseHeaders,
24+
'Content-Security-Policy': [
25+
"default-src 'self'; " +
26+
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
27+
"style-src 'self' 'unsafe-inline'; " +
28+
"img-src 'self' data: https: file:; " +
29+
"connect-src 'self' https: file:; " +
30+
"frame-src 'none';"
31+
]
32+
}
33+
})
34+
})
35+
36+
win.webContents.session.webRequest.onBeforeRequest({
37+
urls: ['https://plausible.toolworks.dev/*']
38+
}, (details, callback) => {
39+
callback({ cancel: true })
40+
})
41+
42+
const indexPath = path.join(__dirname, 'dist', 'index.html')
43+
console.log('Loading from:', indexPath)
44+
45+
if (!fs.existsSync(indexPath)) {
46+
console.error('index.html not found at:', indexPath)
47+
app.quit()
48+
return
49+
}
50+
51+
win.loadFile(indexPath).catch(err => {
52+
console.error('Failed to load index.html:', err)
53+
})
54+
55+
win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
56+
console.error('Failed to load:', errorCode, errorDescription)
57+
})
58+
59+
win.webContents.on('did-finish-load', () => {
60+
console.log('Page loaded successfully')
61+
})
62+
}
63+
64+
app.whenReady().then(() => {
65+
protocol.registerFileProtocol('file', (request, callback) => {
66+
const pathname = decodeURIComponent(url.fileURLToPath(request.url))
67+
console.log('File request:', request.url)
68+
console.log('Resolved path:', pathname)
69+
70+
try {
71+
if (fs.existsSync(pathname)) {
72+
callback(pathname)
73+
} else {
74+
console.error('File not found:', pathname)
75+
callback({ error: -6 })
76+
}
77+
} catch (error) {
78+
console.error('Protocol error:', error)
79+
callback({ error: -2 })
80+
}
81+
})
82+
83+
createWindow()
84+
}).catch(err => {
85+
console.error('Failed to initialize app:', err)
86+
})
87+
88+
app.on('window-all-closed', () => {
89+
if (process.platform !== 'darwin') {
90+
app.quit()
91+
}
92+
})
93+
94+
app.on('activate', () => {
95+
if (BrowserWindow.getAllWindows().length === 0) {
96+
createWindow()
97+
}
98+
})
99+
100+
process.on('uncaughtException', (error) => {
101+
console.error('Uncaught exception:', error)
102+
})

desktop/package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "trusty-notes-electron",
3+
"version": "0.2.0",
4+
"main": "main.js",
5+
"type": "commonjs",
6+
"description": "TrustyNotes is a simple, secure, and reliable note-taking app",
7+
"author": "Toolworks.dev",
8+
"scripts": {
9+
"start": "electron .",
10+
"build:linux": "electron-builder --linux",
11+
"build:windows": "electron-builder --windows",
12+
"build:all": "electron-builder -l -w"
13+
},
14+
"build": {
15+
"appId": "dev.toolworks.trustynotes",
16+
"productName": "TrustyNotes",
17+
"directories": {
18+
"output": "dist-electron"
19+
},
20+
"files": [
21+
"dist/**/*",
22+
"main.js",
23+
"preload.js",
24+
"icon.png"
25+
],
26+
"linux": {
27+
"target": ["AppImage"],
28+
"category": "Office",
29+
"icon": "icon.png"
30+
},
31+
"win": {
32+
"target": ["nsis", "portable"],
33+
"icon": "icon.png"
34+
}
35+
},
36+
"devDependencies": {
37+
"electron": "^34.0.2",
38+
"electron-builder": "^25.1.8"
39+
}
40+
}

desktop/preload.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { contextBridge } = require('electron')
2+
3+
contextBridge.exposeInMainWorld('electron', {
4+
isElectron: true,
5+
platform: process.platform,
6+
versions: {
7+
node: process.versions.node,
8+
electron: process.versions.electron
9+
}
10+
})
11+
12+
window.addEventListener('DOMContentLoaded', () => {
13+
console.log('Preload script loaded')
14+
})

index.html

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,16 @@
7575
</script>
7676

7777
<!-- Analytics -->
78-
<script
79-
defer
80-
data-domain="trustynotes.app"
81-
src="https://plausible.toolworks.dev/js/script.js"
82-
></script>
78+
<script>
79+
// Only load analytics in web mode, not in electron
80+
if (!window.electron) {
81+
const script = document.createElement('script');
82+
script.defer = true;
83+
script.dataset.domain = "trustynotes.app";
84+
script.src = "https://plausible.toolworks.dev/js/script.js";
85+
document.head.appendChild(script);
86+
}
87+
</script>
8388

8489
<!-- Main App Script -->
8590
<script defer type="module" src="/src/main.tsx"></script>
@@ -110,6 +115,7 @@
110115
});
111116
}
112117
</script>
118+
113119
</head>
114120

115121
<body>

package.json

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@
33
"private": true,
44
"version": "0.2.0",
55
"type": "module",
6+
"description": "TrustyNotes is a simple, secure, and reliable note-taking app",
7+
"author": "Toolworks.dev",
68
"scripts": {
79
"dev": "vite",
810
"build": "tsc && vite build",
911
"preview": "vite preview",
12+
"build:electron": "cross-env ELECTRON=true tsc && vite build && node desktop/build.js",
13+
"start:electron": "cd desktop && electron .",
14+
"dev:electron": "concurrently \"vite\" \"cd desktop && cross-env NODE_ENV=development electron .\"",
1015
"build:chrome": "bun browser-extension/build.js chrome",
11-
"build:firefox": "bun browser-extension/build.js firefox"
16+
"build:firefox": "bun browser-extension/build.js firefox",
17+
"dist:linux": "bun run build:electron && cd desktop && bun run build:linux",
18+
"dist:windows": "bun run build:electron && cd desktop && bun run build:windows",
19+
"dist:all": "bun run build:electron && cd desktop && bun run build:all"
1220
},
1321
"dependencies": {
1422
"@capacitor/android": "^6.2.0",
@@ -21,44 +29,49 @@
2129
"@capacitor/splash-screen": "^6.0.3",
2230
"@capacitor/status-bar": "^6.0.2",
2331
"@emotion/react": "^11.14.0",
24-
"@mantine/core": "^7.15.2",
25-
"@mantine/hooks": "^7.15.2",
26-
"@mantine/notifications": "^7.15.2",
27-
"@noble/ed25519": "^2.1.0",
28-
"@tabler/icons-react": "^3.26.0",
29-
"@tiptap/extension-code-block": "^2.10.4",
30-
"@tiptap/extension-highlight": "^2.10.4",
31-
"@tiptap/extension-image": "^2.10.4",
32-
"@tiptap/extension-link": "^2.10.4",
33-
"@tiptap/extension-table": "^2.10.4",
34-
"@tiptap/extension-table-cell": "^2.10.4",
35-
"@tiptap/extension-table-header": "^2.10.4",
36-
"@tiptap/extension-table-row": "^2.10.4",
37-
"@tiptap/extension-text-align": "^2.10.4",
38-
"@tiptap/pm": "^2.10.4",
39-
"@tiptap/react": "^2.10.4",
40-
"@tiptap/starter-kit": "^2.10.4",
32+
"@mantine/core": "^7.16.2",
33+
"@mantine/hooks": "^7.16.2",
34+
"@mantine/notifications": "^7.16.2",
35+
"@noble/ed25519": "^2.2.3",
36+
"@tabler/icons-react": "^3.29.0",
37+
"@tiptap/extension-code-block": "^2.11.5",
38+
"@tiptap/extension-highlight": "^2.11.5",
39+
"@tiptap/extension-image": "^2.11.5",
40+
"@tiptap/extension-link": "^2.11.5",
41+
"@tiptap/extension-table": "^2.11.5",
42+
"@tiptap/extension-table-cell": "^2.11.5",
43+
"@tiptap/extension-table-header": "^2.11.5",
44+
"@tiptap/extension-table-row": "^2.11.5",
45+
"@tiptap/extension-text-align": "^2.11.5",
46+
"@tiptap/pm": "^2.11.5",
47+
"@tiptap/react": "^2.11.5",
48+
"@tiptap/starter-kit": "^2.11.5",
4149
"@types/highlight.js": "^10.1.0",
4250
"bip39": "^3.1.0",
4351
"browserify-zlib": "^0.2.0",
4452
"buffer": "^6.0.3",
53+
"crossenv": "^0.0.2-security",
4554
"date-fns": "^4.1.0",
46-
"highlight.js": "^11.11.0",
55+
"highlight.js": "^11.11.1",
4756
"process": "^0.11.10",
4857
"react": "^18.3.1",
4958
"react-dom": "^18.3.1",
50-
"react-markdown": "^9.0.1",
59+
"react-markdown": "^9.0.3",
5160
"rehype-highlight": "^7.0.1",
5261
"rehype-raw": "^7.0.0",
5362
"remark-gfm": "^4.0.0",
5463
"stream-browserify": "^3.0.0",
5564
"util": "^0.12.5"
5665
},
5766
"devDependencies": {
67+
"electron": "^34.0.2",
68+
"electron-builder": "^25.1.8",
5869
"@types/react": "^18.3.18",
5970
"@types/react-dom": "^18.3.5",
6071
"@vitejs/plugin-react": "^4.3.4",
61-
"typescript": "^5.7.2",
62-
"vite": "^5.4.11"
72+
"typescript": "^5.7.3",
73+
"vite": "^5.4.14",
74+
"concurrently": "^8.2.2",
75+
"cross-env": "^7.0.3"
6376
}
6477
}

src/App.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ import { notifications } from '@mantine/notifications';
4545
import { InstallPrompt } from './components/InstallPrompt';
4646
import { NoteEditor } from './components/NoteEditor';
4747

48+
const isElectron = !!window.electron;
49+
const logoPath = isElectron ? './trusty.jpg' : '/trusty.jpg';
50+
4851
function isBrowserExtensionEnvironment(): boolean {
4952
return typeof chrome !== 'undefined' &&
5053
typeof chrome.runtime !== 'undefined' &&
@@ -69,8 +72,6 @@ function App() {
6972
const [ignoreNextSave, setIgnoreNextSave] = useState(false);
7073
const [saveStatus, setSaveStatus] = useState<'saving' | 'saved'>('saved');
7174

72-
73-
7475
useEffect(() => {
7576
loadNotes();
7677
}, []);
@@ -408,7 +409,7 @@ async function deleteNote(noteId: number) {
408409
hiddenFrom="sm"
409410
size="sm"
410411
/>
411-
<Image src="/trusty.jpg" alt="Logo" w={30} h={30} />
412+
<Image src={logoPath} alt="Logo" w={30} h={30} />
412413
</Group>
413414
<TextInput
414415
placeholder="Note title"
@@ -459,7 +460,7 @@ async function deleteNote(noteId: number) {
459460
>
460461
<Group justify="space-between">
461462
<Group>
462-
<Image src="/trusty.jpg" alt="Logo" w={30} h={30} />
463+
<Image src={logoPath} alt="Logo" w={30} h={30} />
463464
<Text size="lg" fw={500}>TrustyNotes</Text>
464465
<Tooltip label="GitHub">
465466
<Anchor href="https://github.com/toolworks-dev/trusty-notes" target="_blank" rel="noreferrer">

0 commit comments

Comments
 (0)