@wangs-ui/create-react-app 1.0.35 → 1.0.37

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.
package/dist/bin.js CHANGED
@@ -1122,18 +1122,73 @@ function copyProjectTemplate(options) {
1122
1122
  }
1123
1123
  }
1124
1124
  //#endregion
1125
- //#region src/utils/mcpInstaller.ts
1126
- function copyDirRecursive(srcDir, destDir) {
1127
- if (!fs.existsSync(srcDir)) return;
1128
- fs.mkdirSync(destDir, { recursive: true });
1129
- const entries = fs.readdirSync(srcDir, { withFileTypes: true });
1130
- for (const entry of entries) {
1131
- const srcPath = path.join(srcDir, entry.name);
1132
- const destPath = path.join(destDir, entry.name);
1133
- if (entry.isDirectory()) copyDirRecursive(srcPath, destPath);
1134
- else fs.copyFileSync(srcPath, destPath);
1125
+ //#region ../skills/dist/src-DZa2DBdf.js
1126
+ var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
1127
+ function getSkillsDir() {
1128
+ const candidates = [
1129
+ path.resolve(__dirname$1, "skills"),
1130
+ path.resolve(__dirname$1, "../skills"),
1131
+ path.resolve(__dirname$1, "../../skills")
1132
+ ];
1133
+ for (const c of candidates) if (fs.existsSync(c)) return c;
1134
+ return path.resolve(__dirname$1, "skills");
1135
+ }
1136
+ function loadAllSkills() {
1137
+ const skillsDir = getSkillsDir();
1138
+ if (!fs.existsSync(skillsDir)) return [];
1139
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
1140
+ const skills = [];
1141
+ for (const entry of entries) if (entry.isDirectory()) {
1142
+ const skillMdPath = path.join(skillsDir, entry.name, "SKILL.md");
1143
+ if (fs.existsSync(skillMdPath)) {
1144
+ const content = fs.readFileSync(skillMdPath, "utf-8");
1145
+ const { name: entryName } = entry;
1146
+ let name = entryName;
1147
+ let description = "Wangs UI consumer skill";
1148
+ const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
1149
+ if (frontmatterMatch) {
1150
+ const fm = frontmatterMatch[1];
1151
+ const nameMatch = fm.match(/^name:\s*(.+)$/m);
1152
+ const descMatch = fm.match(/^description:\s*(.+)$/m);
1153
+ if (nameMatch) name = nameMatch[1].trim();
1154
+ if (descMatch) description = descMatch[1].trim();
1155
+ }
1156
+ skills.push({
1157
+ id: entry.name,
1158
+ name,
1159
+ description,
1160
+ content
1161
+ });
1162
+ }
1163
+ }
1164
+ return skills;
1165
+ }
1166
+ function getAgentSkillDirs(baseDir = process.cwd()) {
1167
+ const dirs = [];
1168
+ const candidates = [
1169
+ path.join(baseDir, ".agents", "skills"),
1170
+ path.join(baseDir, ".claude", "skills"),
1171
+ path.join(baseDir, ".opencode", "skills"),
1172
+ path.join(baseDir, ".kilo", "skills")
1173
+ ];
1174
+ for (const c of candidates) if (fs.existsSync(c) || fs.existsSync(path.dirname(c))) dirs.push(c);
1175
+ if (dirs.length === 0) dirs.push(path.join(baseDir, ".agents", "skills"));
1176
+ return dirs;
1177
+ }
1178
+ function installSkill(skill, baseDir = process.cwd()) {
1179
+ const targetDirs = getAgentSkillDirs(baseDir);
1180
+ const writtenPaths = [];
1181
+ for (const baseSkillDir of targetDirs) {
1182
+ const destDir = path.join(baseSkillDir, skill.id);
1183
+ fs.mkdirSync(destDir, { recursive: true });
1184
+ const destFile = path.join(destDir, "SKILL.md");
1185
+ fs.writeFileSync(destFile, skill.content, "utf-8");
1186
+ writtenPaths.push(destFile);
1135
1187
  }
1188
+ return writtenPaths;
1136
1189
  }
1190
+ //#endregion
1191
+ //#region src/utils/mcpInstaller.ts
1137
1192
  function setupAgentConfigurations(options) {
1138
1193
  const { targetDir, templateDir, agents } = options;
1139
1194
  const configuredAgents = [];
@@ -1173,13 +1228,14 @@ function setupAgentConfigurations(options) {
1173
1228
  }
1174
1229
  if (agents.includes("antigravity")) {
1175
1230
  const agentsMcpSrc = path.join(agentsTemplateDir, "agents.mcp.json");
1176
- const skillsSrc = path.join(agentsTemplateDir, "skills");
1177
1231
  const dotAgents = path.join(targetDir, ".agents");
1178
1232
  fs.mkdirSync(dotAgents, { recursive: true });
1179
1233
  if (fs.existsSync(agentsMcpSrc)) fs.copyFileSync(agentsMcpSrc, path.join(dotAgents, "mcp_config.json"));
1180
- if (fs.existsSync(skillsSrc)) copyDirRecursive(skillsSrc, path.join(dotAgents, "skills"));
1181
- configuredAgents.push("Antigravity IDE & CLI (.agents/mcp_config.json, .agents/skills)");
1234
+ configuredAgents.push("Antigravity IDE & CLI (.agents/mcp_config.json)");
1182
1235
  }
1236
+ const allSkills = loadAllSkills();
1237
+ for (const skill of allSkills) installSkill(skill, targetDir);
1238
+ if (allSkills.length > 0) configuredAgents.push(`Wangs UI Consumer Skills (${allSkills.map((s) => s.id).join(", ")})`);
1183
1239
  return configuredAgents;
1184
1240
  }
1185
1241
  //#endregion
@@ -1261,7 +1317,7 @@ async function promptUser(initialOptions) {
1261
1317
  "kilo"
1262
1318
  ]
1263
1319
  };
1264
- intro("\x1B[36m🚀 Wangs UI React App Scaffolder\x1B[0m");
1320
+ intro(`\x1b[36m🚀 Wangs UI React App Scaffolder\x1b[0m (v1.0.37)`);
1265
1321
  if (!projectName) {
1266
1322
  const nameResponse = await text({
1267
1323
  message: "What is your project name?",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wangs-ui/create-react-app",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "Scaffold a modern React app with Wangs UI, Vite 8, Oxlint, Oxfmt, and AI Agent MCP integration",
5
5
  "keywords": [
6
6
  "antigravity",
@@ -41,16 +41,19 @@
41
41
  "template/_gitignore",
42
42
  "template/_npmrc",
43
43
  "template/src",
44
- "template/agents"
44
+ "template/agents",
45
+ "template/README.md"
45
46
  ],
46
47
  "type": "module",
47
48
  "publishConfig": {
48
49
  "access": "public",
49
50
  "registry": "https://registry.npmjs.org/"
50
51
  },
51
- "devDependencies": {
52
- "@clack/prompts": "^1.7.0"
52
+ "dependencies": {
53
+ "@clack/prompts": "^1.7.0",
54
+ "@wangs-ui/skills": "1.0.37"
53
55
  },
56
+ "devDependencies": {},
54
57
  "scripts": {
55
58
  "build": "vite build",
56
59
  "check:publint": "publint",
@@ -0,0 +1,114 @@
1
+ # Wangs UI React Application
2
+
3
+ A modern enterprise React application built with **Wangs UI Design System**, Vite 8, Tailwind CSS v4, Oxlint, and pre-configured AI Agent MCP workflows.
4
+
5
+ ---
6
+
7
+ ## 🚀 Getting Started
8
+
9
+ ### 1. Install Dependencies
10
+
11
+ ```bash
12
+ pnpm install
13
+ ```
14
+
15
+ ### 2. Available Scripts
16
+
17
+ | Command | Description |
18
+ | :------------- | :------------------------------------------ |
19
+ | `pnpm dev` | Start the local development server with HMR |
20
+ | `pnpm build` | Build optimized production bundle |
21
+ | `pnpm preview` | Preview production build locally |
22
+ | `pnpm lint` | Lint codebase with fast Oxlint |
23
+ | `pnpm fmt` | Format codebase with Oxfmt |
24
+
25
+ ---
26
+
27
+ ## 🎨 Working with Wangs UI
28
+
29
+ ### 1. Modular Subpath Imports
30
+
31
+ Always import primitive components and icons via their subpaths to ensure optimal tree-shaking:
32
+
33
+ ```tsx
34
+ // Primitives
35
+ import Button from '@wangs-ui/react-core/primitive/button';
36
+ import Card from '@wangs-ui/react-core/primitive/card';
37
+ import InputText from '@wangs-ui/react-core/primitive/inputtext';
38
+ import DataTable, { Column } from '@wangs-ui/react-core/primitive/datatable';
39
+
40
+ // Icons
41
+ import { SearchLine, AddLine, DeleteBin6Line } from '@wangs-ui/react-icons';
42
+
43
+ // Form & Internationalization
44
+ import { Form, Field } from '@wangs-ui/react-core';
45
+ import { useFormControl } from '@wangs-ui/form';
46
+ import { useI18n } from '@wangs-ui/react-i18n';
47
+ ```
48
+
49
+ ### 2. Strict Primitive Rule
50
+
51
+ Never write unstyled raw HTML elements (`<button>`, `<input>`, `<table>`, `<dialog>`). Always use the corresponding Wangs UI primitive component.
52
+
53
+ ### 3. Internationalization (`useI18n`)
54
+
55
+ Never hardcode user-facing text strings in JSX. Always wrap UI labels, button text, table headers, and error messages in `t()`:
56
+
57
+ ```tsx
58
+ const { t } = useI18n();
59
+
60
+ return <Button label={t('Save Changes')} severity="primary" />;
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 🤖 AI Agent & MCP Integration
66
+
67
+ This project comes pre-configured with **`@wangs-ui/mcp`** for leading AI agents:
68
+
69
+ - **Antigravity IDE & CLI**: `.agents/mcp_config.json`
70
+ - **Claude Code**: `.mcp.json` & `CLAUDE.md`
71
+ - **OpenCode**: `opencode.json` & `.opencode/mcp.json`
72
+ - **Kilo**: `.kilo/mcp.json` & `.vscode/mcp.json`
73
+
74
+ The MCP server provides live access to:
75
+
76
+ 1. **Component Documentation & Types**: `get-documentation({ id: "button" })`
77
+ 2. **Component Stories & Variants**: `get-documentation-for-story({ id: "button", storyName: "Primary" })`
78
+ 3. **Knowledge Graph Queries**: `query_graph({ query: "DataTable" })`
79
+
80
+ ---
81
+
82
+ ## 📦 Managing AI Agent Skills (`@wangs-ui/skills`)
83
+
84
+ Skills provide high-level workflows and recipes for AI agents (form management, data grids, modal dialogs, i18n formatting). You can install, update, or remove skills independently using the CLI:
85
+
86
+ ### List Available Skills
87
+
88
+ ```bash
89
+ npx @wangs-ui/skills list
90
+ ```
91
+
92
+ ### Install Specific Skills
93
+
94
+ ```bash
95
+ # Interactive selection
96
+ npx @wangs-ui/skills add
97
+
98
+ # Direct install
99
+ npx @wangs-ui/skills add create-form data-table dialog-modal
100
+ ```
101
+
102
+ ### Update Installed Skills
103
+
104
+ To update all skills in your project to the latest definitions:
105
+
106
+ ```bash
107
+ npx @wangs-ui/skills update
108
+ ```
109
+
110
+ ### Remove a Skill
111
+
112
+ ```bash
113
+ npx @wangs-ui/skills remove create-form
114
+ ```
@@ -13,10 +13,10 @@
13
13
  "format:check": "oxfmt --check"
14
14
  },
15
15
  "dependencies": {
16
- "@wangs-ui/react-core": "^1.0.35",
17
- "@wangs-ui/react-i18n": "^1.0.35",
18
- "@wangs-ui/react-icons": "^1.0.35",
19
- "@wangs-ui/react-presets": "^1.0.35",
16
+ "@wangs-ui/react-core": "^1.0.37",
17
+ "@wangs-ui/react-i18n": "^1.0.37",
18
+ "@wangs-ui/react-icons": "^1.0.37",
19
+ "@wangs-ui/react-presets": "^1.0.37",
20
20
  "clsx": "^2.1.1",
21
21
  "react": "^19.2.7",
22
22
  "react-dom": "^19.2.7",
@@ -1,95 +0,0 @@
1
- ---
2
- name: form-management
3
- description: Guidelines and best practices for creating, validating, and submitting forms using Wangs UI Form and Field components.
4
- ---
5
-
6
- # Skill: Form Management & Validation Guidelines
7
-
8
- This skill defines the standards for form implementation with `@wangs-ui/form`, `useFormControl`, and `@wangs-ui/react-core` `Form` / `Field` components.
9
-
10
- ---
11
-
12
- ## 1. Core Architecture
13
-
14
- Wangs UI provides strongly typed form controllers:
15
-
16
- - `useFormControl({ type: 'json' })`: For standard JSON payload forms.
17
- - `useFormControl({ type: 'formdata' })`: For multipart forms (file uploads).
18
-
19
- ---
20
-
21
- ## 2. Standard Form Pattern
22
-
23
- ```tsx
24
- import React, { useState } from 'react';
25
- import { Button, Card, Form, Field, InputText, InputPassword } from '@wangs-ui/react-core';
26
- import { useFormControl } from '@wangs-ui/form';
27
- import { useI18n } from '@wangs-ui/react-i18n';
28
-
29
- interface LoginFormValues {
30
- email: string;
31
- password: string;
32
- }
33
-
34
- export default function LoginForm(): React.ReactElement {
35
- const { t } = useI18n();
36
- const formControl = useFormControl<LoginFormValues>({ type: 'json' });
37
- const [loading, setLoading] = useState(false);
38
-
39
- const handleSubmit = async (values: LoginFormValues) => {
40
- setLoading(true);
41
- try {
42
- console.log('Submitted values:', values);
43
- } finally {
44
- setLoading(false);
45
- }
46
- };
47
-
48
- return (
49
- <Card className="w-full max-w-md p-6">
50
- <Form control={formControl} onSubmit={handleSubmit} className="flex flex-col gap-4">
51
- <h2 className="heading-2">{t('Sign In')}</h2>
52
-
53
- <Field<string>
54
- name="email"
55
- label={t('Email Address')}
56
- required
57
- rules={{
58
- pattern: {
59
- value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
60
- message: t('Invalid email address'),
61
- },
62
- }}
63
- >
64
- {(field) => (
65
- <InputText {...field} placeholder={t('Enter your email')} value={field.value || ''} />
66
- )}
67
- </Field>
68
-
69
- <Field<string> name="password" label={t('Password')} required>
70
- {(field) => (
71
- <InputPassword
72
- {...field}
73
- placeholder={t('Enter your password')}
74
- value={field.value || ''}
75
- />
76
- )}
77
- </Field>
78
-
79
- <Button type="submit" label={t('Submit')} severity="primary" loading={loading} />
80
- </Form>
81
- </Card>
82
- );
83
- }
84
- ```
85
-
86
- ---
87
-
88
- ## 3. Mandatory Rules
89
-
90
- 1. **Always Use `<Form>` and `<Field>`**:
91
- Never use raw HTML `<form>` or uncontrolled `<input>`.
92
- 2. **Translate Labels and Error Messages**:
93
- Always wrap `label`, `placeholder`, and validation `message` in `t()`.
94
- 3. **Strongly Typed Models**:
95
- Define a TypeScript interface for form values and pass it to `useFormControl<Model>()`.
@@ -1,104 +0,0 @@
1
- ---
2
- name: i18n-standardization
3
- description: Guidelines and rules for implementing and using the JIT i18n translation functions (t) and locale formatters in this Wangs UI project.
4
- ---
5
-
6
- # Skill: JIT i18n Implementation & Usage Guidelines
7
-
8
- This skill defines the coding standards, rules, and best practices for writing translation strings and formatting dates/numbers using the JIT i18n system.
9
-
10
- ---
11
-
12
- ## 1. Core Principles
13
-
14
- The localization system uses a **Just-In-Time (JIT)** workflow. All user-facing English strings act directly as keys. The `t()` function resolves these keys against local memory, the network cache, or triggers a real-time background backend AI translation without blocking UI rendering.
15
-
16
- ---
17
-
18
- ## 2. Mandatory Rules
19
-
20
- ### A. Translate at Consumer Level for `ReactNode` Props
21
-
22
- All user-facing text props in Wangs UI components (`label`, `placeholder`, `emptyMessage`, `header`, `tooltip`, etc.) are typed as `ReactNode`. Components render them as-is and **do NOT call `t()` internally**. Translation MUST be called at the consumer level:
23
-
24
- ```tsx
25
- // DO
26
- const { t } = useI18n();
27
-
28
- <Button label={t('Save Changes')} />
29
- <Select placeholder={t('Select category...')} />
30
- <DataTable emptyMessage={t('No data available')} />
31
-
32
- // DO NOT: pass raw English strings without t()
33
- <Button label="Save Changes" />
34
- ```
35
-
36
- ### B. Named Variable Interpolation (Enforced)
37
-
38
- Always use descriptive named variables (`{count}`, `{userName}`, `{fileName}`) passed via an object argument. This provides necessary semantic context for AI translation engines:
39
-
40
- ```tsx
41
- // DO
42
- t('Upload {count} files to {folderName}', { count: 5, folderName: 'Documents' });
43
-
44
- // DO NOT: use positional indices for multi-variable strings
45
- t('Upload {0} files to {1}', 5, 'Documents');
46
- ```
47
-
48
- ### C. ICU Pluralization & Zero-State (`=0`)
49
-
50
- Write ICU conditional plural/select structures directly inside the text keys. Always include `=0` branches to handle zero values within ICU syntax instead of creating separate JavaScript `if` or ternary branches:
51
-
52
- ```tsx
53
- // DO
54
- t('{count, plural, =0 {No files uploaded} one {Uploaded 1 file} other {Uploaded {count} files}}', {
55
- count,
56
- });
57
-
58
- // DO NOT: write manual JS conditionals for pluralization
59
- count === 0 ? t('No files') : t('{count} files', { count });
60
- ```
61
-
62
- ### D. Formatting Dates, Numbers & Currencies
63
-
64
- Always format dates, numbers, currencies, and relative time using `useLocaleFormatter()` **before** passing them into `t()`:
65
-
66
- ```tsx
67
- import { useI18n, useLocaleFormatter } from '@wangs-ui/react-i18n';
68
-
69
- const { t } = useI18n();
70
- const { formatDate, formatRelativeTime, formatCurrency, formatNumber } = useLocaleFormatter();
71
-
72
- // Date formatting
73
- const dateLabel = t('Created on {date}', {
74
- date: formatDate(new Date(), 'dd MMMM yyyy'),
75
- });
76
-
77
- // Relative time formatting
78
- const timeLabel = t('Updated {time}', {
79
- time: formatRelativeTime(updatedAt),
80
- });
81
-
82
- // Currency formatting
83
- const priceText = formatCurrency(150000, 'IDR');
84
- ```
85
-
86
- ---
87
-
88
- ## 3. Strict Prohibitions (MUST NOT)
89
-
90
- 1. **NO Dynamic String Concatenation**:
91
- Never use string concatenation or template literals inside `t()`:
92
- ```tsx
93
- // FORBIDDEN
94
- t('Hello ' + user.name);
95
- t(`Upload to ${folder}`);
96
- ```
97
- 2. **NO Hardcoded Strings in JSX**:
98
- Never render raw text nodes without `t()`:
99
- ```tsx
100
- // FORBIDDEN
101
- <h1>Dashboard Overview</h1>
102
- ```
103
- 3. **NO Static Translation JSON Files**:
104
- Never create static `.json` dictionaries (e.g. `id.json`, `en.json`). Localization is completely dynamic via JIT translation.
@@ -1,84 +0,0 @@
1
- ---
2
- name: wangs-ui-components
3
- description: Guidelines and rules for using and composing Wangs UI Design System components and icons in this React application.
4
- ---
5
-
6
- # Skill: Wangs UI Component Usage & Design System Standards
7
-
8
- This skill defines the rules for building user interfaces with `@wangs-ui/react-core`, `@wangs-ui/react-icons`, and `@wangs-ui/react-presets`.
9
-
10
- ---
11
-
12
- ## 1. Subpath Import Style (Mandatory)
13
-
14
- Components and providers are exported via clean, modular subpaths:
15
-
16
- ```tsx
17
- // Provider / Contexts
18
- import { WangsUiProvider } from '@wangs-ui/react-core/api';
19
-
20
- // Primitive Components
21
- import Button from '@wangs-ui/react-core/primitive/button';
22
- import Card from '@wangs-ui/react-core/primitive/card';
23
- import InputText from '@wangs-ui/react-core/primitive/inputtext';
24
- import Select from '@wangs-ui/react-core/primitive/select';
25
- import Dialog from '@wangs-ui/react-core/primitive/dialog';
26
- import Modal from '@wangs-ui/react-core/primitive/modal';
27
- import DataTable from '@wangs-ui/react-core/primitive/datatable';
28
-
29
- // Icons
30
- import { AddLine, SearchLine, DeleteBin6Line } from '@wangs-ui/react-icons';
31
- ```
32
-
33
- ---
34
-
35
- ## 2. Primitive Component Rule (Strictly Enforced)
36
-
37
- Never use unstyled raw HTML controls when a corresponding Wangs UI primitive component exists:
38
-
39
- | Raw HTML Control (Forbidden) | Wangs UI Component (Mandatory) | Subpath Import |
40
- | :--------------------------- | :----------------------------- | :--------------------------------------------- |
41
- | `<button>` | `Button` | `@wangs-ui/react-core/primitive/button` |
42
- | `<input type="text">` | `InputText` | `@wangs-ui/react-core/primitive/inputtext` |
43
- | `<input type="number">` | `InputNumber` | `@wangs-ui/react-core/primitive/inputnumber` |
44
- | `<input type="password">` | `InputPassword` | `@wangs-ui/react-core/primitive/inputpassword` |
45
- | `<input type="checkbox">` | `Checkbox` | `@wangs-ui/react-core/primitive/checkbox` |
46
- | `<select>` / dropdown | `Select` / `Dropdown` | `@wangs-ui/react-core/primitive/select` |
47
- | `<dialog>` / modal | `Dialog` / `Modal` | `@wangs-ui/react-core/primitive/dialog` |
48
- | `<table>` | `DataTable` | `@wangs-ui/react-core/primitive/datatable` |
49
- | Container box | `Card` | `@wangs-ui/react-core/primitive/card` |
50
-
51
- ---
52
-
53
- ## 3. Typography & Spacing Scale
54
-
55
- ### Typography (Typeface: Manrope)
56
-
57
- Use semantic heading classes rather than arbitrary text size utilities:
58
-
59
- - `.heading-1` (22px, 600 weight) — Page title
60
- - `.heading-2` (18px, 600 weight) — Section title
61
- - `.heading-3` (16px, 500 weight) — Card / Container header
62
- - `.heading-4` (14px, 500 weight) — Sub-headers / Field labels
63
- - `.heading-5` (12px, 600 weight) — Small titles
64
- - `.p` (12px, 500 weight) — Body text
65
-
66
- ### Color Tokens
67
-
68
- - Primary: `bg-primary-500`, `text-primary-500`, `border-primary-500`
69
- - Secondary / Text: `text-secondary-900` (main text), `text-secondary-500` (muted/caption), `border-secondary-200`
70
- - Semantic: `success-500`, `danger-500`, `warning-500`, `info-500`
71
-
72
- ### Spacing Tokens (4px Grid)
73
-
74
- - `gap-xs` (4px), `gap-s` (6px), `gap-md` (8px), `gap-m` (12px), `gap-l` (16px), `gap-xl` (20px), `gap-xxl` (24px)
75
- - `p-xs`, `p-s`, `p-md`, `p-m`, `p-l`, `p-xl`, `p-xxl`
76
-
77
- ---
78
-
79
- ## 4. MCP Component Inspection
80
-
81
- When creating new views or composing complex components:
82
-
83
- - Use the `@wangs-ui/mcp` server tools (`get-documentation`, `list-all-documentation`) to inspect component props, types, and story examples.
84
- - Do NOT guess prop names or event handlers.