Skip to content

How to develop GoofCord

Milkshift edited this page Mar 5, 2025 · 8 revisions

Getting started

Install:

  • Node.js (most recent stable version). Node.js is the main JS runtime
  • Bun. Bun is used for package management, bundling, and running build scripts

Run:

  • git clone https://github.com/Milkshiift/GoofCord.git to clone GoofCord's repository
  • cd GoofCord
  • bun install to install dependencies
  • bun run start to run GoofCord

Folder structure

├── assets — Contains static resources that are packaged with the app
│   ├── badges — Badges used for showing pings ("Dynamic icon" setting)
│   ├── css
│   ├── fonts
│   ├── html
│   └── lang — Translation files
├── assetsDev — Resources that are NOT packaged with the app and are used elsewhere
├── build — Build scripts
└── src — Main source code. Files in the root of the folder are the most important scripts
    ├── modules — GoofCord features
    └── windows — Code responsible for individual windows
        ├── main
        ├── screenshare
        └── settings
            └── cloud

Settings

GoofCord uses a centralized settings system managed through the settingsSchema.ts file. This file defines all available settings, their types, default values, and how they are displayed in the settings window.

Entries

Default entry (question marks are optionals):
// Key that is actually used by getConfig, setConfig, etc.
settingKey: {
	name, // Display name in the settings window
	description, // HTML tags can be used too, like <a> for links
	inputType, // Can be "checkbox", "textfield", "textarea", "file", "dropdown", "dropdown-multiselect". See /src/windows/settings/settingsRenderer.ts
	defaultValue, // Must be fitting for the inputType. See /build/genSettingsTypes.ts TYPE_MAPPING
	accept?, // Specifies what file types are accepted when "file" input is used. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept
	encrypted?, // Whether to encrypt the setting or not. Uses Electron's safeStorage API
	options?, // Options for "dropdown" and "dropdown-multiselect"
	showAfter?: { // If specified makes so the setting is hidden until the condition is met. customJsBundle setting is a good example
		key, // Key of the setting to run the condition function against
                // A function that accepts the current value of the key and returns a bool. If output is true, the setting is shown.
                // It doesn't have to use the input. It can also for example hide the setting on platforms other than Linux (see "autoscroll" setting)
		condition,
	}
}

Hidden setting that will not be shown in the settings window:
settingKey: {
	defaultValue, // Default value with the same type as outputType
	outputType: "string", // Any default TypeScript type
}

A button. The name MUST start with "button-"
"button-example": {
	name, // Text inside the button
	onClick // A string containing a function that will be executed on click. That function should be first exposed in settings/preload.mts contextBridge.exposeInMainWorld
}

Adding a New Setting

To add a new setting to GoofCord:

  1. Define the Setting: Add a new entry to the settingsSchema.ts file, following the structure and properties described above.
  2. Generate Types and Localization: Run bun run build or bun run start. This will automatically generate the necessary types in src/configTypes.d.ts and update the localization files in assets/lang.
  3. Use the Setting: Access the setting in the code using getConfig and setConfig functions from config.ts

IPC

https://www.electronjs.org/docs/latest/tutorial/ipc
IPC is needed to call code from the main process in the renderer process (preload) and vice versa. GoofCord uses a code generation approach for IPC to make defining IPC channels easier.
Here is an example of how to turn a simple function into a channel the GoofCord way:

  1. Starting function:
// Located in hello.ts
function helloWorld(string name) {
    console.log("Hello "+name+"!");
}
  1. Modify it like this to turn it into a channel:
// Must be exported
// A type parameter "<IPCHandle>" is added. This tells the build script that it's an async IPC channel
// This can be either IPCHandle or IPCOn. IPCHandle generates ipcMain.handle, which is async and is called with: await ipcRenderer.invoke()
// IPCOn generates ipcMain.on, which is synchronous and is called with: ipcRenderer.sendSync()
// Prefer IPCHandle
export function helloWorld<IPCHandle>(string name) {
    console.log("Hello "+name+"!");
}
  1. When bun run build is executed, build scripts will generate the channel in ipcGen.ts, with a name functionFileName:functionName:
import { helloWorld } from "./hello.ts";
ipcMain.handle("hello:helloWorld", (event, name) => { return helloWorld(name); });
  1. Use the channel in the renderer process:
await ipcRenderer.invoke("hello:helloWorld", "Goofball");

Localization

Localization files are stored in assets/lang. New strings can be added in en-US.json. Never modify other localization files, that should be done through Weblate to avoid merge conflicts.
String keys follow a naming convention, using kebabCase for names and combining categories using dashes.
For example: "menu-edit-selectAll": "Select all" ("menu" is the main category, "edit" is a subcategory, "selectAll" is a name, all combined by dashes)
String keys starting with category- or opt- are auto-generated from settingsSchema.ts.

In code, the strings can be accessed with i(keyName) from modules/localization.ts. If the string is not found in the user selected language, en-US is used as a fallback.
All strings that the user will ever see should be localized.

Clone this wiki locally