Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions windows/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
38 changes: 38 additions & 0 deletions windows/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copy to .env and fill in. .env is gitignored.

# Firebase — Omi's public web config (project: based-hardware).
# These values let the app authenticate against the same Firebase project that
# the Omi macOS/web apps use, so any Omi account works for sign-in.
# Discovered via: GET https://www.googleapis.com/identitytoolkit/v3/relyingparty/getProjectConfig?key=<API_KEY>
VITE_FIREBASE_API_KEY=AIzaSyD9dzBdglc7IO9pPDIOvqnCoTis_xKkkC8
VITE_FIREBASE_AUTH_DOMAIN=based-hardware.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=based-hardware

# Omi backend base URLs
VITE_OMI_API_BASE=https://api.omi.me
VITE_OMI_DESKTOP_API_BASE=https://desktop-backend-hhibjajaja-uc.a.run.app

# PostHog analytics — same project the macOS desktop app reports to. Both have
# sensible defaults baked in (the key is a publishable client key), so these only
# need setting to point at a different PostHog project.
VITE_POSTHOG_HOST=https://us.i.posthog.com
VITE_POSTHOG_KEY=phc_z3qUFhGUgYIOMYnfxVSrLmYISQvbgph8iREQv3sez3Y

# Omi developer API key — required for POST /v1/dev/user/conversations/from-segments
# (cloud sync of recorded conversations). Generate one in your Omi app under
# Settings → Developer → Create API key. If unset, recordings save locally only.
VITE_OMI_API_KEY=

# Google integration (parity 3d). Copy this file to `.env` and fill in real values.
# Desktop OAuth client id from Google Cloud Console (an OAuth 2.0 "Desktop app" client).
# Read by the MAIN process (electron-vite MAIN_VITE_ prefix).
MAIN_VITE_GOOGLE_CLIENT_ID=

# Client secret for the same Desktop client. Google's Desktop clients require it
# at the token endpoint even with PKCE. Main-process only; never exposed to the
# renderer. Leave blank only if your client type genuinely needs no secret.
MAIN_VITE_GOOGLE_CLIENT_SECRET=

# Show the Google integration in Settings ('1' = on). Read by the RENDERER.
# Leave unset/'0' for non-test builds until Google verification is granted.
VITE_ENABLE_GOOGLE_INTEGRATION=0
52 changes: 52 additions & 0 deletions windows/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
node_modules
dist
out
.DS_Store
.eslintcache
*.log*

# Secrets — never commit
.env
.env.local
.env.*.local

# Build / installer artifacts
build/
release/
dist/
*.exe
*.msi
*.dmg
*.AppImage

# Perf benchmark artifacts + tooling (dev-only; keep out of the public repo)
.bench/
scripts/bench/
src/main/bench/

# HuggingFace / model cache
.cache/
models/

# OS
Thumbs.db
desktop.ini

#Personal files
.agents/
.vscode/
docs/
skills-lock.json

# Visual brainstorming companion
.superpowers/

