Agent SkillsTangleML/tangle-ui › analytics-tracking

analytics-tracking

GitHub

提供 tangle-ui 中分析事件追踪的最佳实践,包括使用 tracking 辅助函数、命名规范及元数据传递方式。

.claude/skills/analytics-tracking/SKILL.md TangleML/tangle-ui

Trigger Scenarios

添加新的分析追踪事件 定义 action_type 名称 配置分析提供商相关逻辑

Install

npx skills add TangleML/tangle-ui --skill analytics-tracking -g -y
More Options

Non-standard path

npx skills add https://github.com/TangleML/tangle-ui/tree/master/.claude/skills/analytics-tracking -g -y

Use without installing

npx skills use TangleML/tangle-ui@analytics-tracking

指定 Agent (Claude Code)

npx skills add TangleML/tangle-ui --skill analytics-tracking -a claude-code -g -y

安装 repo 全部 skill

npx skills add TangleML/tangle-ui --all -g -y

预览 repo 内 skill

npx skills add TangleML/tangle-ui --list

SKILL.md

Frontmatter
{
    "name": "analytics-tracking",
    "description": "How to track analytics events in tangle-ui and naming conventions for action_type values. Use when adding a new tracked event, naming an action_type, or working with the analytics provider."
}

Analytics Tracking

Tracking click events (preferred approach)

For click events on interactive elements (<button>, <a>, <summary>, or elements with role="button" / role="link"), use the tracking() helper to attach data attributes. A document-level click listener (useClickTracking) automatically fires the analytics event — no manual track() call needed.

import { tracking } from "@/utils/tracking";

<Button onClick={handleSave} {...tracking("pipeline_editor.save_pipeline")}>
  Save
</Button>;

The listener appends .click to the identifier automatically, so "pipeline_editor.save_pipeline" fires as "pipeline_editor.save_pipeline.click".

With metadata

Pass a second argument for event-specific properties. The metadata is serialized as a data-tracking-metadata JSON attribute on the DOM element.

<Button
  onClick={() => handleLayout(algo)}
  {...tracking("pipeline_canvas.tool_bar.auto_layout_select", {
    selected_layout: algo,
    page_type: "pipeline_editor",
  })}
>
  {algo}
</Button>

Prop drilling for click tracking

When a child component renders the interactive element but the parent owns the tracking context, prefer prop drilling or forwarding rest props so data-tracking-id reaches the DOM element. This is intentional — it keeps tracking declarative and avoids manual track() calls scattered across the codebase.

The parent passes the tracking attributes, and ActionButton forwards its rest props down the <TooltipButton><Button> → DOM chain:

<ActionButton
  tooltip="Export Pipeline"
  icon="FileDown"
  onClick={handleExport}
  {...tracking("pipeline_editor.pipeline_actions.export_pipeline")}
/>;

export const ActionButton = ({
  tooltip,
  onClick,
  icon,
  ...rest
}: ActionButtonProps) => (
  <TooltipButton onClick={onClick} tooltip={tooltip} {...rest}>
    <Icon name={icon} />
  </TooltipButton>
);

For components that wrap interactive elements (e.g. Radix asChild patterns), spread tracking() on the wrapper — Radix merges props onto the child:

<DialogTrigger
  asChild
  {...tracking("pipeline_editor.task_node.component_info")}
>
  <InfoIconButton />
</DialogTrigger>

Dynamic metadata at render time

When metadata depends on component state that is known at render time, compute it inline. The data attribute updates on each render:

<Button
  onClick={() => toggleFavorite()}
  {...(analyticsActionType
    ? tracking(analyticsActionType, { new_value: !active })
    : {})}
>

When to use manual track() instead

Use useAnalytics and call track() directly only when the document-level click listener cannot handle the scenario:

Scenario Why manual Example
Outcome events Fire after an async operation succeeds, not on click track("pipeline_editor.pipeline_actions.save_pipeline_as_completed")
Impression events Fire when something becomes visible (dialog, panel), not from a click track("pipeline_editor.name_pipeline_dialog_impression")
Debounced events Fire after a delay (e.g. text field editing) debouncedTrack() using debounce from @/utils/debounce
Toggle state Metadata includes the new value from an onChange callback argument, not known at render time track("settings.toggle_changed", { new_value: checked })
Non-interactive elements The click target is not a <button>, <a>, <summary>, or role="button"/role="link" React Flow's internal Controls callbacks (onZoomIn, etc.)
import { useAnalytics } from "@/providers/AnalyticsProvider";

const { track } = useAnalytics();

const handleSave = async (name: string) => {
  await savePipeline(name);
  track("pipeline_editor.pipeline_actions.save_pipeline_as_completed");
};

useEffect(() => {
  if (open) {
    track("component_editor.save.already_exists_impression");
  }
}, [open]);

The first is an outcome event — it fires after the save succeeds, not on click. The second is an impression event, firing when the dialog opens.

Event detail shape

The dispatched CustomEvent carries the following detail fields:

Field Type Description
actionType string The action type string (with .click appended for click events)
metadata Record<string,unknown> Optional metadata object
sessionId string Anonymous tab session ID
route string window.location.pathname at time of call
appVersion string | undefined VITE_GIT_COMMIT build variable
environment string | undefined VITE_TANGLE_ENV build variable

