Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .dprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"**/*-lock.json",
"translations/*.json",
"dist",
"mockServiceWorker.js"
"mockServiceWorker.js",
"src-tauri"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.88.7.wasm"
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/electron-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Electron Build

on:
push:
paths-ignore:
- '*.md'
workflow_dispatch:

jobs:
build:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
platform: win
- os: macos-latest
platform: mac
- os: ubuntu-latest
platform: linux

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install dependencies
run: npm i

- name: Build Electron app
run: npm run electron:build:${{ matrix.platform }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Windows artifacts
if: matrix.platform == 'win'
uses: actions/upload-artifact@v4
with:
name: synergism-windows
path: |
release/*.exe
if-no-files-found: error

- name: Upload macOS artifacts
if: matrix.platform == 'mac'
uses: actions/upload-artifact@v4
with:
name: synergism-macos
path: |
release/*.dmg
release/*.zip
if-no-files-found: error

- name: Upload Linux artifacts
if: matrix.platform == 'linux'
uses: actions/upload-artifact@v4
with:
name: synergism-linux
path: |
release/*.AppImage
release/*.deb
release/*.tar.gz
if-no-files-found: error
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ Pictures/Splash/Thumbs.db
Pictures/TransparentPics/Thumbs.db

**/.DS_Store
release
!electron/**/*.js
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib"
}
"typescript.tsdk": "node_modules\\typescript\\lib"
}
Binary file added Pictures/electron-logo.ico
Binary file not shown.
6 changes: 4 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
"correctness": {
"noUndeclaredVariables": "error"
}
},
"ignore": ["./dist/*", "**/node_modules/*", "mockServiceWorker.js"]
}
},
"formatter": {
"enabled": false
Expand All @@ -39,5 +38,8 @@
"parser": {
"allowComments": true
}
},
"files": {
"ignore": ["./dist/*", "**/node_modules/*", "mockServiceWorker.js", "./src-tauri/*"]
}
}
34 changes: 21 additions & 13 deletions claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,11 @@
2. **Check back with user** after writing significant code
3. **Ask questions** when task requirements are unclear

### Quality Assurance Commands
Run these after making changes:
```bash
node --run format # Format code
npx -p typescript tsc # TypeScript check
```

## File Structure Rules
```
src/ # Core game logic
├── mock/ # Mock API responses
├── steam/ # Steam (Electron) features
├── Purchases/ # Purchase-related logic
├── saves/ # Save system logic
├── types/ # TypeScript definitions
Expand Down Expand Up @@ -57,7 +51,7 @@ translations/en.json # Required for all new text strings
### Save System Variables
**CRITICAL**: Before adding to `player` object:
1. Get explicit permission from user
2. Add to `src/types/Synergism.d.ts`
2. Add to `src/types/Synergism.ts`
3. Add to `src/saves/PlayerSchema.ts`
4. Variable location: `player` in `src/Synergism.ts`

Expand All @@ -67,9 +61,6 @@ translations/en.json # Required for all new text strings
- **DOM Access**: ALWAYS use `DOMCacheGetOrSet('elementId')` instead of `document.getElementById`
- Import: `import { DOMCacheGetOrSet } from './Cache/DOM'`
- Reason: Performance optimization through caching
- **Import Style**: Top-level imports ONLY - never use dynamic imports like `import().then()`
- Correct: `import { functionName } from './ModuleName'` at top of file
- Wrong: `import('./Module').then(({ functionName }) => ...)`
- **Import Organization**: Alphabetical ordering within import groups
- **Destructured Imports**: Use for specific functions/variables from modules

Expand All @@ -79,9 +70,26 @@ translations/en.json # Required for all new text strings
- Match existing naming conventions
- Maintain consistency with current architecture

### Steam
- There is a Steam version of the app that uses Electron.
- Steam features MUST be gated by checking the `platform` variable from Config.ts
- When using a feature only available to the Electron app, you MUST use dynamic imports. Example:

```ts
import { platform } from './Config'

async function myFunction () {
if (platform === 'steam') {
const { steamOnlyFeature } = await import('./steam/steam')

await steamOnlyFeature()
} else {
// browser version
browserOnlyFeature()
}
}
```



- The platform variable comes from esbuild define hooks. These act as macros essentially, which removes the
`else` block on Steam and vice-versa on browser builds.
- **Wrong**: `import { steamOnlyFeature } from './steam/steam'`
85 changes: 85 additions & 0 deletions electron-builder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
"appId": "cc.synergism.app",
"productName": "Synergism",
"directories": {
"output": "release",
"buildResources": "build"
},
"files": [
"dist/**/*",
"electron/**/*"
],
"extraMetadata": {
"main": "electron/main.ts"
},
"win": {
"target": [
{
"target": "nsis",
"arch": ["x64"]
},
{
"target": "portable",
"arch": ["x64"]
}
],
"icon": "dist/Pictures/electron-logo.ico",
"artifactName": "${productName}-${os}-${arch}.${ext}",
"extraFiles": [
{ "from": "node_modules/steamworks.js/dist/win64/steam_api64.dll", "to": "." }
]
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"installerIcon": "dist/Pictures/electron-logo.ico",
"uninstallerIcon": "dist/Pictures/electron-logo.ico",
"installerHeaderIcon": "dist/Pictures/electron-logo.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "Synergism"
},
"mac": {
"target": [
{
"target": "dmg",
"arch": ["universal"]
},
{
"target": "zip",
"arch": ["universal"]
}
],
"icon": "build/icon.icns",
"category": "public.app-category.games",
"artifactName": "${productName}-${os}-${arch}.${ext}",
"extraFiles": [
{ "from": "node_modules/steamworks.js/dist/osx/libsteam_api.dylib", "to": "." }
],
"x64ArchFiles": "**/steamworksjs.darwin-*.node"
},
"linux": {
"maintainer": "Khafra <support@synergism.cc>",
"target": [
{
"target": "AppImage",
"arch": ["x64"]
},
{
"target": "deb",
"arch": ["x64"]
},
{
"target": "tar.gz",
"arch": ["x64"]
}
],
"icon": "build/icons",
"category": "Game",
"artifactName": "${productName}-${os}-${arch}.${ext}",
"extraFiles": [
{ "from": "node_modules/steamworks.js/dist/linux64/libsteam_api.so", "to": "." }
]
}
}
51 changes: 51 additions & 0 deletions electron/lib/discord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Client, type Presence } from 'discord-rpc'
import { ipcMain } from 'electron'

export type PresenceOptions = Omit<Presence, 'instance' | 'buttons'>

const clientId = '1289263890445631581'

const rpc = new Client({ transport: 'ipc' })
const startTimestamp = new Date()

let options: PresenceOptions | undefined

// biome-ignore lint/complexity/noUselessLoneBlockStatements: organization
{
ipcMain.handle('discord:setRichPresence', (_, presence: PresenceOptions) => {
options = presence
})
}

async function setActivity () {
if (!options) return

await rpc.setActivity({
startTimestamp,
...options,
// largeImageKey: 'snek_large',
// largeImageText: 'This is large image text',
// smallImageKey: 'snek_small',
// smallImageText: 'This is small image text',
instance: false,
buttons: [
{
label: 'Play Synergism!',
url: 'https://synergism.cc'
}
]
})

options = undefined
}

rpc.once('ready', () => {
setActivity()

// activity can only be set every 15 seconds
setInterval(() => {
setActivity()
}, 15e3)
})

rpc.login({ clientId })
Loading