Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31da869800 |
@@ -36,7 +36,7 @@ BUILDER_ARGS := $(if $(STORES_API),-- --config.extraMetadata.warpEngine.registry
|
|||||||
|
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
.PHONY: help setup node-check build typecheck lint lint-fix check start smoke uitest storetest test \
|
.PHONY: help setup node-check build typecheck lint lint-fix check start smoke uitest storetest icons test \
|
||||||
dist dist-mac dist-win dist-linux release publish clean distclean version
|
dist dist-mac dist-win dist-linux release publish clean distclean version
|
||||||
|
|
||||||
help: ## List available targets
|
help: ## List available targets
|
||||||
@@ -83,6 +83,9 @@ smoke: ## Drive the store bridge with no window at all
|
|||||||
uitest: ## Load the window once and report what rendered
|
uitest: ## Load the window once and report what rendered
|
||||||
npm run uitest
|
npm run uitest
|
||||||
|
|
||||||
|
icons: ## Render every icon format from resources/icon.svg
|
||||||
|
npm run icons
|
||||||
|
|
||||||
storetest: ## Add and remove a store in a sandbox (the only code that deletes a tree)
|
storetest: ## Add and remove a store in a sandbox (the only code that deletes a tree)
|
||||||
npm run storetest
|
npm run storetest
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ names. `make` on its own lists everything.
|
|||||||
| `make lint` | the strict rule set (`lint-fix` fixes what it can) |
|
| `make lint` | the strict rule set (`lint-fix` fixes what it can) |
|
||||||
| `make check` | **typecheck, lint and every test suite** — the gate |
|
| `make check` | **typecheck, lint and every test suite** — the gate |
|
||||||
| `make start` | run the app against whatever store is installed |
|
| `make start` | run the app against whatever store is installed |
|
||||||
|
| `make icons` | render every icon format from `resources/icon.svg` |
|
||||||
| `make smoke` | drive the store with no window and no Electron at all |
|
| `make smoke` | drive the store with no window and no Electron at all |
|
||||||
| `make storetest` | add and remove a store in a sandbox — the only code that deletes a tree |
|
| `make storetest` | add and remove a store in a sandbox — the only code that deletes a tree |
|
||||||
| `SMOKE_HOME=<dir> SMOKE_TOKEN=<bearer> npm run smoke` | the same, against a sandbox store and as a signed-in person |
|
| `SMOKE_HOME=<dir> SMOKE_TOKEN=<bearer> npm run smoke` | the same, against a sandbox store and as a signed-in person |
|
||||||
@@ -246,6 +247,23 @@ The npm scripts still work directly (`npm start`, `npm run dist:mac`) — the
|
|||||||
Makefile adds no logic of its own beyond the release step. Every script that runs the
|
Makefile adds no logic of its own beyond the release step. Every script that runs the
|
||||||
app builds first, so there is no way to test a stale bundle.
|
app builds first, so there is no way to test a stale bundle.
|
||||||
|
|
||||||
|
### The icon
|
||||||
|
|
||||||
|
`resources/icon.svg` is the source and the only file to edit; `make icons` renders the
|
||||||
|
rest — `icon.png`, `icon.ico`, `icon.icns` and the `icons/` directory Linux packages
|
||||||
|
want. Three committed binaries with no way to regenerate them is how an icon becomes
|
||||||
|
something nobody dares change, so the render is a script rather than a memory.
|
||||||
|
|
||||||
|
It needs `rsvg-convert` (`brew install librsvg`, `apt install librsvg2-bin`). The
|
||||||
|
`.icns` step additionally needs `iconutil`, which exists only on macOS — elsewhere it
|
||||||
|
is skipped with a warning and the committed `.icns` stands, which is what a mac build
|
||||||
|
uses anyway.
|
||||||
|
|
||||||
|
The mark is a **W with three lines running into it**: the product's initial, and what
|
||||||
|
it is doing. It was drawn for the smallest size first — at 32px the W still reads and
|
||||||
|
the lines survive as motion rather than as noise. A portal, a play triangle and a send
|
||||||
|
arrow were all tried and all discarded: each already means something else.
|
||||||
|
|
||||||
### Continuous integration
|
### Continuous integration
|
||||||
|
|
||||||
`.woodpecker.yaml` builds the **Linux and Windows** packages, and on a tag attaches
|
`.woodpecker.yaml` builds the **Linux and Windows** packages, and on a tag attaches
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
# WarpEngine Client 2.5.0
|
# WarpEngine Client 2.5.1
|
||||||
|
|
||||||
**A store can be taken off the machine again.** Hovering a store in the side menu shows
|
**The app has an icon.** Until now every build shipped the default Electron one —
|
||||||
a bin. It asks first, and says how many titles will go with it — because removing a
|
`electron-builder` said so on every run, in a line easy to read past: *"default Electron
|
||||||
store *uninstalls what it installed*. That is not a convenience: a store's `state.json`
|
icon is used, reason=application icon is not set"*. A store you install games with
|
||||||
is the only record of which payloads, icons and menu entries belong to it, so leaving
|
should not look like a framework demo in the Dock.
|
||||||
the games behind would leave orphans nothing could ever identify, least of all a later
|
|
||||||
install of the same store into the same folder. The catalog cache, the settings and any
|
|
||||||
sign-in token go too.
|
|
||||||
|
|
||||||
**Adding one moved to a + beside Refresh**, and it now takes a catalog address of your
|
The mark is a **W with three lines running into it**: the product's initial, and what it
|
||||||
own as well as the ones the registry lists. A bare host is enough — `https` is assumed —
|
is doing. It uses the window's own palette, so the icon and the application it opens are
|
||||||
and the name is taken from the address. Both are actions on the whole store rather than
|
the same object. It was drawn for the smallest size first — at 32px the W still reads
|
||||||
on one of them, which is why they sit together; the full-width "Add a store…" button
|
and the lines survive as motion rather than as noise. A portal, a play triangle and a
|
||||||
under the list read as a third store.
|
send arrow were each tried and each discarded: they already mean a loading spinner, a
|
||||||
|
media player and a submit button.
|
||||||
|
|
||||||
**The picker has a way out.** Opening it with a store already installed used to be a
|
`resources/icon.svg` is the source and the only file to edit. `make icons` renders the
|
||||||
trap: the grid was replaced and nothing short of installing something brought it back.
|
`.png`, the `.ico`, the `.icns` and the Linux size directory from it — three committed
|
||||||
|
binaries with no way to regenerate them is how an icon becomes something nobody dares
|
||||||
|
touch.
|
||||||
|
|
||||||
**Two things found by the new test.** `make storetest` adds and removes a store in a
|
The window also picks it up when run from source, where there is otherwise no icon to
|
||||||
sandbox, because removal is the only code here that deletes a directory tree and the
|
carry and a dev run looks like a different application from the one being built.
|
||||||
path it deletes is named by the window. It immediately caught that `http://` was
|
|
||||||
accepted and became a store called *http* — the trailing slashes were being stripped
|
|
||||||
before the scheme was checked — and that `STORE_ROOT` only *prepended* to the search
|
|
||||||
path, so a "sandboxed" run still listed the real stores. With a delete button in the
|
|
||||||
window, a sandbox that can reach a working installation is not a sandbox; it replaces
|
|
||||||
the search path now.
|
|
||||||
|
|
||||||
**`btn-secondary` had no styling at all.** It was introduced in 2.4.0 on the card's
|
Nothing else changed: same store handling, same catalog, same sign-in.
|
||||||
sign-in button and on the sign-in panel, and rendered as a plain button in both places.
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "warp-engine-client",
|
"name": "warp-engine-client",
|
||||||
"version": "2.4.0",
|
"version": "2.5.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "warp-engine-client",
|
"name": "warp-engine-client",
|
||||||
"version": "2.4.0",
|
"version": "2.5.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^26.2.0",
|
"@types/node": "^26.2.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "warp-engine-client",
|
"name": "warp-engine-client",
|
||||||
"productName": "WarpEngine Client",
|
"productName": "WarpEngine Client",
|
||||||
"version": "2.5.0",
|
"version": "2.5.1",
|
||||||
"description": "Graphical client for WarpEngine stores: install a catalog into your own application menu.",
|
"description": "Graphical client for WarpEngine stores: install a catalog into your own application menu.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Teletype Games <games@teletype.hu>",
|
"author": "Teletype Games <games@teletype.hu>",
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
"dist:win": "npm run build && electron-builder --win",
|
"dist:win": "npm run build && electron-builder --win",
|
||||||
"dist:linux": "npm run build && electron-builder --linux",
|
"dist:linux": "npm run build && electron-builder --linux",
|
||||||
"storetest": "npm run build && node build/scripts/StoreLifecycleTest.js",
|
"storetest": "npm run build && node build/scripts/StoreLifecycleTest.js",
|
||||||
"test": "npm run smoke && npm run storetest && npm run uitest"
|
"test": "npm run smoke && npm run storetest && npm run uitest",
|
||||||
|
"icons": "node scripts/build-icons.mjs"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^26.2.0",
|
"@types/node": "^26.2.0",
|
||||||
@@ -47,7 +48,8 @@
|
|||||||
"dmg",
|
"dmg",
|
||||||
"zip"
|
"zip"
|
||||||
],
|
],
|
||||||
"artifactName": "WarpEngineClient-${version}-${arch}-mac.${ext}"
|
"artifactName": "WarpEngineClient-${version}-${arch}-mac.${ext}",
|
||||||
|
"icon": "resources/icon.icns"
|
||||||
},
|
},
|
||||||
"dmg": {
|
"dmg": {
|
||||||
"artifactName": "WarpEngineClient-${version}-${arch}.${ext}"
|
"artifactName": "WarpEngineClient-${version}-${arch}.${ext}"
|
||||||
@@ -56,7 +58,8 @@
|
|||||||
"target": [
|
"target": [
|
||||||
"nsis",
|
"nsis",
|
||||||
"portable"
|
"portable"
|
||||||
]
|
],
|
||||||
|
"icon": "resources/icon.ico"
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"artifactName": "WarpEngineClient-Setup-${version}-${arch}.${ext}"
|
"artifactName": "WarpEngineClient-Setup-${version}-${arch}.${ext}"
|
||||||
@@ -69,12 +72,16 @@
|
|||||||
"target": [
|
"target": [
|
||||||
"AppImage",
|
"AppImage",
|
||||||
"deb"
|
"deb"
|
||||||
]
|
],
|
||||||
|
"icon": "resources/icons"
|
||||||
},
|
},
|
||||||
"appImage": {
|
"appImage": {
|
||||||
"artifactName": "WarpEngineClient-${version}-${arch}.${ext}"
|
"artifactName": "WarpEngineClient-${version}-${arch}.${ext}"
|
||||||
},
|
},
|
||||||
"afterPack": "scripts/after-pack.js"
|
"afterPack": "scripts/after-pack.js",
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "resources"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"allowScripts": {
|
"allowScripts": {
|
||||||
"electron@43.4.0": true,
|
"electron@43.4.0": true,
|
||||||
|
|||||||
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 117 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
<!--
|
||||||
|
The WarpEngine Client mark.
|
||||||
|
|
||||||
|
A W, and three lines running into it. The W is the product's initial; the lines are
|
||||||
|
what the W is doing — motion, read left to right, which is also why they are dimmer
|
||||||
|
than it is. Together they say "warp" without a spaceship, a portal or a play triangle,
|
||||||
|
each of which was tried and each of which turned out to mean something else already:
|
||||||
|
a send arrow, a loading spinner, a media player.
|
||||||
|
|
||||||
|
Drawn for the smallest size first. At 32px the W still reads and the lines survive as
|
||||||
|
a stack of motion rather than as noise; anything with an outline, a ring or fine
|
||||||
|
detail did not. The palette is the window's own, taken from src/renderer/style.css,
|
||||||
|
so the icon and the application it opens are the same object.
|
||||||
|
|
||||||
|
This file is the source. `npm run icons` renders every format from it; nothing here is
|
||||||
|
hand-edited binary.
|
||||||
|
-->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="tile" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#1e2937"/>
|
||||||
|
<stop offset="1" stop-color="#0d1116"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="glow" cx="0.5" cy="0.42" r="0.6">
|
||||||
|
<stop offset="0" stop-color="#37b98a" stop-opacity="0.24"/>
|
||||||
|
<stop offset="1" stop-color="#37b98a" stop-opacity="0"/>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Inset by 7%: a macOS icon is a rounded tile with air around it, and the same
|
||||||
|
shape is what Windows and the Linux menus get. -->
|
||||||
|
<rect x="72" y="72" width="880" height="880" rx="200" fill="url(#tile)"/>
|
||||||
|
<rect x="72" y="72" width="880" height="880" rx="200" fill="url(#glow)"/>
|
||||||
|
<rect x="72" y="72" width="880" height="880" rx="200" fill="none" stroke="#2a3440" stroke-width="8"/>
|
||||||
|
|
||||||
|
<g stroke="#2c8f6c" stroke-width="48" stroke-linecap="round">
|
||||||
|
<path d="M196 400 H286"/>
|
||||||
|
<path d="M172 512 H274"/>
|
||||||
|
<path d="M196 624 H286"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<path d="M366 348 L462 676 L588 456 L714 676 L810 348" fill="none" stroke="#37b98a"
|
||||||
|
stroke-width="82" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Render every icon format from resources/icon.svg.
|
||||||
|
//
|
||||||
|
// The point of this script is that the icon stays *editable*. Three committed binaries
|
||||||
|
// with no way to regenerate them is how an icon becomes something nobody dares touch;
|
||||||
|
// here the SVG is the source and everything else is output, so changing the mark is
|
||||||
|
// changing one file and running this.
|
||||||
|
//
|
||||||
|
// npm run icons
|
||||||
|
//
|
||||||
|
// Needs `rsvg-convert` (brew install librsvg). The .icns additionally needs `iconutil`,
|
||||||
|
// which only exists on macOS — on Linux that step is skipped with a warning, because CI
|
||||||
|
// builds Linux and Windows there and the committed .icns is what a mac build uses.
|
||||||
|
|
||||||
|
import { execFileSync } from 'node:child_process'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dirname, '..')
|
||||||
|
const RESOURCES = path.join(ROOT, 'resources')
|
||||||
|
const SOURCE = path.join(RESOURCES, 'icon.svg')
|
||||||
|
|
||||||
|
/** Windows wants these; anything larger than 256 cannot go in an ICO as PNG anyway. */
|
||||||
|
const ICO_SIZES = [16, 24, 32, 48, 64, 128, 256]
|
||||||
|
|
||||||
|
/** What macOS asks for in an iconset, with the @2x names it insists on. */
|
||||||
|
const ICNS_ENTRIES = [
|
||||||
|
[16, 'icon_16x16.png'], [32, 'icon_16x16@2x.png'],
|
||||||
|
[32, 'icon_32x32.png'], [64, 'icon_32x32@2x.png'],
|
||||||
|
[128, 'icon_128x128.png'], [256, 'icon_128x128@2x.png'],
|
||||||
|
[256, 'icon_256x256.png'], [512, 'icon_256x256@2x.png'],
|
||||||
|
[512, 'icon_512x512.png'], [1024, 'icon_512x512@2x.png']
|
||||||
|
]
|
||||||
|
|
||||||
|
function render (size, target) {
|
||||||
|
execFileSync('rsvg-convert', ['-w', String(size), '-h', String(size), SOURCE, '-o', target])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ICO holding PNGs.
|
||||||
|
*
|
||||||
|
* The format allows it since Vista and every tool this project's packages reach has
|
||||||
|
* supported it for longer than that. Writing the container by hand is a few lines and
|
||||||
|
* saves a dependency on ImageMagick, which is not installed here and is not worth
|
||||||
|
* making a build requirement for 22 bytes of header per image.
|
||||||
|
*/
|
||||||
|
function writeIco (pngs, target) {
|
||||||
|
const header = Buffer.alloc(6)
|
||||||
|
header.writeUInt16LE(0, 0)
|
||||||
|
header.writeUInt16LE(1, 2) // 1 = icon
|
||||||
|
header.writeUInt16LE(pngs.length, 4)
|
||||||
|
|
||||||
|
const directory = Buffer.alloc(16 * pngs.length)
|
||||||
|
let offset = header.length + directory.length
|
||||||
|
|
||||||
|
pngs.forEach(({ size, data }, index) => {
|
||||||
|
const at = index * 16
|
||||||
|
// 0 means 256 in this field, which is the whole reason 256 is the largest size here.
|
||||||
|
directory.writeUInt8(size >= 256 ? 0 : size, at)
|
||||||
|
directory.writeUInt8(size >= 256 ? 0 : size, at + 1)
|
||||||
|
directory.writeUInt8(0, at + 2) // palette: none
|
||||||
|
directory.writeUInt8(0, at + 3) // reserved
|
||||||
|
directory.writeUInt16LE(1, at + 4) // colour planes
|
||||||
|
directory.writeUInt16LE(32, at + 6) // bits per pixel
|
||||||
|
directory.writeUInt32LE(data.length, at + 8)
|
||||||
|
directory.writeUInt32LE(offset, at + 12)
|
||||||
|
offset += data.length
|
||||||
|
})
|
||||||
|
|
||||||
|
fs.writeFileSync(target, Buffer.concat([header, directory, ...pngs.map((p) => p.data)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildIco () {
|
||||||
|
const temporary = fs.mkdtempSync(path.join(RESOURCES, '.ico-'))
|
||||||
|
try {
|
||||||
|
const pngs = ICO_SIZES.map((size) => {
|
||||||
|
const file = path.join(temporary, `${size}.png`)
|
||||||
|
render(size, file)
|
||||||
|
return { size, data: fs.readFileSync(file) }
|
||||||
|
})
|
||||||
|
writeIco(pngs, path.join(RESOURCES, 'icon.ico'))
|
||||||
|
console.log(` icon.ico ${ICO_SIZES.join(', ')}`)
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(temporary, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildIcns () {
|
||||||
|
const iconset = path.join(RESOURCES, 'icon.iconset')
|
||||||
|
fs.rmSync(iconset, { recursive: true, force: true })
|
||||||
|
fs.mkdirSync(iconset)
|
||||||
|
try {
|
||||||
|
for (const [size, name] of ICNS_ENTRIES) render(size, path.join(iconset, name))
|
||||||
|
execFileSync('iconutil', ['-c', 'icns', iconset, '-o', path.join(RESOURCES, 'icon.icns')])
|
||||||
|
console.log(' icon.icns 16 … 512@2x')
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(iconset, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linux takes a directory of sizes; electron-builder reads whatever is in it.
|
||||||
|
*
|
||||||
|
* Named `<size>x<size>.png`, which is the convention it expects and also what a
|
||||||
|
* `.desktop` entry's icon lookup walks.
|
||||||
|
*/
|
||||||
|
function buildLinuxIcons () {
|
||||||
|
const directory = path.join(RESOURCES, 'icons')
|
||||||
|
fs.rmSync(directory, { recursive: true, force: true })
|
||||||
|
fs.mkdirSync(directory)
|
||||||
|
const sizes = [16, 32, 48, 64, 128, 256, 512, 1024]
|
||||||
|
for (const size of sizes) render(size, path.join(directory, `${size}x${size}.png`))
|
||||||
|
console.log(` icons/ ${sizes.join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function main () {
|
||||||
|
if (!fs.existsSync(SOURCE)) {
|
||||||
|
console.error(`no ${path.relative(ROOT, SOURCE)} — the icon source is missing`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
execFileSync('rsvg-convert', ['--version'], { stdio: 'ignore' })
|
||||||
|
} catch {
|
||||||
|
console.error('rsvg-convert is not installed (brew install librsvg / apt install librsvg2-bin)')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('rendering icons from resources/icon.svg')
|
||||||
|
render(1024, path.join(RESOURCES, 'icon.png'))
|
||||||
|
console.log(' icon.png 1024')
|
||||||
|
buildLinuxIcons()
|
||||||
|
buildIco()
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
buildIcns()
|
||||||
|
} else {
|
||||||
|
// Not fatal: the committed .icns is what a mac build uses, and only a Mac can make
|
||||||
|
// one. Saying so is better than a build that quietly ships the Electron default.
|
||||||
|
console.warn(' icon.icns skipped — iconutil is macOS only; the committed one stands')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { BrowserWindow, shell, type BrowserWindowConstructorOptions } from 'electron'
|
import { app, BrowserWindow, shell, type BrowserWindowConstructorOptions } from 'electron'
|
||||||
|
|
||||||
const WINDOW_OPTIONS: BrowserWindowConstructorOptions = {
|
const WINDOW_OPTIONS: BrowserWindowConstructorOptions = {
|
||||||
width: 1040,
|
width: 1040,
|
||||||
@@ -10,6 +10,22 @@ const WINDOW_OPTIONS: BrowserWindowConstructorOptions = {
|
|||||||
title: 'WarpEngine Client'
|
title: 'WarpEngine Client'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The window icon, in development only.
|
||||||
|
*
|
||||||
|
* A packaged app carries its icon in the bundle, the .exe and the .desktop entry, and
|
||||||
|
* `resources/` is not inside the package at all — pointing at it there would be a path
|
||||||
|
* that does not exist. Run from source there is nothing to carry the icon, so the
|
||||||
|
* window and the taskbar show Electron's own, which makes a dev run look like a
|
||||||
|
* different application from the one being built. macOS ignores this either way: the
|
||||||
|
* Dock icon comes from the bundle.
|
||||||
|
*/
|
||||||
|
function developmentIcon (): Pick<BrowserWindowConstructorOptions, 'icon'> {
|
||||||
|
if (app.isPackaged) return {}
|
||||||
|
|
||||||
|
return { icon: path.join(__dirname, '..', '..', 'resources', 'icons', '512x512.png') }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The one window.
|
* The one window.
|
||||||
*
|
*
|
||||||
@@ -23,6 +39,7 @@ export class MainWindowFactory {
|
|||||||
public createWindow (): BrowserWindow {
|
public createWindow (): BrowserWindow {
|
||||||
const window = new BrowserWindow({
|
const window = new BrowserWindow({
|
||||||
...WINDOW_OPTIONS,
|
...WINDOW_OPTIONS,
|
||||||
|
...developmentIcon(),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, '..', 'preload', 'preload.js'),
|
preload: path.join(__dirname, '..', 'preload', 'preload.js'),
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
|
|||||||