Imported from KONFeature/wordforge (
packages/desktop/src/AGENTS.md). Install upstream withnpx skills add KONFeature/wordforge --skill src. Copyright stays with the author.
@wordforge/desktop/src - React Frontend
Tauri desktop app frontend using React, TanStack Router, and TanStack Query.
Structure
src/
├── main.tsx # Entry point (QueryClient + RouterProvider)
├── router.tsx # TanStack Router with hash history
├── routeTree.gen.ts # AUTO-GENERATED - do not edit
├── components/ # UI components
├── hooks/ # React hooks (useOpenCode, useSites, etc.)
├── context/ # React contexts (OpenCodeClientContext)
├── routes/ # File-based routing (TanStack Router)
│ ├── __root.tsx # Root layout
│ ├── index.tsx # Home route
│ ├── onboarding.tsx # Onboarding flow
│ └── site/$siteId/ # Site-specific routes
├── lib/ # Utilities (wpFetch)
├── styles/ # Global CSS
└── types.ts # Shared types
CRITICAL: Standard React (NOT WordPress)
// CORRECT - Regular React imports
import { useState, useEffect, useCallback } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
// WRONG - This is for @wordforge/ui only
import { useState } from '@wordpress/element'; // NO!
Tauri IPC Pattern
All backend communication via invoke():
import { invoke } from '@tauri-apps/api/core';
// Call Rust command
const port = await invoke<number>('start_opencode');
const sites = await invoke<WordPressSite[]>('list_sites');
Available commands (defined in src-tauri/src/lib.rs):
get_status,download_opencode,start_opencode,stop_opencodelist_sites,get_active_site,set_active_site,remove_site,connect_sitecheck_config_update,refresh_site_config
Event Listeners
import { listen } from '@tauri-apps/api/event';
// Listen for backend events
useEffect(() => {
const unlisten = listen<DownloadProgress>('opencode:download-progress', (event) => {
setProgress(event.payload);
});
return () => { unlisten.then(fn => fn()); };
}, []);
Events emitted by Rust:
opencode:download-progress- Download progress updatesopencode:log/opencode:error- Server stdout/stderropencode:idle-shutdown- Idle timeout reacheddeep-link:connect- WordPress connection viawordforge://URLconfig:updated- Config refresh completed
Hooks Pattern
// Query keys pattern
const siteKeys = {
all: ['sites'] as const,
list: () => [...siteKeys.all, 'list'] as const,
active: () => [...siteKeys.all, 'active'] as const,
};
// Hook composition
export function useSites() {
const { sites, isLoading } = useSitesList();
const { activeSite } = useActiveSite();
const mutations = useSiteMutations();
return { sites, activeSite, isLoading, ...mutations };
}
Routing (TanStack Router)
File-based routing with hash history:
// routes/site/$siteId/index.tsx
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/site/$siteId/')({
component: SiteDashboard,
});
// Access params
const { siteId } = Route.useParams();
Hash history required - Tauri loads from file://, no web server for path routing.
Anti-Patterns
- Using
@wordpress/element- This package uses standard React - Path-based router history - Must use hash history for Tauri
- Editing
routeTree.gen.ts- Auto-generated by TanStack Router - Direct fetch() for WordPress - Use
invoke()orwpFetchutility - Blocking on Tauri events - Always clean up listeners in useEffect