claude-mpm 5.4.55__py3-none-any.whl → 5.4.85__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md +405 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +63 -241
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +36 -9
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/skills.py +166 -14
- claude_mpm/cli/executor.py +1 -0
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/startup.py +223 -388
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/output_style_manager.py +21 -13
- claude_mpm/core/unified_config.py +50 -8
- claude_mpm/core/unified_paths.py +30 -13
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +8 -0
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +7 -4
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/pm_skills_deployer.py +4 -0
- claude_mpm/services/skills/git_skill_source_manager.py +24 -8
- claude_mpm/services/skills/selective_skill_deployer.py +82 -83
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/pm-bug-reporting/pm-bug-reporting.md +248 -0
- claude_mpm/skills/bundled/pm/pm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/pm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/pm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/pm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/pm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/pm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/METADATA +47 -23
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/RECORD +159 -47
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.4.85.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tauri-frontend-integration
|
|
3
|
+
description: Frontend integration patterns for Tauri including TypeScript services, React hooks, Vue composition, Svelte stores, and framework-agnostic patterns
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: development
|
|
6
|
+
author: Claude MPM Team
|
|
7
|
+
license: MIT
|
|
8
|
+
progressive_disclosure:
|
|
9
|
+
entry_point:
|
|
10
|
+
summary: "Frontend patterns: TypeScript service layer, React hooks for commands/events, Vue/Svelte integration, type safety across IPC boundary"
|
|
11
|
+
when_to_use: "Building Tauri frontend with React, Vue, Svelte, or vanilla JS with proper TypeScript types and abstractions"
|
|
12
|
+
quick_start: "1. Create typed service layer 2. Framework-specific hooks/composables 3. Event handling 4. Error boundaries"
|
|
13
|
+
context_limit: 700
|
|
14
|
+
tags:
|
|
15
|
+
- tauri
|
|
16
|
+
- frontend
|
|
17
|
+
- react
|
|
18
|
+
- vue
|
|
19
|
+
- svelte
|
|
20
|
+
- typescript
|
|
21
|
+
- integration
|
|
22
|
+
requires_tools: []
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Tauri Frontend Integration
|
|
26
|
+
|
|
27
|
+
## TypeScript Service Layer
|
|
28
|
+
|
|
29
|
+
### Type-Safe Command Service
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// src/services/tauri.ts
|
|
33
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
34
|
+
|
|
35
|
+
// Backend command types
|
|
36
|
+
export interface FileMetadata {
|
|
37
|
+
name: string;
|
|
38
|
+
size: number;
|
|
39
|
+
modified: number;
|
|
40
|
+
isDir: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UserConfig {
|
|
44
|
+
theme: 'light' | 'dark';
|
|
45
|
+
language: string;
|
|
46
|
+
autoSave: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Command service with full typing
|
|
50
|
+
export class TauriService {
|
|
51
|
+
static async readFile(path: string): Promise<string> {
|
|
52
|
+
return await invoke<string>('read_file', { path });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static async writeFile(path: string, content: string): Promise<void> {
|
|
56
|
+
await invoke('write_file', { path, content });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static async listFiles(directory: string): Promise<FileMetadata[]> {
|
|
60
|
+
return await invoke<FileMetadata[]>('list_files', { directory });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static async getConfig(): Promise<UserConfig> {
|
|
64
|
+
return await invoke<UserConfig>('get_config');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static async updateConfig(config: Partial<UserConfig>): Promise<void> {
|
|
68
|
+
await invoke('update_config', { config });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static async performSearch(query: string, caseSensitive: boolean): Promise<string[]> {
|
|
72
|
+
return await invoke<string[]>('perform_search', {
|
|
73
|
+
query,
|
|
74
|
+
caseSensitive
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Result Type Pattern
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// src/types/result.ts
|
|
84
|
+
export type Result<T, E = string> =
|
|
85
|
+
| { ok: true; value: T }
|
|
86
|
+
| { ok: false; error: E };
|
|
87
|
+
|
|
88
|
+
export function Ok<T>(value: T): Result<T> {
|
|
89
|
+
return { ok: true, value };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function Err<E = string>(error: E): Result<never, E> {
|
|
93
|
+
return { ok: false, error };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Service using Result type
|
|
97
|
+
export class SafeTauriService {
|
|
98
|
+
static async readFile(path: string): Promise<Result<string>> {
|
|
99
|
+
try {
|
|
100
|
+
const content = await invoke<string>('read_file', { path });
|
|
101
|
+
return Ok(content);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return Err(String(error));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static async writeFile(path: string, content: string): Promise<Result<void>> {
|
|
108
|
+
try {
|
|
109
|
+
await invoke('write_file', { path, content });
|
|
110
|
+
return Ok(undefined);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return Err(String(error));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## React Integration
|
|
119
|
+
|
|
120
|
+
### Custom Hooks for Commands
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// src/hooks/useTauriCommand.ts
|
|
124
|
+
import { useState, useEffect } from 'react';
|
|
125
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
126
|
+
|
|
127
|
+
export interface CommandState<T> {
|
|
128
|
+
data: T | null;
|
|
129
|
+
loading: boolean;
|
|
130
|
+
error: string | null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function useTauriCommand<T>(
|
|
134
|
+
command: string,
|
|
135
|
+
args?: Record<string, unknown>,
|
|
136
|
+
immediate: boolean = true
|
|
137
|
+
): CommandState<T> & { refetch: () => Promise<void> } {
|
|
138
|
+
const [state, setState] = useState<CommandState<T>>({
|
|
139
|
+
data: null,
|
|
140
|
+
loading: immediate,
|
|
141
|
+
error: null
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const execute = async () => {
|
|
145
|
+
setState(prev => ({ ...prev, loading: true, error: null }));
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const data = await invoke<T>(command, args);
|
|
149
|
+
setState({ data, loading: false, error: null });
|
|
150
|
+
} catch (error) {
|
|
151
|
+
setState({ data: null, loading: false, error: String(error) });
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (immediate) {
|
|
157
|
+
execute();
|
|
158
|
+
}
|
|
159
|
+
}, [command, JSON.stringify(args)]);
|
|
160
|
+
|
|
161
|
+
return { ...state, refetch: execute };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Usage
|
|
165
|
+
function FileList() {
|
|
166
|
+
const { data: files, loading, error, refetch } = useTauriCommand<FileMetadata[]>(
|
|
167
|
+
'list_files',
|
|
168
|
+
{ directory: '/home/user/documents' }
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (loading) return <div>Loading...</div>;
|
|
172
|
+
if (error) return <div>Error: {error}</div>;
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<div>
|
|
176
|
+
<button onClick={refetch}>Refresh</button>
|
|
177
|
+
{files?.map(file => (
|
|
178
|
+
<div key={file.name}>{file.name}</div>
|
|
179
|
+
))}
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Event Hooks
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// src/hooks/useTauriEvent.ts
|
|
189
|
+
import { useEffect, useState } from 'react';
|
|
190
|
+
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
191
|
+
|
|
192
|
+
export function useTauriEvent<T>(event: string): T | null {
|
|
193
|
+
const [data, setData] = useState<T | null>(null);
|
|
194
|
+
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
let unlisten: UnlistenFn;
|
|
197
|
+
|
|
198
|
+
listen<T>(event, (e) => {
|
|
199
|
+
setData(e.payload);
|
|
200
|
+
}).then(fn => {
|
|
201
|
+
unlisten = fn;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return () => {
|
|
205
|
+
if (unlisten) unlisten();
|
|
206
|
+
};
|
|
207
|
+
}, [event]);
|
|
208
|
+
|
|
209
|
+
return data;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Usage
|
|
213
|
+
function ProgressBar() {
|
|
214
|
+
const progress = useTauriEvent<{ current: number; total: number }>('download-progress');
|
|
215
|
+
|
|
216
|
+
if (!progress) return null;
|
|
217
|
+
|
|
218
|
+
const percentage = (progress.current / progress.total) * 100;
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div className="progress-bar">
|
|
222
|
+
<div style={{ width: `${percentage}%` }} />
|
|
223
|
+
<span>{percentage.toFixed(0)}%</span>
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Bidirectional Event Hook
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// src/hooks/useTauriEventEmitter.ts
|
|
233
|
+
import { useCallback, useEffect } from 'react';
|
|
234
|
+
import { listen, emit, UnlistenFn } from '@tauri-apps/api/event';
|
|
235
|
+
|
|
236
|
+
export function useTauriEventEmitter<T, R>(
|
|
237
|
+
emitEvent: string,
|
|
238
|
+
listenEvent: string,
|
|
239
|
+
handler: (data: R) => void
|
|
240
|
+
) {
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
let unlisten: UnlistenFn;
|
|
243
|
+
|
|
244
|
+
listen<R>(listenEvent, (e) => {
|
|
245
|
+
handler(e.payload);
|
|
246
|
+
}).then(fn => {
|
|
247
|
+
unlisten = fn;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return () => {
|
|
251
|
+
if (unlisten) unlisten();
|
|
252
|
+
};
|
|
253
|
+
}, [listenEvent, handler]);
|
|
254
|
+
|
|
255
|
+
const send = useCallback(async (data: T) => {
|
|
256
|
+
await emit(emitEvent, data);
|
|
257
|
+
}, [emitEvent]);
|
|
258
|
+
|
|
259
|
+
return send;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Usage
|
|
263
|
+
function Chat() {
|
|
264
|
+
const [messages, setMessages] = useState<string[]>([]);
|
|
265
|
+
|
|
266
|
+
const sendMessage = useTauriEventEmitter<string, string>(
|
|
267
|
+
'send-message',
|
|
268
|
+
'receive-message',
|
|
269
|
+
(msg) => setMessages(prev => [...prev, msg])
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
{messages.map((msg, i) => <div key={i}>{msg}</div>)}
|
|
275
|
+
<button onClick={() => sendMessage('Hello!')}>Send</button>
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Context Provider Pattern
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// src/contexts/TauriContext.tsx
|
|
285
|
+
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
286
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
287
|
+
|
|
288
|
+
interface AppState {
|
|
289
|
+
config: UserConfig | null;
|
|
290
|
+
updateConfig: (config: Partial<UserConfig>) => Promise<void>;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const TauriContext = createContext<AppState | undefined>(undefined);
|
|
294
|
+
|
|
295
|
+
export function TauriProvider({ children }: { children: React.ReactNode }) {
|
|
296
|
+
const [config, setConfig] = useState<UserConfig | null>(null);
|
|
297
|
+
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
invoke<UserConfig>('get_config').then(setConfig);
|
|
300
|
+
}, []);
|
|
301
|
+
|
|
302
|
+
const updateConfig = async (newConfig: Partial<UserConfig>) => {
|
|
303
|
+
await invoke('update_config', { config: newConfig });
|
|
304
|
+
setConfig(prev => ({ ...prev!, ...newConfig }));
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<TauriContext.Provider value={{ config, updateConfig }}>
|
|
309
|
+
{children}
|
|
310
|
+
</TauriContext.Provider>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function useTauri() {
|
|
315
|
+
const context = useContext(TauriContext);
|
|
316
|
+
if (!context) throw new Error('useTauri must be used within TauriProvider');
|
|
317
|
+
return context;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Usage
|
|
321
|
+
function App() {
|
|
322
|
+
return (
|
|
323
|
+
<TauriProvider>
|
|
324
|
+
<SettingsPanel />
|
|
325
|
+
</TauriProvider>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function SettingsPanel() {
|
|
330
|
+
const { config, updateConfig } = useTauri();
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<div>
|
|
334
|
+
<label>
|
|
335
|
+
<input
|
|
336
|
+
type="checkbox"
|
|
337
|
+
checked={config?.autoSave}
|
|
338
|
+
onChange={(e) => updateConfig({ autoSave: e.target.checked })}
|
|
339
|
+
/>
|
|
340
|
+
Auto Save
|
|
341
|
+
</label>
|
|
342
|
+
</div>
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Vue 3 Integration
|
|
348
|
+
|
|
349
|
+
### Composition API Composables
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// src/composables/useTauriCommand.ts
|
|
353
|
+
import { ref, Ref, onMounted } from 'vue';
|
|
354
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
355
|
+
|
|
356
|
+
export function useTauriCommand<T>(
|
|
357
|
+
command: string,
|
|
358
|
+
args?: Record<string, unknown>,
|
|
359
|
+
immediate: boolean = true
|
|
360
|
+
) {
|
|
361
|
+
const data: Ref<T | null> = ref(null);
|
|
362
|
+
const loading = ref(immediate);
|
|
363
|
+
const error: Ref<string | null> = ref(null);
|
|
364
|
+
|
|
365
|
+
const execute = async () => {
|
|
366
|
+
loading.value = true;
|
|
367
|
+
error.value = null;
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
data.value = await invoke<T>(command, args);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
error.value = String(e);
|
|
373
|
+
} finally {
|
|
374
|
+
loading.value = false;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
if (immediate) {
|
|
379
|
+
onMounted(execute);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return { data, loading, error, refetch: execute };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Usage in component
|
|
386
|
+
<script setup lang="ts">
|
|
387
|
+
import { useTauriCommand } from '@/composables/useTauriCommand';
|
|
388
|
+
|
|
389
|
+
const { data: files, loading, error } = useTauriCommand<FileMetadata[]>(
|
|
390
|
+
'list_files',
|
|
391
|
+
{ directory: '/home/user' }
|
|
392
|
+
);
|
|
393
|
+
</script>
|
|
394
|
+
|
|
395
|
+
<template>
|
|
396
|
+
<div v-if="loading">Loading...</div>
|
|
397
|
+
<div v-else-if="error">Error: {{ error }}</div>
|
|
398
|
+
<div v-else>
|
|
399
|
+
<div v-for="file in files" :key="file.name">
|
|
400
|
+
{{ file.name }}
|
|
401
|
+
</div>
|
|
402
|
+
</div>
|
|
403
|
+
</template>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Event Composable
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// src/composables/useTauriEvent.ts
|
|
410
|
+
import { ref, Ref, onMounted, onUnmounted } from 'vue';
|
|
411
|
+
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
412
|
+
|
|
413
|
+
export function useTauriEvent<T>(event: string): Ref<T | null> {
|
|
414
|
+
const data: Ref<T | null> = ref(null);
|
|
415
|
+
let unlisten: UnlistenFn;
|
|
416
|
+
|
|
417
|
+
onMounted(async () => {
|
|
418
|
+
unlisten = await listen<T>(event, (e) => {
|
|
419
|
+
data.value = e.payload;
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
onUnmounted(() => {
|
|
424
|
+
if (unlisten) unlisten();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
return data;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Usage
|
|
431
|
+
<script setup lang="ts">
|
|
432
|
+
const progress = useTauriEvent<{ current: number; total: number }>('download-progress');
|
|
433
|
+
</script>
|
|
434
|
+
|
|
435
|
+
<template>
|
|
436
|
+
<div v-if="progress">
|
|
437
|
+
{{ Math.floor((progress.current / progress.total) * 100) }}%
|
|
438
|
+
</div>
|
|
439
|
+
</template>
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Provide/Inject Pattern
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// src/composables/useTauriApp.ts
|
|
446
|
+
import { provide, inject, InjectionKey, Ref, ref, onMounted } from 'vue';
|
|
447
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
448
|
+
|
|
449
|
+
interface TauriApp {
|
|
450
|
+
config: Ref<UserConfig | null>;
|
|
451
|
+
updateConfig: (config: Partial<UserConfig>) => Promise<void>;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const TauriAppKey: InjectionKey<TauriApp> = Symbol('TauriApp');
|
|
455
|
+
|
|
456
|
+
export function provideTauriApp() {
|
|
457
|
+
const config = ref<UserConfig | null>(null);
|
|
458
|
+
|
|
459
|
+
onMounted(async () => {
|
|
460
|
+
config.value = await invoke<UserConfig>('get_config');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const updateConfig = async (newConfig: Partial<UserConfig>) => {
|
|
464
|
+
await invoke('update_config', { config: newConfig });
|
|
465
|
+
config.value = { ...config.value!, ...newConfig };
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const app: TauriApp = { config, updateConfig };
|
|
469
|
+
provide(TauriAppKey, app);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export function useTauriApp(): TauriApp {
|
|
473
|
+
const app = inject(TauriAppKey);
|
|
474
|
+
if (!app) throw new Error('useTauriApp must be called within TauriApp provider');
|
|
475
|
+
return app;
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## Svelte Integration
|
|
480
|
+
|
|
481
|
+
### Svelte Stores
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// src/stores/tauri.ts
|
|
485
|
+
import { writable, derived, Readable } from 'svelte/store';
|
|
486
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
487
|
+
import { listen } from '@tauri-apps/api/event';
|
|
488
|
+
|
|
489
|
+
export function createCommandStore<T>(command: string, args?: Record<string, unknown>) {
|
|
490
|
+
const { subscribe, set } = writable<{
|
|
491
|
+
data: T | null;
|
|
492
|
+
loading: boolean;
|
|
493
|
+
error: string | null;
|
|
494
|
+
}>({
|
|
495
|
+
data: null,
|
|
496
|
+
loading: true,
|
|
497
|
+
error: null
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const load = async () => {
|
|
501
|
+
set({ data: null, loading: true, error: null });
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
const data = await invoke<T>(command, args);
|
|
505
|
+
set({ data, loading: false, error: null });
|
|
506
|
+
} catch (error) {
|
|
507
|
+
set({ data: null, loading: false, error: String(error) });
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
load();
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
subscribe,
|
|
515
|
+
refetch: load
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Usage
|
|
520
|
+
<script lang="ts">
|
|
521
|
+
import { createCommandStore } from '$lib/stores/tauri';
|
|
522
|
+
|
|
523
|
+
const files = createCommandStore<FileMetadata[]>('list_files', {
|
|
524
|
+
directory: '/home/user'
|
|
525
|
+
});
|
|
526
|
+
</script>
|
|
527
|
+
|
|
528
|
+
{#if $files.loading}
|
|
529
|
+
<div>Loading...</div>
|
|
530
|
+
{:else if $files.error}
|
|
531
|
+
<div>Error: {$files.error}</div>
|
|
532
|
+
{:else}
|
|
533
|
+
{#each $files.data ?? [] as file}
|
|
534
|
+
<div>{file.name}</div>
|
|
535
|
+
{/each}
|
|
536
|
+
{/if}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Event Store
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
// src/stores/events.ts
|
|
543
|
+
import { writable } from 'svelte/store';
|
|
544
|
+
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
545
|
+
|
|
546
|
+
export function createEventStore<T>(event: string) {
|
|
547
|
+
const { subscribe, set } = writable<T | null>(null);
|
|
548
|
+
|
|
549
|
+
let unlisten: UnlistenFn;
|
|
550
|
+
|
|
551
|
+
listen<T>(event, (e) => {
|
|
552
|
+
set(e.payload);
|
|
553
|
+
}).then(fn => {
|
|
554
|
+
unlisten = fn;
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
subscribe,
|
|
559
|
+
cleanup: () => {
|
|
560
|
+
if (unlisten) unlisten();
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Usage
|
|
566
|
+
<script lang="ts">
|
|
567
|
+
import { onDestroy } from 'svelte';
|
|
568
|
+
|
|
569
|
+
const progress = createEventStore<{ current: number; total: number }>('download-progress');
|
|
570
|
+
|
|
571
|
+
onDestroy(() => {
|
|
572
|
+
progress.cleanup();
|
|
573
|
+
});
|
|
574
|
+
</script>
|
|
575
|
+
|
|
576
|
+
{#if $progress}
|
|
577
|
+
<div>
|
|
578
|
+
{Math.floor(($progress.current / $progress.total) * 100)}%
|
|
579
|
+
</div>
|
|
580
|
+
{/if}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
## Framework-Agnostic Patterns
|
|
584
|
+
|
|
585
|
+
### Observable Pattern
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// src/utils/observable.ts
|
|
589
|
+
export class TauriObservable<T> {
|
|
590
|
+
private listeners: Set<(data: T) => void> = new Set();
|
|
591
|
+
private value: T | null = null;
|
|
592
|
+
|
|
593
|
+
subscribe(listener: (data: T) => void): () => void {
|
|
594
|
+
this.listeners.add(listener);
|
|
595
|
+
if (this.value !== null) listener(this.value);
|
|
596
|
+
|
|
597
|
+
return () => this.listeners.delete(listener);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
emit(data: T) {
|
|
601
|
+
this.value = data;
|
|
602
|
+
this.listeners.forEach(listener => listener(data));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
getValue(): T | null {
|
|
606
|
+
return this.value;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Command observable
|
|
611
|
+
export function createCommandObservable<T>(
|
|
612
|
+
command: string,
|
|
613
|
+
args?: Record<string, unknown>
|
|
614
|
+
): TauriObservable<T> {
|
|
615
|
+
const observable = new TauriObservable<T>();
|
|
616
|
+
|
|
617
|
+
invoke<T>(command, args)
|
|
618
|
+
.then(data => observable.emit(data))
|
|
619
|
+
.catch(err => console.error(err));
|
|
620
|
+
|
|
621
|
+
return observable;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Event observable
|
|
625
|
+
export function createEventObservable<T>(event: string): TauriObservable<T> {
|
|
626
|
+
const observable = new TauriObservable<T>();
|
|
627
|
+
|
|
628
|
+
listen<T>(event, (e) => {
|
|
629
|
+
observable.emit(e.payload);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
return observable;
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
## Error Boundaries
|
|
637
|
+
|
|
638
|
+
### React Error Boundary
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
// src/components/TauriErrorBoundary.tsx
|
|
642
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
643
|
+
|
|
644
|
+
interface Props {
|
|
645
|
+
children: ReactNode;
|
|
646
|
+
fallback?: (error: Error) => ReactNode;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
interface State {
|
|
650
|
+
hasError: boolean;
|
|
651
|
+
error: Error | null;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
export class TauriErrorBoundary extends Component<Props, State> {
|
|
655
|
+
state: State = {
|
|
656
|
+
hasError: false,
|
|
657
|
+
error: null
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
static getDerivedStateFromError(error: Error): State {
|
|
661
|
+
return { hasError: true, error };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
665
|
+
console.error('Tauri Error:', error, errorInfo);
|
|
666
|
+
|
|
667
|
+
// Log to backend
|
|
668
|
+
import('@tauri-apps/api/core').then(({ invoke }) => {
|
|
669
|
+
invoke('log_error', {
|
|
670
|
+
error: error.toString(),
|
|
671
|
+
info: errorInfo.componentStack
|
|
672
|
+
}).catch(console.error);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
render() {
|
|
677
|
+
if (this.state.hasError && this.state.error) {
|
|
678
|
+
if (this.props.fallback) {
|
|
679
|
+
return this.props.fallback(this.state.error);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return (
|
|
683
|
+
<div className="error-boundary">
|
|
684
|
+
<h2>Something went wrong</h2>
|
|
685
|
+
<pre>{this.state.error.message}</pre>
|
|
686
|
+
</div>
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return this.props.children;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Usage
|
|
695
|
+
<TauriErrorBoundary>
|
|
696
|
+
<App />
|
|
697
|
+
</TauriErrorBoundary>
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
## Best Practices
|
|
701
|
+
|
|
702
|
+
1. **Type everything** - Full TypeScript types across IPC boundary
|
|
703
|
+
2. **Service layer** - Abstract Tauri commands into typed service
|
|
704
|
+
3. **Framework hooks/composables** - Idiomatic patterns for each framework
|
|
705
|
+
4. **Error boundaries** - Catch and log Tauri errors
|
|
706
|
+
5. **Loading states** - Always handle loading/error states
|
|
707
|
+
6. **Event cleanup** - Unlisten from events on unmount
|
|
708
|
+
7. **Result types** - Use Result<T, E> for explicit error handling
|
|
709
|
+
8. **Context/Provide** - Share Tauri state across component tree
|
|
710
|
+
9. **Observable pattern** - Framework-agnostic state management
|
|
711
|
+
10. **Async safety** - Handle race conditions and cancellation
|
|
712
|
+
|
|
713
|
+
## Common Pitfalls
|
|
714
|
+
|
|
715
|
+
❌ **Not unlistening from events**:
|
|
716
|
+
```typescript
|
|
717
|
+
// WRONG - memory leak
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
listen('my-event', handler);
|
|
720
|
+
}, []);
|
|
721
|
+
|
|
722
|
+
// CORRECT - cleanup
|
|
723
|
+
useEffect(() => {
|
|
724
|
+
let unlisten: UnlistenFn;
|
|
725
|
+
listen('my-event', handler).then(fn => unlisten = fn);
|
|
726
|
+
return () => { if (unlisten) unlisten(); };
|
|
727
|
+
}, []);
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
❌ **Missing loading/error states**:
|
|
731
|
+
```typescript
|
|
732
|
+
// WRONG - no feedback
|
|
733
|
+
const data = await invoke('command');
|
|
734
|
+
return <div>{data}</div>;
|
|
735
|
+
|
|
736
|
+
// CORRECT - handle all states
|
|
737
|
+
const { data, loading, error } = useTauriCommand('command');
|
|
738
|
+
if (loading) return <div>Loading...</div>;
|
|
739
|
+
if (error) return <div>Error: {error}</div>;
|
|
740
|
+
return <div>{data}</div>;
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
❌ **Invoking in render**:
|
|
744
|
+
```typescript
|
|
745
|
+
// WRONG - invokes every render
|
|
746
|
+
function Component() {
|
|
747
|
+
const data = invoke('command'); // DON'T DO THIS
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// CORRECT - use hooks/effects
|
|
751
|
+
function Component() {
|
|
752
|
+
const { data } = useTauriCommand('command');
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
## Summary
|
|
757
|
+
|
|
758
|
+
- **TypeScript service layer** for type-safe command abstraction
|
|
759
|
+
- **React hooks** (useTauriCommand, useTauriEvent) for declarative data
|
|
760
|
+
- **Vue composables** with Composition API patterns
|
|
761
|
+
- **Svelte stores** for reactive command/event data
|
|
762
|
+
- **Framework-agnostic** observable pattern
|
|
763
|
+
- **Error boundaries** to catch and log Tauri errors
|
|
764
|
+
- **Result types** for explicit error handling
|
|
765
|
+
- **Context patterns** for shared Tauri state
|
|
766
|
+
- **Event cleanup** to prevent memory leaks
|
|
767
|
+
- **Type safety** across entire IPC boundary
|