Markstar
Markstar

Reputation: 873

How to make multiple Svelte windows in Electron?

For my electron app, I would like to open another Svelte-window (or load different windows/components depending on the startup variables).

So let's say I used this tutorial to set up a basic structure, with my App.svelte looking like this:

<script>
    const openLauncher = () => {
        window.api.openWindow("launcher");
    };
</script>

<button on:click={openLauncher}>Open Launcher</button>

As you can see, I added an IPC function to open a new window. The corresponding index.js:

const { app, BrowserWindow, ipcMain } = require("electron");
const { join } = require("path");

app.on("ready", () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            preload: join(__dirname, "./preload.js"),
        }
    });
    mainWindow.loadFile(join(__dirname, "../public/index.html"));
});

ipcMain.on("openLauncher", (event, args) => {
    const launcher = new BrowserWindow({
        webPreferences: {
            preload: join(__dirname, "./preload.js"),
        }
    });
    launcher.loadFile(join(__dirname, "../public/launcher.html"));
});

preload.js:

const { contextBridge, ipcRenderer } = require("electron");

const API = {
    openWindow: (obj) => ipcRenderer.send("openLauncher", obj),
}

contextBridge.exposeInMainWorld("api", API);

This does work and opens a new window with the launcher.html, but I can't figure out how to get the Svelte components working in that new file.

One idea I had was modifying main.js file so that the body component changes, like so:

import App from './App.svelte';
import LauncherApp from './LauncherApp.svelte';

const bodyID = document.getElementsByTagName('body')[0].id;

const app = {};
if (bodyID == "index") {
    app = new App({
        target: document.body,
    });
}
else if (bodyID == "launcher") {
    app = new LauncherApp({
        target: document.body,
    });
}
export default app;

This works for the main window (i.e. if I switch the IDs, it loads the correct component at startup) but since it doesn't load any Svelte whe opening the new window, this doesn't work.

So I would really appreciate any ideas on how to get Svelte to load for new/different windows/html-files! And if there is a way to do this with SvelteKit, even better!

Thank you in advance!

Upvotes: 1

Views: 1046

Answers (2)

Webbie_NL
Webbie_NL

Reputation: 1

As this post helped me alot to create seperate windows using Svelte and Electron, i just had to create my account after lurking Stack Overflow for years. I think i've cracked your problem without the "hackery" workaround.

I got it working without the #IF statements in my original App.svelte. This is how i did it:

My main.js in the src map (renderer side) is as follows:

import App from './App.svelte';
import Window2 from './Window2.svelte';

let bodyID = document.getElementsByTagName('body')[0].id;

let app;

if (bodyID == "index"){
    app = new App({
        target: document.body,
    });
}
else if (bodyID == "window2"){
    app = new Window2({
        target: document.body,
    });
}

export default app;

I think however that the real magic happens in my index.html and Window2.html. I made mine using the excellent YouTube videos provided by Tylerlaceby.

Basicly, the index.js in the main folder (so the Electron main js) opens the window with the following lines:

const main_window = new BrowserWindow({//Your settings
});

main_window.loadFile(join(__dirname, "../public/index.html"));
main_window.on("ready-to-show", main_window.show);

And the index.html that is in the public folder contains the following head: `

<title>Main Window</title>

<link rel='icon' type='image/png' href='./favicon.ico'>
<link rel='stylesheet' href='global.css'>
<link rel='stylesheet' href='build/bundle.css'>
 
<script defer src='build/bundle.js'></script>

The body is empty, but has the id attached which i use in the main.js renderer side script.

I believe that the header in the index.html loads the build folder after the electron app has been build, containing all of your converted Svelte files.

The Window2.html is the same. The secondary window loads it the same way as the main window does in the second code block from the main.js on the electron side but refers to the Window2.html. The Window2.html contains a body with the ID Window2.

If the above is not the solution, it could also be because i use let instead of const. Once a variable has been assigned to const, it cannot be changed which might explain why it works the first time, but not the second time.

I'm sorry if i did not get all the formatting and refering to previous awnsers correctly. I'm still using to learn Stackoverflow but was eager to share how i made it work for me.

Upvotes: 0

cascading-jox
cascading-jox

Reputation: 1141

The obvious quick fix I see is using an #if block on bodyID in App.svelte containing two components, MainApp (content of App.svelte) and LauncherApp, and then simply changing bodyID depending on in which mode you are.

When using sveltekit I think it would make sense to treat LauncherApp as a separate route (I believe this is the only way to have "separated" pages with sveltekit, though I am not 100%). So when opening a new window you navigate the new instance of your application to the LauncherApp route. If you don't want the same base layout as in the main app, you can add a __layout.reset.svelte file.

I don't know why your solution didn't work, it was quite elegant.

Upvotes: 1

Related Questions