@remcostoeten/dev-widget
v0.0.1
Published
Minimal, portable dev menu widget for React applications
Maintainers
Readme
@remcostoeten/dev-widget
Minimal, type-safe dev widget for React apps.
Installation
bun add @remcostoeten/dev-widgetReact usage
import { DevWidget } from '@remcostoeten/dev-widget/react'
export function App(): React.ReactElement {
return (
<>
<main>App</main>
<DevWidget
mode='development-only'
theme={{ mode: 'system', accent: '#3bb273' }}
features={{
performance: { enabled: true, fpsThreshold: 50 },
auth: { enabled: true, provider: 'better-auth' }
}}
/>
</>
)
}Features
- Auth domain
- Routes domain
- Links domain
- Performance domain
- System domain
- Monitor domain
Feature checklist
- [x] React widget shell with tab categories
- [x] Domain lifecycle (setup/start/stop) via runtime registry
- [x] Plugin API with typed event subscription
- [x] Event bus for cross-domain communication
- [x] Next.js log ingestion route handler
- [x] In-memory storage adapter
- [x] Tauri storage adapter export
- [x] Route discovery adapter for Next.js and React projects
- [x] Right rail with diagnostics render panel
- [x] Keyboard shortcuts (toggle + Escape)
- [x] Accessibility: focus-visible, reduced-motion handling, live region announcements
- [x] Internal subpath export (
@remcostoeten/dev-widget/internal) - [x] Source maps and declaration output in package build
- [x] CI publish artifact validation step
- [x] Demo app build pipeline
Needs additional work
- AI providers:
openaiandlocalmodes are declared but not implemented yet; onlyrulesandcustomare active (src/domains/performance/aiProviderFactory.ts). - Runtime prop updates:
DevWidgetmemoizes runtime byshouldRenderonly, so runtime does not re-bootstrap when non-visibility props change after mount (src/devWidget.tsx). - Storage wiring:
providers.storageis part of public config but not wired into runtime/domain persistence flow yet (src/types/public.tsandsrc/core/bootstrap.ts). - Endpoint validation: Next log handler accepts event payloads without schema validation; malformed event shapes can be persisted (
src/adapters/next/routeHandler.ts). - CI parity: local checks pass, but final release confidence still depends on a pushed branch and green remote GitHub Actions run.
Plugin API
import { createRuntime } from '@remcostoeten/dev-widget'
const runtime = createRuntime({ mode: 'always' })
await runtime.registerPlugin({
id: 'my-plugin',
version: '1.0.0',
register(ctx) {
ctx.onEvent('performance/drop-detected', function onDrop(payload) {
console.log('FPS drop:', payload.fps)
})
}
})Subpath exports
import { DevWidget } from '@remcostoeten/dev-widget/react'
import { createLogHandler } from '@remcostoeten/dev-widget/next'
import { createTauriStorage } from '@remcostoeten/dev-widget/tauri'
import type { WidgetDomain } from '@remcostoeten/dev-widget/internal'Accessibility
The widget includes keyboard shortcuts, focus-visible styling, live announcements, and reduced-motion support.
See specs/10-dev-widget-accessibility-keyboard-spec.md for the detailed behavior contract.
Core options
enabled?: booleanmode?: 'development-only' | 'always'theme?: { mode?: 'system' | 'light' | 'dark'; accent?: string; density?: 'compact' | 'comfortable'; radius?: 'none' | 'sm' | 'md' | 'lg' }features?: { auth?: boolean | AuthOptions; routes?: boolean | RoutesOptions; performance?: boolean | PerfOptions; system?: boolean | SystemOptions; links?: boolean | LinksOptions }disable?: FeatureId[]providers?: { auth?: AuthConfig; ai?: AiConfig; storage?: StorageConfig }keyboard?: { toggleKey?: string; enableShortcuts?: boolean }accessibility?: { reducedMotion?: 'system' | 'always' | 'never'; announceUpdates?: boolean }rightRail?: { enabled?: boolean; defaultPanel?: string | null; collapsible?: boolean; panels?: PanelConfig[] }
Changelog
Contributing
bun run lint
bun run format:check
bun run type-check
bun test
bun run buildLicense
MIT
