claude-mpm 5.4.36__py3-none-any.whl → 5.4.62__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +5 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +489 -177
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/cli/commands/configure_agent_display.py +12 -0
- claude_mpm/cli/commands/mpm_init/core.py +72 -0
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/skills.py +14 -18
- claude_mpm/cli/executor.py +10 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -6
- claude_mpm/cli/startup.py +433 -147
- claude_mpm/commands/mpm-config.md +13 -250
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -206
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +61 -441
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/core/optimized_startup.py +61 -0
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CWc5urbQ.js → 4TdZjIqw.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → Vzk33B_K.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.m1gL8KXf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +10 -10
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/init.py +276 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/deployment/agent_deployment.py +22 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +149 -4
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +47 -26
- claude_mpm/services/agents/git_source_manager.py +21 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
- claude_mpm/services/monitor/management/lifecycle.py +7 -1
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/skills/git_skill_source_manager.py +148 -11
- claude_mpm/services/skills/selective_skill_deployer.py +97 -48
- claude_mpm/services/skills_deployer.py +161 -65
- 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-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-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/security-scanning.md +112 -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/skills/skill_manager.py +98 -3
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/METADATA +3 -2
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/RECORD +244 -68
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tauri-event-system
|
|
3
|
+
description: Advanced Tauri event patterns for bidirectional communication, streaming data, window-to-window messaging, and custom event handling
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: development
|
|
6
|
+
author: Claude MPM Team
|
|
7
|
+
license: MIT
|
|
8
|
+
progressive_disclosure:
|
|
9
|
+
entry_point:
|
|
10
|
+
summary: "Advanced event patterns: bidirectional events, streaming, window messaging, custom payloads, listener management"
|
|
11
|
+
when_to_use: "When implementing real-time updates, progress tracking, inter-window communication, or streaming data"
|
|
12
|
+
quick_start: "1. Backend emits with window.emit() 2. Frontend listens with listen() 3. Clean up with unlisten() 4. Use typed payloads"
|
|
13
|
+
context_limit: 500
|
|
14
|
+
tags:
|
|
15
|
+
- tauri
|
|
16
|
+
- events
|
|
17
|
+
- ipc
|
|
18
|
+
- streaming
|
|
19
|
+
- real-time
|
|
20
|
+
requires_tools: []
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Tauri Advanced Event System
|
|
24
|
+
|
|
25
|
+
## Event Fundamentals
|
|
26
|
+
|
|
27
|
+
### Backend → Frontend Events
|
|
28
|
+
|
|
29
|
+
**Basic event emission**:
|
|
30
|
+
```rust
|
|
31
|
+
use tauri::Window;
|
|
32
|
+
|
|
33
|
+
#[tauri::command]
|
|
34
|
+
async fn start_download(
|
|
35
|
+
url: String,
|
|
36
|
+
window: Window,
|
|
37
|
+
) -> Result<(), String> {
|
|
38
|
+
window.emit("download-started", url)
|
|
39
|
+
.map_err(|e| e.to_string())?;
|
|
40
|
+
|
|
41
|
+
// Perform download...
|
|
42
|
+
|
|
43
|
+
window.emit("download-complete", "Success")
|
|
44
|
+
.map_err(|e| e.to_string())
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Frontend listener**:
|
|
49
|
+
```typescript
|
|
50
|
+
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
51
|
+
|
|
52
|
+
const unlisten = await listen<string>('download-started', (event) => {
|
|
53
|
+
console.log('Download started:', event.payload);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Clean up when done
|
|
57
|
+
unlisten();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Structured Event Payloads
|
|
61
|
+
|
|
62
|
+
### Typed Events with Serde
|
|
63
|
+
|
|
64
|
+
**Backend**:
|
|
65
|
+
```rust
|
|
66
|
+
use serde::Serialize;
|
|
67
|
+
|
|
68
|
+
#[derive(Serialize, Clone)]
|
|
69
|
+
struct ProgressEvent {
|
|
70
|
+
current: usize,
|
|
71
|
+
total: usize,
|
|
72
|
+
percentage: f64,
|
|
73
|
+
message: String,
|
|
74
|
+
speed_mbps: Option<f64>,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[tauri::command]
|
|
78
|
+
async fn download_file(
|
|
79
|
+
url: String,
|
|
80
|
+
window: Window,
|
|
81
|
+
) -> Result<(), String> {
|
|
82
|
+
let total_size = get_file_size(&url).await?;
|
|
83
|
+
|
|
84
|
+
for chunk in 0..total_size {
|
|
85
|
+
// Download chunk...
|
|
86
|
+
|
|
87
|
+
let progress = ProgressEvent {
|
|
88
|
+
current: chunk,
|
|
89
|
+
total: total_size,
|
|
90
|
+
percentage: (chunk as f64 / total_size as f64) * 100.0,
|
|
91
|
+
message: format!("Downloading... {}/{}", chunk, total_size),
|
|
92
|
+
speed_mbps: Some(calculate_speed()),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
window.emit("download-progress", progress)
|
|
96
|
+
.map_err(|e| e.to_string())?;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Ok(())
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Frontend**:
|
|
104
|
+
```typescript
|
|
105
|
+
interface ProgressEvent {
|
|
106
|
+
current: number;
|
|
107
|
+
total: number;
|
|
108
|
+
percentage: number;
|
|
109
|
+
message: string;
|
|
110
|
+
speed_mbps?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const unlisten = await listen<ProgressEvent>('download-progress', (event) => {
|
|
114
|
+
const { current, total, percentage, message, speed_mbps } = event.payload;
|
|
115
|
+
|
|
116
|
+
updateProgressBar(percentage);
|
|
117
|
+
updateStatus(message);
|
|
118
|
+
|
|
119
|
+
if (speed_mbps) {
|
|
120
|
+
updateSpeed(speed_mbps);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Complex Event Payloads
|
|
126
|
+
|
|
127
|
+
```rust
|
|
128
|
+
#[derive(Serialize, Clone)]
|
|
129
|
+
#[serde(tag = "type", content = "data")]
|
|
130
|
+
enum AppEvent {
|
|
131
|
+
UserLoggedIn { user_id: String, username: String },
|
|
132
|
+
UserLoggedOut { user_id: String },
|
|
133
|
+
DataSynced { items_count: usize, timestamp: String },
|
|
134
|
+
ErrorOccurred { code: String, message: String, recoverable: bool },
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[tauri::command]
|
|
138
|
+
async fn perform_login(
|
|
139
|
+
username: String,
|
|
140
|
+
password: String,
|
|
141
|
+
window: Window,
|
|
142
|
+
) -> Result<String, String> {
|
|
143
|
+
let user = authenticate(&username, &password).await?;
|
|
144
|
+
|
|
145
|
+
// Emit structured event
|
|
146
|
+
window.emit("app-event", AppEvent::UserLoggedIn {
|
|
147
|
+
user_id: user.id.clone(),
|
|
148
|
+
username: user.username.clone(),
|
|
149
|
+
}).map_err(|e| e.to_string())?;
|
|
150
|
+
|
|
151
|
+
Ok(user.id)
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Frontend**:
|
|
156
|
+
```typescript
|
|
157
|
+
type AppEvent =
|
|
158
|
+
| { type: 'UserLoggedIn'; data: { user_id: string; username: string } }
|
|
159
|
+
| { type: 'UserLoggedOut'; data: { user_id: string } }
|
|
160
|
+
| { type: 'DataSynced'; data: { items_count: number; timestamp: string } }
|
|
161
|
+
| { type: 'ErrorOccurred'; data: { code: string; message: string; recoverable: boolean } };
|
|
162
|
+
|
|
163
|
+
listen<AppEvent>('app-event', (event) => {
|
|
164
|
+
const appEvent = event.payload;
|
|
165
|
+
|
|
166
|
+
switch (appEvent.type) {
|
|
167
|
+
case 'UserLoggedIn':
|
|
168
|
+
handleLogin(appEvent.data.user_id, appEvent.data.username);
|
|
169
|
+
break;
|
|
170
|
+
case 'UserLoggedOut':
|
|
171
|
+
handleLogout(appEvent.data.user_id);
|
|
172
|
+
break;
|
|
173
|
+
case 'DataSynced':
|
|
174
|
+
showSyncSuccess(appEvent.data.items_count);
|
|
175
|
+
break;
|
|
176
|
+
case 'ErrorOccurred':
|
|
177
|
+
handleError(appEvent.data);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Streaming Data Patterns
|
|
184
|
+
|
|
185
|
+
### Real-Time Data Stream
|
|
186
|
+
|
|
187
|
+
```rust
|
|
188
|
+
#[tauri::command]
|
|
189
|
+
async fn stream_sensor_data(
|
|
190
|
+
sensor_id: String,
|
|
191
|
+
window: Window,
|
|
192
|
+
) -> Result<(), String> {
|
|
193
|
+
let mut interval = tokio::time::interval(Duration::from_millis(100));
|
|
194
|
+
|
|
195
|
+
for _ in 0..100 {
|
|
196
|
+
interval.tick().await;
|
|
197
|
+
|
|
198
|
+
let reading = read_sensor(&sensor_id).await?;
|
|
199
|
+
|
|
200
|
+
window.emit("sensor-reading", reading)
|
|
201
|
+
.map_err(|e| e.to_string())?;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
window.emit("sensor-stream-ended", sensor_id)
|
|
205
|
+
.map_err(|e| e.to_string())
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Frontend with React**:
|
|
210
|
+
```typescript
|
|
211
|
+
import { useEffect, useState } from 'react';
|
|
212
|
+
import { listen } from '@tauri-apps/api/event';
|
|
213
|
+
|
|
214
|
+
interface SensorReading {
|
|
215
|
+
value: number;
|
|
216
|
+
timestamp: number;
|
|
217
|
+
unit: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function SensorMonitor() {
|
|
221
|
+
const [readings, setReadings] = useState<SensorReading[]>([]);
|
|
222
|
+
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
let unlisten: UnlistenFn | undefined;
|
|
225
|
+
|
|
226
|
+
listen<SensorReading>('sensor-reading', (event) => {
|
|
227
|
+
setReadings(prev => [...prev.slice(-99), event.payload]);
|
|
228
|
+
}).then(fn => unlisten = fn);
|
|
229
|
+
|
|
230
|
+
return () => unlisten?.();
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<div>
|
|
235
|
+
{readings.map((r, i) => (
|
|
236
|
+
<div key={i}>{r.value} {r.unit}</div>
|
|
237
|
+
))}
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Buffered Streaming
|
|
244
|
+
|
|
245
|
+
```rust
|
|
246
|
+
#[tauri::command]
|
|
247
|
+
async fn stream_logs(
|
|
248
|
+
log_file: String,
|
|
249
|
+
window: Window,
|
|
250
|
+
) -> Result<(), String> {
|
|
251
|
+
use tokio::io::{AsyncBufReadExt, BufReader};
|
|
252
|
+
use tokio::fs::File;
|
|
253
|
+
|
|
254
|
+
let file = File::open(log_file).await
|
|
255
|
+
.map_err(|e| e.to_string())?;
|
|
256
|
+
|
|
257
|
+
let reader = BufReader::new(file);
|
|
258
|
+
let mut lines = reader.lines();
|
|
259
|
+
|
|
260
|
+
let mut buffer = Vec::new();
|
|
261
|
+
|
|
262
|
+
while let Some(line) = lines.next_line().await
|
|
263
|
+
.map_err(|e| e.to_string())? {
|
|
264
|
+
|
|
265
|
+
buffer.push(line);
|
|
266
|
+
|
|
267
|
+
// Send in batches of 10 lines
|
|
268
|
+
if buffer.len() >= 10 {
|
|
269
|
+
window.emit("log-batch", buffer.clone())
|
|
270
|
+
.map_err(|e| e.to_string())?;
|
|
271
|
+
buffer.clear();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Send remaining lines
|
|
276
|
+
if !buffer.is_empty() {
|
|
277
|
+
window.emit("log-batch", buffer)
|
|
278
|
+
.map_err(|e| e.to_string())?;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
Ok(())
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Multi-Window Communication
|
|
286
|
+
|
|
287
|
+
### Broadcasting to All Windows
|
|
288
|
+
|
|
289
|
+
```rust
|
|
290
|
+
use tauri::{AppHandle, Manager};
|
|
291
|
+
|
|
292
|
+
#[tauri::command]
|
|
293
|
+
async fn broadcast_message(
|
|
294
|
+
message: String,
|
|
295
|
+
app: AppHandle,
|
|
296
|
+
) -> Result<(), String> {
|
|
297
|
+
// Emit to ALL windows
|
|
298
|
+
app.emit_all("broadcast", message)
|
|
299
|
+
.map_err(|e| e.to_string())
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Targeted Window Messaging
|
|
304
|
+
|
|
305
|
+
```rust
|
|
306
|
+
#[tauri::command]
|
|
307
|
+
async fn send_to_window(
|
|
308
|
+
target_window: String,
|
|
309
|
+
message: String,
|
|
310
|
+
app: AppHandle,
|
|
311
|
+
) -> Result<(), String> {
|
|
312
|
+
// Get specific window
|
|
313
|
+
if let Some(window) = app.get_window(&target_window) {
|
|
314
|
+
window.emit("private-message", message)
|
|
315
|
+
.map_err(|e| e.to_string())?;
|
|
316
|
+
Ok(())
|
|
317
|
+
} else {
|
|
318
|
+
Err(format!("Window '{}' not found", target_window))
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Window-to-Window via Backend
|
|
324
|
+
|
|
325
|
+
**Window A (sender)**:
|
|
326
|
+
```typescript
|
|
327
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
328
|
+
|
|
329
|
+
async function sendToSettings(data: any) {
|
|
330
|
+
await invoke('relay_to_settings', { data });
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Backend relay**:
|
|
335
|
+
```rust
|
|
336
|
+
#[tauri::command]
|
|
337
|
+
async fn relay_to_settings(
|
|
338
|
+
data: serde_json::Value,
|
|
339
|
+
app: AppHandle,
|
|
340
|
+
) -> Result<(), String> {
|
|
341
|
+
if let Some(settings_window) = app.get_window("settings") {
|
|
342
|
+
settings_window.emit("data-update", data)
|
|
343
|
+
.map_err(|e| e.to_string())?;
|
|
344
|
+
}
|
|
345
|
+
Ok(())
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Window B (receiver - settings)**:
|
|
350
|
+
```typescript
|
|
351
|
+
import { listen } from '@tauri-apps/api/event';
|
|
352
|
+
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
let unlisten: UnlistenFn | undefined;
|
|
355
|
+
|
|
356
|
+
listen('data-update', (event) => {
|
|
357
|
+
console.log('Received from main window:', event.payload);
|
|
358
|
+
updateSettings(event.payload);
|
|
359
|
+
}).then(fn => unlisten = fn);
|
|
360
|
+
|
|
361
|
+
return () => unlisten?.();
|
|
362
|
+
}, []);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Frontend → Backend Events
|
|
366
|
+
|
|
367
|
+
### Custom Frontend Events
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
import { emit } from '@tauri-apps/api/event';
|
|
371
|
+
|
|
372
|
+
// Frontend emits event
|
|
373
|
+
await emit('user-action', {
|
|
374
|
+
action: 'button-click',
|
|
375
|
+
button_id: 'save-button',
|
|
376
|
+
timestamp: Date.now()
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Backend listener**:
|
|
381
|
+
```rust
|
|
382
|
+
use tauri::{Manager, Listener};
|
|
383
|
+
|
|
384
|
+
fn main() {
|
|
385
|
+
tauri::Builder::default()
|
|
386
|
+
.setup(|app| {
|
|
387
|
+
let app_handle = app.handle();
|
|
388
|
+
|
|
389
|
+
// Listen for frontend events
|
|
390
|
+
app_handle.listen_global("user-action", move |event| {
|
|
391
|
+
if let Some(payload) = event.payload() {
|
|
392
|
+
println!("User action: {}", payload);
|
|
393
|
+
// Process event...
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
Ok(())
|
|
398
|
+
})
|
|
399
|
+
.run(tauri::generate_context!())
|
|
400
|
+
.expect("error while running tauri application");
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Advanced Listener Management
|
|
405
|
+
|
|
406
|
+
### React Hook for Events
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { useEffect, useState } from 'react';
|
|
410
|
+
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
411
|
+
|
|
412
|
+
function useEvent<T>(eventName: string): T | null {
|
|
413
|
+
const [payload, setPayload] = useState<T | null>(null);
|
|
414
|
+
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
let unlisten: UnlistenFn | undefined;
|
|
417
|
+
|
|
418
|
+
listen<T>(eventName, (event) => {
|
|
419
|
+
setPayload(event.payload);
|
|
420
|
+
}).then(fn => unlisten = fn);
|
|
421
|
+
|
|
422
|
+
return () => unlisten?.();
|
|
423
|
+
}, [eventName]);
|
|
424
|
+
|
|
425
|
+
return payload;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Usage
|
|
429
|
+
function ProgressDisplay() {
|
|
430
|
+
const progress = useEvent<ProgressEvent>('download-progress');
|
|
431
|
+
|
|
432
|
+
if (!progress) return null;
|
|
433
|
+
|
|
434
|
+
return (
|
|
435
|
+
<div>
|
|
436
|
+
Progress: {progress.percentage.toFixed(2)}%
|
|
437
|
+
</div>
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Event Queue Pattern
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { listen } from '@tauri-apps/api/event';
|
|
446
|
+
|
|
447
|
+
class EventQueue<T> {
|
|
448
|
+
private queue: T[] = [];
|
|
449
|
+
private unlisten?: UnlistenFn;
|
|
450
|
+
|
|
451
|
+
async start(eventName: string) {
|
|
452
|
+
this.unlisten = await listen<T>(eventName, (event) => {
|
|
453
|
+
this.queue.push(event.payload);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
dequeue(): T | undefined {
|
|
458
|
+
return this.queue.shift();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
clear() {
|
|
462
|
+
this.queue = [];
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
stop() {
|
|
466
|
+
this.unlisten?.();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
get length() {
|
|
470
|
+
return this.queue.length;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Usage
|
|
475
|
+
const progressQueue = new EventQueue<ProgressEvent>();
|
|
476
|
+
await progressQueue.start('download-progress');
|
|
477
|
+
|
|
478
|
+
// Process queue periodically
|
|
479
|
+
setInterval(() => {
|
|
480
|
+
while (progressQueue.length > 0) {
|
|
481
|
+
const event = progressQueue.dequeue();
|
|
482
|
+
processProgress(event);
|
|
483
|
+
}
|
|
484
|
+
}, 100);
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### One-Time Events
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import { once } from '@tauri-apps/api/event';
|
|
491
|
+
|
|
492
|
+
// Listen for event only once
|
|
493
|
+
await once<string>('initialization-complete', (event) => {
|
|
494
|
+
console.log('App initialized:', event.payload);
|
|
495
|
+
startApp();
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Error Handling in Events
|
|
500
|
+
|
|
501
|
+
### Safe Event Emission
|
|
502
|
+
|
|
503
|
+
```rust
|
|
504
|
+
async fn emit_safe(window: &Window, event: &str, payload: impl Serialize) -> Result<(), String> {
|
|
505
|
+
window.emit(event, payload)
|
|
506
|
+
.map_err(|e| {
|
|
507
|
+
eprintln!("Failed to emit event '{}': {}", event, e);
|
|
508
|
+
e.to_string()
|
|
509
|
+
})
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#[tauri::command]
|
|
513
|
+
async fn process_with_events(
|
|
514
|
+
window: Window,
|
|
515
|
+
) -> Result<(), String> {
|
|
516
|
+
emit_safe(&window, "processing-started", "Starting...")
|
|
517
|
+
.await?;
|
|
518
|
+
|
|
519
|
+
// Process...
|
|
520
|
+
|
|
521
|
+
emit_safe(&window, "processing-complete", "Done!")
|
|
522
|
+
.await?;
|
|
523
|
+
|
|
524
|
+
Ok(())
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Performance Considerations
|
|
529
|
+
|
|
530
|
+
### Throttling Events
|
|
531
|
+
|
|
532
|
+
```rust
|
|
533
|
+
use std::time::{Duration, Instant};
|
|
534
|
+
|
|
535
|
+
#[tauri::command]
|
|
536
|
+
async fn high_frequency_updates(
|
|
537
|
+
window: Window,
|
|
538
|
+
) -> Result<(), String> {
|
|
539
|
+
let mut last_emit = Instant::now();
|
|
540
|
+
let throttle_duration = Duration::from_millis(100);
|
|
541
|
+
|
|
542
|
+
for i in 0..10000 {
|
|
543
|
+
let value = compute_value(i);
|
|
544
|
+
|
|
545
|
+
// Only emit every 100ms
|
|
546
|
+
if last_emit.elapsed() >= throttle_duration {
|
|
547
|
+
window.emit("update", value)
|
|
548
|
+
.map_err(|e| e.to_string())?;
|
|
549
|
+
last_emit = Instant::now();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
Ok(())
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Batching Events
|
|
558
|
+
|
|
559
|
+
```rust
|
|
560
|
+
#[tauri::command]
|
|
561
|
+
async fn batch_updates(
|
|
562
|
+
window: Window,
|
|
563
|
+
) -> Result<(), String> {
|
|
564
|
+
let mut batch = Vec::new();
|
|
565
|
+
|
|
566
|
+
for item in process_items() {
|
|
567
|
+
batch.push(item);
|
|
568
|
+
|
|
569
|
+
// Emit in batches of 50
|
|
570
|
+
if batch.len() >= 50 {
|
|
571
|
+
window.emit("batch-update", batch.clone())
|
|
572
|
+
.map_err(|e| e.to_string())?;
|
|
573
|
+
batch.clear();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Emit remaining items
|
|
578
|
+
if !batch.is_empty() {
|
|
579
|
+
window.emit("batch-update", batch)
|
|
580
|
+
.map_err(|e| e.to_string())?;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
Ok(())
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
## Best Practices
|
|
588
|
+
|
|
589
|
+
1. **Always clean up listeners** - Use `unlisten()` to prevent memory leaks
|
|
590
|
+
2. **Type event payloads** - Define interfaces for type safety
|
|
591
|
+
3. **Use structured events** - Tagged unions for multiple event types
|
|
592
|
+
4. **Throttle high-frequency events** - Prevent overwhelming frontend
|
|
593
|
+
5. **Batch when possible** - Reduce serialization overhead
|
|
594
|
+
6. **Handle errors gracefully** - Log failed emissions, don't crash
|
|
595
|
+
7. **Use once() for one-time events** - Initialization, completion signals
|
|
596
|
+
8. **Namespace event names** - Use prefixes like "download:", "user:", "system:"
|
|
597
|
+
|
|
598
|
+
## Common Pitfalls
|
|
599
|
+
|
|
600
|
+
❌ **Forgetting to unlisten**:
|
|
601
|
+
```typescript
|
|
602
|
+
// WRONG - memory leak
|
|
603
|
+
function Component() {
|
|
604
|
+
listen('my-event', handler); // Never cleaned up!
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// CORRECT
|
|
608
|
+
function Component() {
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
let unlisten: UnlistenFn | undefined;
|
|
611
|
+
listen('my-event', handler).then(fn => unlisten = fn);
|
|
612
|
+
return () => unlisten?.();
|
|
613
|
+
}, []);
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
❌ **Not handling serialization errors**:
|
|
618
|
+
```rust
|
|
619
|
+
// WRONG - struct can't serialize
|
|
620
|
+
#[derive(Clone)] // Missing Serialize!
|
|
621
|
+
struct Event { }
|
|
622
|
+
|
|
623
|
+
window.emit("event", Event {}); // Runtime error!
|
|
624
|
+
|
|
625
|
+
// CORRECT
|
|
626
|
+
#[derive(Serialize, Clone)]
|
|
627
|
+
struct Event { }
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
❌ **Emitting too frequently**:
|
|
631
|
+
```rust
|
|
632
|
+
// WRONG - 10000 events in quick succession
|
|
633
|
+
for i in 0..10000 {
|
|
634
|
+
window.emit("update", i); // Overwhelming!
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// CORRECT - throttle or batch
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Summary
|
|
641
|
+
|
|
642
|
+
- **Events are async** - Backend → Frontend communication
|
|
643
|
+
- **Always type payloads** - Use serde::Serialize + TypeScript interfaces
|
|
644
|
+
- **Clean up listeners** - Call `unlisten()` in cleanup
|
|
645
|
+
- **Throttle/batch** - High-frequency events need rate limiting
|
|
646
|
+
- **Use structured payloads** - Tagged unions for multiple event types
|
|
647
|
+
- **Window targeting** - `emit()` for specific, `emit_all()` for broadcast
|
|
648
|
+
- **Frontend events** - Use `emit()` from frontend, listen in backend setup
|