Skip to content

Commit 291e4f7

Browse files
committed
Add logging and fix packaging issues
1 parent b16c54d commit 291e4f7

6 files changed

Lines changed: 119 additions & 12 deletions

File tree

‎README.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ brew install --cask libreoffice
2828
brew install poppler
2929
```
3030

31+
## Troubleshooting
32+
33+
### Logs Location
34+
If you encounter issues, check the application logs:
35+
- **macOS**: `~/Library/Logs/SlideCue/main.log`
36+
- **Windows**: `%USERPROFILE%\AppData\Roaming\SlideCue\logs\main.log`
37+
38+
### Common Issues
39+
- **PNGs not generating**: Ensure LibreOffice and Poppler are installed on macOS
40+
- **Server won't start**: Check if port 3000 is available or being blocked by firewall
41+
- **Permission errors**: Grant Accessibility and Automation permissions to SlideCue in System Settings
42+
3143
## Getting Started
3244

3345
### Development

‎package-lock.json‎

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,28 @@
3434
"out/**/*",
3535
"package.json"
3636
],
37+
"extraResources": [
38+
{
39+
"from": "resources/remote",
40+
"to": "remote"
41+
}
42+
],
3743
"mac": {
3844
"category": "public.app-category.productivity",
3945
"icon": "resources/icon.icns",
4046
"identity": null,
4147
"target": [
4248
{
4349
"target": "dmg",
44-
"arch": ["universal"]
50+
"arch": [
51+
"universal"
52+
]
4553
},
4654
{
4755
"target": "zip",
48-
"arch": ["universal"]
56+
"arch": [
57+
"universal"
58+
]
4959
}
5060
]
5161
},
@@ -54,7 +64,9 @@
5464
"target": [
5565
{
5666
"target": "nsis",
57-
"arch": ["x64"]
67+
"arch": [
68+
"x64"
69+
]
5870
}
5971
]
6072
},
@@ -70,6 +82,7 @@
7082
},
7183
"dependencies": {
7284
"bonjour-service": "^1.3.0",
85+
"electron-log": "^5.4.3",
7386
"electron-updater": "^6.6.2",
7487
"express": "^5.2.1",
7588
"qrcode.react": "^4.2.0",

‎src/main/index.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { app, BrowserWindow, ipcMain, dialog, protocol, net } from 'electron';
2+
import log from 'electron-log';
23
import path from 'path';
34
import os from 'os';
45
import fs from 'fs/promises';
@@ -89,13 +90,15 @@ ipcMain.handle('import-presentation', async () => {
8990
}
9091

9192
presentationFile = result.filePaths[0];
93+
log.info('Selected presentation:', presentationFile);
9294

93-
// Create temp directory for thumbnails
95+
// Create directory for thumbnails in app userData
96+
const appDataDir = path.join(app.getPath('userData'), 'thumbnails');
9497
currentThumbsDir = path.join(
95-
os.tmpdir(),
96-
'slidecue-thumbs',
98+
appDataDir,
9799
Date.now().toString()
98100
);
101+
log.info('Creating thumbnails directory:', currentThumbsDir);
99102
await fs.mkdir(currentThumbsDir, { recursive: true });
100103

101104
// Send progress callback
@@ -105,9 +108,11 @@ ipcMain.handle('import-presentation', async () => {
105108

106109
// Now that we have a file selected, start the loading screen
107110
sendProgress(0, 5, 'Starting...');
111+
log.info('Starting import process');
108112

109113
// Export thumbnails with progress
110114
sendProgress(1, 5, 'Opening presentation...');
115+
log.info('Opening presentation in PowerPoint');
111116
await automation.openPresentation(presentationFile);
112117

113118
sendProgress(2, 5, 'Getting slide count...');
@@ -174,7 +179,8 @@ ipcMain.handle('get-slide-info', async () => {
174179

175180
// Cleanup temp directories (synchronous for use in quit handler)
176181
function cleanupTempDirs() {
177-
const tempBase = os.tmpdir();
182+
log.info('Cleaning up thumbnail directories');
183+
const tempBase = path.join(app.getPath('userData'), 'thumbnails');
178184
const dirsToClean = ['slidecue-thumbs', 'slidecue-presentations'];
179185

180186
for (const dir of dirsToClean) {

‎src/main/pptx/macos.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { join, basename } from 'path';
55
import { tmpdir } from 'os';
66
import { PowerPointAutomation, SlideInfo, SlideMetadata, ProgressCallback } from './types';
77
import { parsePresentationData, PresentationData, getNextVisibleSlide, getSlideData } from './slideParser';
8+
import log from 'electron-log';
89

910
const execAsync = promisify(exec);
1011

@@ -123,7 +124,11 @@ end tell
123124
try {
124125
// Use LibreOffice headless conversion (most reliable)
125126
console.log('Using LibreOffice to convert PPTX to PDF...');
126-
await execAsync(`/Applications/LibreOffice.app/Contents/MacOS/soffice --headless --convert-to pdf --outdir "${outputDir}" "${pptxPath}"`);
127+
log.info('Starting LibreOffice conversion for:', pptxPath);
128+
const sofficeCmd = `/Applications/LibreOffice.app/Contents/MacOS/soffice --headless --convert-to pdf --outdir "${outputDir}" "${pptxPath}"`;
129+
log.info('Running soffice command:', sofficeCmd);
130+
await execAsync(sofficeCmd);
131+
log.info('LibreOffice conversion completed');
127132

128133
// Rename the output file (LibreOffice uses the original filename)
129134
const baseName = basename(pptxPath).replace(/\.(pptx?|PPTX?)$/, '');
@@ -141,9 +146,16 @@ end tell
141146
// Check if PDF was created
142147
await access(pdfPath);
143148
console.log('PDF created, converting to PNG...');
149+
log.info('PDF created successfully at:', pdfPath);
144150

145151
// Convert PDF pages to PNG using pdftoppm
146-
await execAsync(`pdftoppm -png -r 150 "${pdfPath}" "${outputDir}/slide"`);
152+
// Use full path to pdftoppm since packaged apps don't have full PATH
153+
const pdftoppmPath = await execAsync('which pdftoppm').then(r => r.stdout.trim()).catch(() => '/opt/homebrew/bin/pdftoppm');
154+
log.info('Using pdftoppm at:', pdftoppmPath);
155+
const pdftoppmCmd = `"${pdftoppmPath}" -png -r 150 "${pdfPath}" "${outputDir}/slide"`;
156+
log.info('Running pdftoppm command:', pdftoppmCmd);
157+
await execAsync(pdftoppmCmd);
158+
log.info('Successfully converted PDF to PNGs');
147159

148160
// pdftoppm creates slide-1.png, slide-2.png, etc.
149161
// LibreOffice only exports visible slides, so we need to rename them
@@ -174,6 +186,7 @@ end tell
174186
await unlink(pdfPath).catch(() => {});
175187

176188
} catch (e) {
189+
log.error('Thumbnail export failed:', e);
177190
console.error('Export failed:', e);
178191
}
179192

@@ -208,16 +221,19 @@ end tell
208221
async startSlideshow() {
209222
currentSlide = 1;
210223
currentAnimationStep = 0;
224+
log.info('Starting slideshow');
211225

226+
// Use keyboard shortcut which works in any language
212227
await runAppleScript(`
213228
tell application "Microsoft PowerPoint"
214229
activate
215230
end tell
216-
delay 0.2
231+
delay 0.3
217232
tell application "System Events"
218233
keystroke return using {command down, shift down}
219234
end tell
220235
`);
236+
221237
await new Promise(resolve => setTimeout(resolve, 500));
222238

223239
// PowerPoint may skip hidden slide 1, query actual position

‎src/main/server/index.ts‎

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { networkInterfaces } from 'os';
55
import { readdir } from 'fs/promises';
66
import { join, dirname } from 'path';
77
import { fileURLToPath } from 'url';
8+
import log from 'electron-log';
89

910
let server: ReturnType<typeof createServer> | null = null;
1011
let io: Server | null = null;
@@ -15,16 +16,21 @@ let currentPort: number = 3000;
1516
function isPortAvailable(port: number): Promise<boolean> {
1617
return new Promise((resolve) => {
1718
const testServer = createServer();
18-
testServer.once('error', () => resolve(false));
19+
testServer.once('error', (err) => {
20+
log.warn(`Port ${port} is not available:`, err.message);
21+
resolve(false);
22+
});
1923
testServer.once('listening', () => {
2024
testServer.close();
25+
log.info(`Port ${port} is available`);
2126
resolve(true);
2227
});
2328
testServer.listen(port, '0.0.0.0');
2429
});
2530
}
2631

2732
async function findAvailablePort(startPort: number): Promise<number> {
33+
log.info(`Looking for available port starting from ${startPort}`);
2834
let port = startPort;
2935
while (!(await isPortAvailable(port))) {
3036
port++;
@@ -56,6 +62,10 @@ export async function startServer(
5662
pin: string;
5763
io: Server;
5864
}> {
65+
log.info('Starting Express server...');
66+
log.info('Remote UI path:', remoteUIPath);
67+
log.info('Thumbnails directory:', thumbsDir);
68+
5969
const app = express();
6070
server = createServer(app);
6171
io = new Server(server, {
@@ -65,6 +75,9 @@ export async function startServer(
6575
currentPin = generatePin();
6676
thumbnailsDir = thumbsDir;
6777
currentPort = await findAvailablePort(3000);
78+
79+
log.info(`Found available port: ${currentPort}`);
80+
log.info(`Generated PIN: ${currentPin}`);
6881

6982
// Serve remote UI at root
7083
app.use(express.static(remoteUIPath));
@@ -106,9 +119,20 @@ export async function startServer(
106119
}
107120
});
108121

109-
server.listen(currentPort, '0.0.0.0');
122+
server.listen(currentPort, '0.0.0.0', () => {
123+
log.info(`Server started successfully on port ${currentPort}`);
124+
});
125+
126+
server.on('error', (err: any) => {
127+
log.error('Server error:', err);
128+
if (err.code === 'EADDRINUSE') {
129+
log.error(`Port ${currentPort} is already in use`);
130+
}
131+
});
110132

111133
const localIP = getLocalIP();
134+
log.info(`Server URL: http://${localIP}:${currentPort}`);
135+
112136
return {
113137
url: `http://${localIP}:${currentPort}`,
114138
pin: currentPin,

0 commit comments

Comments
 (0)