# .NET build artifacts (win-ocr-helper)
src/main/ocr/win-ocr-helper/bin/
src/main/ocr/win-ocr-helper/obj/
resources/win-ocr-helper/*.pdb

# .NET build artifacts (win-automation-helper)
src/main/automation/helper/bin/
src/main/automation/helper/obj/
resources/win-automation-helper/*.pdb
1 change: 1 addition & 0 deletions windows/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node-linker=hoisted
6 changes: 6 additions & 0 deletions windows/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
4 changes: 4 additions & 0 deletions windows/.prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
singleQuote: true
semi: false
printWidth: 100
trailingComma: none
60 changes: 60 additions & 0 deletions windows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# omi-windows

Omi for Windows — an Electron + React + TypeScript port of the Omi desktop app.

## Recommended IDE Setup

- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)

## Run from source

```bash
# 1. Install dependencies
npm install

# 2. Create your local env file (required — the app won't start without it)
cp .env.example .env

# 3. Start the app
npm run dev
```

`.env` is gitignored. `.env.example` ships with Omi's **public** Firebase + PostHog
config, so after `cp .env.example .env` the app runs and sign-in works with no extra
keys to obtain.

## Authentication

- **App sign-in:** each user signs in with **their own** Google/Omi account through
the built-in popup. The Firebase project is shared (Omi's `based-hardware`); accounts
are individual. Nothing to configure — it works out of the box from `.env.example`.
- **Google integration** (optional Gmail/Google connect — separate from sign-in): bring
your own credentials. Create an OAuth **Desktop app** client in the
[Google Cloud Console](https://console.cloud.google.com/apis/credentials), then in your
local `.env` set `MAIN_VITE_GOOGLE_CLIENT_ID`, `MAIN_VITE_GOOGLE_CLIENT_SECRET`, and
`VITE_ENABLE_GOOGLE_INTEGRATION=1`. Keep these in your local `.env` only — never commit them.

## Optional keys

Everything below is blank in `.env.example` and safe to leave unset:

- `VITE_OMI_API_KEY` — cloud-sync recorded conversations (generate in Omi → Settings →
Developer). Blank = recordings save locally only.
- `MAIN_VITE_GOOGLE_CLIENT_ID` / `MAIN_VITE_GOOGLE_CLIENT_SECRET` /
`VITE_ENABLE_GOOGLE_INTEGRATION` — the Google integration above.

## Build

```bash
# Windows
npm run build:win

# macOS
npm run build:mac

# Linux
npm run build:linux
```

Vite inlines the `.env` values at build time, so a packaged installer needs no `.env` —
the config is compiled into the binary.
54 changes: 54 additions & 0 deletions windows/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
appId: com.omiwindows.app
productName: Omi for Windows
directories:
buildResources: build
files:
# Explicit default; positive patterns below would otherwise replace it.
- '**/*'
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
- '!{tailwind.config.ts,postcss.config.js}'
- '!resources/win-ocr-helper/*.pdb'
- '!resources/win-automation-helper/*.pdb'
asarUnpack:
- resources/**
# koffi loads its native .node at runtime, resolved relative to its own package
# dir — it must live outside the asar archive or the foreground monitor fails.
- node_modules/koffi/**
win:
executableName: omi-windows
target:
- target: nsis
arch: [x64]
nsis:
oneClick: false
perMachine: false
allowToChangeInstallationDirectory: true
artifactName: ${productName}-Setup-${version}.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
26 changes: 26 additions & 0 deletions windows/electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { resolve } from 'path'
import { defineConfig } from 'electron-vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
main: {},
preload: {},
renderer: {
// Pin the dev server to a fixed port so the renderer's origin
// (http://localhost:5179) — and therefore its localStorage (onboarding flag,
// preferences, Firebase session) — stays stable across launches. Without a
// pinned port, a second worktree holding 5173 pushes this one to 5174, which
// is a different origin and silently drops all saved state (re-onboarding).
// strictPort fails fast instead of drifting to a new origin.
server: {
port: 5179,
strictPort: true
},
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
}
},
plugins: [react()]
}
})
32 changes: 32 additions & 0 deletions windows/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { defineConfig } from 'eslint/config'
import tseslint from '@electron-toolkit/eslint-config-ts'
import eslintConfigPrettier from '@electron-toolkit/eslint-config-prettier'
import eslintPluginReact from 'eslint-plugin-react'
import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
import eslintPluginReactRefresh from 'eslint-plugin-react-refresh'

export default defineConfig(
{ ignores: ['**/node_modules', '**/dist', '**/out'] },
tseslint.configs.recommended,
eslintPluginReact.configs.flat.recommended,
eslintPluginReact.configs.flat['jsx-runtime'],
{
settings: {
react: {
version: 'detect'
}
}
},
{
files: ['**/*.{ts,tsx}'],
plugins: {
'react-hooks': eslintPluginReactHooks,
'react-refresh': eslintPluginReactRefresh
},
rules: {
...eslintPluginReactHooks.configs.recommended.rules,
...eslintPluginReactRefresh.configs.vite.rules
}
},
eslintConfigPrettier
)
83 changes: 83 additions & 0 deletions windows/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"name": "omi-windows",
"version": "1.0.0",
"description": "An Electron application with React and TypeScript",
"main": "./out/main/index.js",
"author": "example.com",
"homepage": "https://electron-vite.org",
"scripts": {
"format": "prettier --write .",
"lint": "eslint --cache .",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "npm run typecheck && electron-vite build",
"rebuild:sqlite": "electron-rebuild -f -w better-sqlite3",
"build:ocr-helper": "powershell -ExecutionPolicy Bypass -File scripts/build-ocr-helper.ps1",
"postinstall": "electron-builder install-app-deps && electron-rebuild -f -w better-sqlite3 && node scripts/ensure-ocr-helper.mjs",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win --x64",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
"test": "vitest run",
"test:watch": "vitest",
"bench": "node scripts/bench/run.mjs",
"bench:anim": "node scripts/bench/anim.mjs"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"axios": "^1.16.1",
"better-sqlite3": "^12.10.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"d3-force": "^3.0.0",
"d3-force-3d": "^3.0.6",
"firebase": "^12.13.0",
"koffi": "^3.0.2",
"lucide-react": "^1.16.0",
"react-router-dom": "^7.15.1",
"tailwind-merge": "^3.6.0",
"three": "^0.184.0",
"ws": "^8.21.0"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^2.0.0",
"@electron/rebuild": "^4.0.4",
"@types/better-sqlite3": "^7.6.13",
"@types/d3-force": "^3.0.10",
"@types/node": "^22.19.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@types/three": "^0.184.1",
"@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.5.0",
"electron": "^39.2.6",
"electron-builder": "^26.0.12",
"electron-vite": "^5.0.0",
"eslint": "^9.39.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"postcss": "^8.5.15",
"prettier": "^3.7.4",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"tailwindcss": "^3.4.19",
"typescript": "^5.9.3",
"vite": "^7.2.6",
"vitest": "^3.2.4"
}
}
Loading
Loading