action_type naming convention

Use dot-separated, snake_case segments:

<feature_area>.<entity>[.<sub_entity>].<action_verb>

For tracking() data attributes, omit the action verb — the listener appends .click:

<feature_area>.<entity>[.<sub_entity>]

For manual track() calls, include the full action verb:

<feature_area>.<entity>[.<sub_entity>].<action_verb>

Action verb reference

Verb When to use
click User explicitly clicked or tapped a button/link (auto-appended by click listener)
impression Something became visible for the first time in a session (dialog opened, panel shown)
completed An async operation finished successfully
toggle User toggled a switch or checkbox

Rules

  • All segments are snake_case — no camelCase, no hyphens.
  • The last segment must always be an action verb for manual track() calls.
  • For tracking() helper calls, the last segment is the entity — .click is appended automatically.
  • Keep hierarchy shallow — prefer pipeline.component over pipeline.canvas.node.component.
  • Do not embed counts, IDs, or dynamic values in the action_type string. Put them in metadata instead.

Examples

# tracking() helper (click listener appends .click)
tracking("header.settings")                                    → header.settings.click
tracking("pipeline_editor.task_node.z_index", { action: "move_forward" })
                                                               → pipeline_editor.task_node.z_index.click

# Manual track() calls
track("pipeline_editor.pipeline_actions.save_pipeline_as_completed")    ✓  outcome
track("pipeline_editor.name_pipeline_dialog_impression")                ✓  impression
track("settings.toggle_changed", { flag_name: "dashboard" })           ✓  toggle
track("session.tab.start", { flags: { ... } })                         ✓  lifecycle

# Bad
track("header.settings_click")                    ✗  use tracking() helper instead
track("pipeline.run.submit.clicked")              ✗  "clicked" is redundant

Metadata

Pass an object as the second argument for event-specific properties. Keys should be snake_case. Values must never contain PII (no emails, names, user IDs, or free-form user input).

tracking("pipeline_canvas.tool_bar.auto_layout_select", {
  selected_layout: "sugiyama",
  page_type: "pipeline_editor",
});

track("settings.secrets.secret_mutated", { action: "created" });

Component events

Two namespaces are reserved for the Component Marketplace telemetry baseline:

  • pipeline_editor.component.* — canvas interactions for a specific component instance (drop, replace, remove, upgrade, duplicate).
  • component_library.* — discovery and library CRUD (search, row click, add, remove, future publish/deprecate).

Standard metadata for component events

Spread componentMetadata(ref, source) from @/utils/componentTracking so every event carries the same identity fields:

import { componentMetadata } from "@/utils/componentTracking";

track("pipeline_editor.component.dropped", {
  ...componentMetadata(componentRef, "library"),
  drop_kind: "new_node",
  pipeline_id: pipelineId,
});
Key Type Notes
component_id string | undefined Content-addressed digest. Stable across renames.
component_name string | undefined Human-readable name.
component_source "user" | "library" | "published" | "url" | "file" | "unknown" Where the component came from.

Library mutation events fire from the provider

component_library.added and component_library.removed are emitted from inside ComponentLibraryProvider — callers do not call track() for them. Instead, pass an entryPoint string so the provider can attribute which UI surface triggered the mutation:

await addToComponentLibrary(hydratedComponent, "favorite_button");
await removeFromComponentLibrary(component, "favorite_button");

Valid entryPoint values: "favorite_button", "canvas_file_drop", "canvas_file_drop_v2", "import_dialog", "editor_save", "unknown".

Reserved names (do not use yet)

These are documented so future PRs don't collide with the marketplace taxonomy:

  • component_library.published, component_library.deprecated, component_library.superseded
  • component_library.shared_link.copied
  • component_collection.created, component_collection.viewed, component_collection.member_added
  • component_library.search.result.semantic_match

Version History

  • d7768e8 Current 2026-09-02 20:59

Same Skill Collection

.claude/skills/accessibility/SKILL.md
.claude/skills/address-pr-comments/SKILL.md
.claude/skills/audit-tickets/SKILL.md
.claude/skills/docs-update/SKILL.md
.claude/skills/e2e-testing/SKILL.md
.claude/skills/list-skills/SKILL.md
.claude/skills/open-source/SKILL.md
.claude/skills/project-conventions/SKILL.md
.claude/skills/react-patterns/SKILL.md
.claude/skills/review/SKILL.md
.claude/skills/tangle-domain/SKILL.md
.claude/skills/tanstack-query/SKILL.md
.claude/skills/tanstack-router/SKILL.md
.claude/skills/typescript-standards/SKILL.md
.claude/skills/ui-primitives/SKILL.md
.claude/skills/validate/SKILL.md
.claude/skills/vitest-testing/SKILL.md
.cursor/skills/playwright-testing/SKILL.md
public/agent-skills/componentYamlFormat/SKILL.md
public/agent-skills/tangleBestPractices/SKILL.md
.claude/skills/gardening/SKILL.md

Metadata

Files
0
Version
d7768e8
Hash
4797c225
Indexed
2026-09-02 20:59

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-03 03:45
浙ICP备14020137号-1 $bản đồ khách truy cập$