This document explains how to manage and organize platform-specific code (PWA, Chrome Extension) in our codebase using the *.platform convention and the custom vite-plugin-platform-resolver.
The goal is to make platform specific code super easy to write as we prepare for new platform addons (also it was fun to learn).
- Multiple divergent implementations: If a component or module contains more than one or two significant platform-specific functions or logic, extract it into its own file:
Put the platform overrides alongside the
.platformfile.
DO: ✅
// ~/example/PlatformBridge.pwa.ts
export default class PlatformBridge {
/* PWA logic */
}
// ~/example/PlatformBridge.ext.ts
export default class PlatformBridge {
/* Chrome extension logic */
}
// ~/example/PlatformBridge.platform.ts (fallback)
export default class PlatformBridge {
/* generic or shared logic */
}Do not forget the
.platformfile, the plugin will not look for.pwaif there is not a reason to
DO NOT: ❌
// ~/example/PlatformBridge.pwa.ts
export default class PlatformBridge {
/* PWA logic */
}
// ~/example/PlatformBridge.ext.ts
export default class PlatformBridge {
/* Chrome extension logic */
}
// missing .platform File or platform file in another directory- Large platform-specific utility: If a helper or service has platform-only dependencies (e.g.,
ipcRenderer,chrome.runtime, orComlink), keep it isolated in its own file to avoid bundling unused code into other targets.
- Single small conditional: If a file only needs one simple
if (platform === 'electron')check around a trivial line of code, it’s better to keep it inline to reduce fragmentation.
// Good: inline conditional
if (platform === 'electron') {
await ipcRenderer.invoke('do-thing')
} else {
await broadcastChannel.postMessage('do-thing')
}- Shared logic with one exception: If 95% of the code is identical and only a tiny piece differs, consider extracting just that small piece into a helper function rather than splitting the entire file.
- Import Pattern: Use
import Foo from '~/Foo.platform'in your code. - Resolution: At build time,
vite-plugin-platform-resolverwill look in the same directory forFoo.<platform>.*. If found, it uses that file; otherwise it falls back toFoo.platform.*. - Supported Extensions: This works for any file type as long as it has
.platformand its in thesrc/directory (ie the import path starts with~/)
-
Full split (complex logic):
import PlatformBridge from './PlatformBridge.platform' // resolves to PlatformBridge.electron.ts or fallback
-
Inline conditional (simple):
import { platform } from './env' function logEvent(event: string) { if (platform === 'pwa') { console.log('client side code') } else { console.log('server side code') } }
This is meant to be living documentation of llm-x. If you find these examples outdate, incorrect, or confusing please submit a PR to keep this alive!