From 35f07c508d49aad1cf260e76594619ec2a447ba3 Mon Sep 17 00:00:00 2001 From: Khalid Abdi Date: Thu, 9 Jul 2026 20:41:58 +0300 Subject: [PATCH] frontend: block past dates in appointment/invoice pickers; drop stale Settings Features - Appointment date picker disables days before today. - Invoice issue-date picker disables past days by default, with an opt-in "Back-date" checkbox for recording older invoices (edit mode keeps past dates). Added invoices.dialog.backdate to all locales. - Removed the inert, outdated "Features" section (patient-owned storage / require signed toggles) from Settings and its now-unused i18n keys. Co-Authored-By: Claude Opus 4.8 --- .agents/skills/heroui-migration/LICENSE.txt | 199 ++++ .agents/skills/heroui-migration/SKILL.md | 140 +++ .../get_component_migration_guides.mjs | 110 +++ .../scripts/get_hooks_migration_guide.mjs | 53 ++ .../scripts/get_migration_guide.mjs | 60 ++ .../scripts/get_styling_migration_guide.mjs | 53 ++ .../scripts/list_migration_guides.mjs | 65 ++ .agents/skills/heroui-native/LICENSE.txt | 199 ++++ .agents/skills/heroui-native/SKILL.md | 229 +++++ .../scripts/get_component_docs.mjs | 157 +++ .../skills/heroui-native/scripts/get_docs.mjs | 154 +++ .../heroui-native/scripts/get_theme.mjs | 222 +++++ .../heroui-native/scripts/list_components.mjs | 134 +++ .agents/skills/heroui-react/LICENSE.txt | 199 ++++ .agents/skills/heroui-react/SKILL.md | 234 +++++ .../scripts/get_component_docs.mjs | 157 +++ .../skills/heroui-react/scripts/get_docs.mjs | 148 +++ .../heroui-react/scripts/get_source.mjs | 160 ++++ .../heroui-react/scripts/get_styles.mjs | 160 ++++ .../skills/heroui-react/scripts/get_theme.mjs | 177 ++++ .../heroui-react/scripts/list_components.mjs | 134 +++ .claude/skills/heroui-migration | 1 + .claude/skills/heroui-native | 1 + .claude/skills/heroui-react | 1 + .mcp.json | 12 +- .../appointments/add-appointment-dialog.tsx | 9 + .../invoices/invoice-form-dialog.tsx | 39 +- .../settings/settings-preferences.tsx | 17 - frontend/lib/i18n/locales/ar/translation.json | 9 +- frontend/lib/i18n/locales/de/translation.json | 9 +- frontend/lib/i18n/locales/en/translation.json | 9 +- frontend/lib/i18n/locales/fr/translation.json | 9 +- frontend/lib/i18n/locales/so/translation.json | 9 +- package-lock.json | 898 ++++++++++++++++-- skills-lock.json | 18 + 35 files changed, 4028 insertions(+), 157 deletions(-) create mode 100644 .agents/skills/heroui-migration/LICENSE.txt create mode 100644 .agents/skills/heroui-migration/SKILL.md create mode 100644 .agents/skills/heroui-migration/scripts/get_component_migration_guides.mjs create mode 100644 .agents/skills/heroui-migration/scripts/get_hooks_migration_guide.mjs create mode 100644 .agents/skills/heroui-migration/scripts/get_migration_guide.mjs create mode 100644 .agents/skills/heroui-migration/scripts/get_styling_migration_guide.mjs create mode 100644 .agents/skills/heroui-migration/scripts/list_migration_guides.mjs create mode 100644 .agents/skills/heroui-native/LICENSE.txt create mode 100644 .agents/skills/heroui-native/SKILL.md create mode 100755 .agents/skills/heroui-native/scripts/get_component_docs.mjs create mode 100755 .agents/skills/heroui-native/scripts/get_docs.mjs create mode 100755 .agents/skills/heroui-native/scripts/get_theme.mjs create mode 100755 .agents/skills/heroui-native/scripts/list_components.mjs create mode 100644 .agents/skills/heroui-react/LICENSE.txt create mode 100644 .agents/skills/heroui-react/SKILL.md create mode 100644 .agents/skills/heroui-react/scripts/get_component_docs.mjs create mode 100644 .agents/skills/heroui-react/scripts/get_docs.mjs create mode 100644 .agents/skills/heroui-react/scripts/get_source.mjs create mode 100644 .agents/skills/heroui-react/scripts/get_styles.mjs create mode 100644 .agents/skills/heroui-react/scripts/get_theme.mjs create mode 100644 .agents/skills/heroui-react/scripts/list_components.mjs create mode 120000 .claude/skills/heroui-migration create mode 120000 .claude/skills/heroui-native create mode 120000 .claude/skills/heroui-react diff --git a/.agents/skills/heroui-migration/LICENSE.txt b/.agents/skills/heroui-migration/LICENSE.txt new file mode 100644 index 0000000..bf4dfe2 --- /dev/null +++ b/.agents/skills/heroui-migration/LICENSE.txt @@ -0,0 +1,199 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please also get an approval + from the project maintainers before using the Apache License. + + Copyright 2025 NextUI Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.agents/skills/heroui-migration/SKILL.md b/.agents/skills/heroui-migration/SKILL.md new file mode 100644 index 0000000..611d31c --- /dev/null +++ b/.agents/skills/heroui-migration/SKILL.md @@ -0,0 +1,140 @@ +--- +name: heroui-migration +description: "HeroUI v2 to v3 migration guide for agents. Use when migrating HeroUI v2 apps to v3, upgrading components, or accessing migration documentation. Keywords: HeroUI migration, v2 to v3, migration guide, upgrade HeroUI." +metadata: + author: heroui + version: "2.0.0" + status: preview +--- + +# HeroUI v2 to v3 Migration Guide + +This skill helps agents migrate HeroUI v2 applications to v3. HeroUI v3 introduces breaking changes: compound components, no Provider, Tailwind v4, and removed hooks. + +--- + +## Installation + +```bash +curl -fsSL https://heroui.com/install | bash -s heroui-migration +``` + +--- + +## CRITICAL: Always Fetch Migration Docs Before Applying + +**Do NOT assume v2 patterns work in v3.** Always fetch migration guides before implementing changes. + +### Key v2 → v3 Changes + +| Feature | v2 (Migrate From) | v3 (Migrate To) | +| ------------- | -------------------------- | -------------------------------------- | +| Provider | `` required | **No Provider needed** | +| Component API | Flat props: `` | Compound: `` | +| Event handlers | `onClick` | `onPress` | +| Styling | `classNames` prop | `className` prop | +| Hooks | `useSwitch`, `useDisclosure`, etc. | Compound components, `useOverlayState` | +| Packages | `@heroui/system`, `@heroui/theme` | `@heroui/react`, `@heroui/styles` | + +--- + +## Accessing Migration Documentation + +**For migration details, examples, and step-by-step guides, always fetch documentation:** + +### Using Scripts + +```bash +# List all available component migration guides +node scripts/list_migration_guides.mjs + +# Get main migration workflow (full or incremental) +node scripts/get_migration_guide.mjs full +node scripts/get_migration_guide.mjs incremental + +# Get component-specific migration guides +node scripts/get_component_migration_guides.mjs button +node scripts/get_component_migration_guides.mjs button card modal + +# Get styling migration guide +node scripts/get_styling_migration_guide.mjs + +# Get hooks migration guide +node scripts/get_hooks_migration_guide.mjs +``` + +### Direct URLs + +Migration docs (preview): `https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration/{filename}` + +Examples: + +- Full migration: `.../agent-guide-full.mdx` +- Incremental: `.../agent-guide-incremental.mdx` +- Button: `.../button.mdx` +- Styling: `.../styling.mdx` +- Hooks: `.../hooks.mdx` + +Override base URL with `HEROUI_MIGRATION_DOCS_BASE` when docs are merged to production. + +### MCP Alternative + +When using Cursor or other MCP clients, configure the Migration MCP server for tool-based access: + +```json +{ + "mcpServers": { + "heroui-migration": { + "url": "https://migration-mcp.heroui.com" + } + } +} +``` + +--- + +## Migration Strategies + +### Full Migration + +- Best for: Projects that can dedicate focused time; teams comfortable with temporarily broken code +- Migrate all component code first (project broken during migration) +- Switch dependencies to v3 +- Complete styling migration + +### Incremental Migration + +- Best for: Projects that must stay functional; large codebases migrating gradually +- Set up coexistence (pnpm aliases or component packages) +- Migrate components one-by-one +- Both v2 and v3 coexist during migration + +**Always fetch the agent guide before starting:** `node scripts/get_migration_guide.mjs full` or `incremental` + +--- + +## Core Principles + +1. **Fetch first**: Use scripts to get migration guides before applying changes +2. **Compound components**: v3 uses `Card.Header`, `Card.Title`, `Button` with children—not flat props +3. **No Provider**: Remove `HeroUIProvider` when migrating +4. **onPress not onClick**: All interactive components use `onPress` +5. **Workflow**: Analyze → Migrate components → Switch deps → Styling migration + +--- + +## Migration Workflow Summary + +1. Create migration branch +2. Analyze project (HeroUI imports, component usage) +3. Fetch main guide: `node scripts/get_migration_guide.mjs full` +4. Migrate components in batches (fetch component guides per batch) +5. Switch dependencies to v3 +6. Fetch styling guide: `node scripts/get_styling_migration_guide.mjs` +7. Apply styling updates + +--- + +## Preview Mode + +This skill targets the staging deployment of the `docs/migration` branch. Once docs are merged to main and live on heroui.com, set `HEROUI_MIGRATION_DOCS_BASE=https://heroui.com/docs/react/migration` or update the default in scripts. diff --git a/.agents/skills/heroui-migration/scripts/get_component_migration_guides.mjs b/.agents/skills/heroui-migration/scripts/get_component_migration_guides.mjs new file mode 100644 index 0000000..2bed636 --- /dev/null +++ b/.agents/skills/heroui-migration/scripts/get_component_migration_guides.mjs @@ -0,0 +1,110 @@ +#!/usr/bin/env node +/** + * Get migration guides for HeroUI components (v2 to v3). + * + * Usage: + * node get_component_migration_guides.mjs button + * node get_component_migration_guides.mjs button card modal + * + * Output: + * MDX migration guide content for each component + */ + +const DOCS_BASE = + process.env.HEROUI_MIGRATION_DOCS_BASE || + "https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration"; +const APP_PARAM = "app=migration-skills"; + +/** + * Convert PascalCase or mixed case to kebab-case. + */ +function toKebabCase(name) { + return name + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") + .toLowerCase() + .trim(); +} + +async function fetchDoc(filename) { + const url = `${DOCS_BASE}/${filename}?${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Migration-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.text(); + } catch (error) { + throw new Error(error.message); + } +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_component_migration_guides.mjs [Component2] ..."); + console.error("Example: node get_component_migration_guides.mjs button card modal"); + console.error("\nUse list_migration_guides.mjs to see all available components."); + process.exit(1); + } + + const components = args.map((c) => toKebabCase(c)); + + console.error(`# Fetching migration guides for: ${components.join(", ")}...`); + + const results = []; + + for (const component of components) { + const filename = `${component}.mdx`; + + try { + const content = await fetchDoc(filename); + const title = component.charAt(0).toUpperCase() + component.slice(1).replace(/-/g, " "); + + results.push({ + component, + content: `# ${title} Migration Guide\n\n**Component:** ${component}\n**Source:** ${DOCS_BASE}/${filename}\n\n---\n\n${content}`, + }); + } catch (error) { + results.push({ + component, + error: error.message, + }); + } + } + + const failed = results.filter((r) => r.error); + + if (failed.length > 0) { + failed.forEach((r) => console.error(`# Error for ${r.component}: ${r.error}`)); + } + + if (results.length === 1) { + const r = results[0]; + + if (r.content) { + console.log(r.content); + } else { + console.log(JSON.stringify(r, null, 2)); + process.exit(1); + } + } else { + const output = results + .map((r) => (r.content ? r.content : `# ${r.component} Migration Guide\n\nError: ${r.error}`)) + .join("\n\n---\n\n"); + + console.log(output); + if (failed.length === results.length) { + process.exit(1); + } + } +} + +main(); diff --git a/.agents/skills/heroui-migration/scripts/get_hooks_migration_guide.mjs b/.agents/skills/heroui-migration/scripts/get_hooks_migration_guide.mjs new file mode 100644 index 0000000..72fe248 --- /dev/null +++ b/.agents/skills/heroui-migration/scripts/get_hooks_migration_guide.mjs @@ -0,0 +1,53 @@ +#!/usr/bin/env node +/** + * Get the hooks migration guide for HeroUI v2 to v3. + * + * Usage: + * node get_hooks_migration_guide.mjs + * + * Output: + * MDX hooks migration guide content + */ + +const DOCS_BASE = + process.env.HEROUI_MIGRATION_DOCS_BASE || + "https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration"; +const APP_PARAM = "app=migration-skills"; + +async function fetchDoc(filename) { + const url = `${DOCS_BASE}/${filename}?${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Migration-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.text(); + } catch (error) { + throw new Error(`Failed to fetch ${filename}: ${error.message}`); + } +} + +async function main() { + const filename = "hooks.mdx"; + + console.error("# Fetching hooks migration guide..."); + + try { + const content = await fetchDoc(filename); + + console.log( + `# HeroUI v2 to v3 Hooks Migration Guide\n\n**Source:** ${DOCS_BASE}/${filename}\n\n---\n\n${content}`, + ); + } catch (error) { + console.error(`# Error: ${error.message}`); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-migration/scripts/get_migration_guide.mjs b/.agents/skills/heroui-migration/scripts/get_migration_guide.mjs new file mode 100644 index 0000000..423e326 --- /dev/null +++ b/.agents/skills/heroui-migration/scripts/get_migration_guide.mjs @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/** + * Get the main migration workflow guide (full or incremental) for HeroUI v2 to v3. + * + * Usage: + * node get_migration_guide.mjs [full|incremental] + * + * Default: full + * + * Output: + * MDX migration guide content + */ + +const DOCS_BASE = + process.env.HEROUI_MIGRATION_DOCS_BASE || + "https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration"; +const APP_PARAM = "app=migration-skills"; + +async function fetchDoc(filename) { + const url = `${DOCS_BASE}/${filename}?${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Migration-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.text(); + } catch (error) { + throw new Error(`Failed to fetch ${filename}: ${error.message}`); + } +} + +async function main() { + const arg = (process.argv[2] || "full").toLowerCase(); + const migrationType = arg === "incremental" ? "incremental" : "full"; + const filename = + migrationType === "incremental" ? "agent-guide-incremental.mdx" : "agent-guide-full.mdx"; + + console.error(`# Fetching ${migrationType} migration guide...`); + + try { + const content = await fetchDoc(filename); + const title = + migrationType === "incremental" + ? "HeroUI v2 to v3 Agent Migration Guide - Incremental Migration" + : "HeroUI v2 to v3 Agent Migration Guide - Full Migration"; + + console.log(`# ${title}\n\n**Source:** ${DOCS_BASE}/${filename}\n\n---\n\n${content}`); + } catch (error) { + console.error(`# Error: ${error.message}`); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-migration/scripts/get_styling_migration_guide.mjs b/.agents/skills/heroui-migration/scripts/get_styling_migration_guide.mjs new file mode 100644 index 0000000..68e3566 --- /dev/null +++ b/.agents/skills/heroui-migration/scripts/get_styling_migration_guide.mjs @@ -0,0 +1,53 @@ +#!/usr/bin/env node +/** + * Get the styling migration guide for HeroUI v2 to v3. + * + * Usage: + * node get_styling_migration_guide.mjs + * + * Output: + * MDX styling migration guide content + */ + +const DOCS_BASE = + process.env.HEROUI_MIGRATION_DOCS_BASE || + "https://heroui-git-docs-migration-heroui.vercel.app/docs/react/migration"; +const APP_PARAM = "app=migration-skills"; + +async function fetchDoc(filename) { + const url = `${DOCS_BASE}/${filename}?${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Migration-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.text(); + } catch (error) { + throw new Error(`Failed to fetch ${filename}: ${error.message}`); + } +} + +async function main() { + const filename = "styling.mdx"; + + console.error("# Fetching styling migration guide..."); + + try { + const content = await fetchDoc(filename); + + console.log( + `# HeroUI v2 to v3 Styling Migration Guide\n\n**Source:** ${DOCS_BASE}/${filename}\n\n---\n\n${content}`, + ); + } catch (error) { + console.error(`# Error: ${error.message}`); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-migration/scripts/list_migration_guides.mjs b/.agents/skills/heroui-migration/scripts/list_migration_guides.mjs new file mode 100644 index 0000000..91982c0 --- /dev/null +++ b/.agents/skills/heroui-migration/scripts/list_migration_guides.mjs @@ -0,0 +1,65 @@ +#!/usr/bin/env node +/** + * List all available HeroUI v2 to v3 component migration guides. + * + * Usage: + * node list_migration_guides.mjs + * + * Output: + * List of component names that have migration guides + * + * Note: Keep in sync with migration-mcp list-migration-guides.ts when adding components. + */ + +// Component migration guides available - must match migration-mcp list-migration-guides.ts +const AVAILABLE_COMPONENTS = [ + "accordion", + "alert", + "autocomplete", + "avatar", + "breadcrumbs", + "button", + "button-group", + "card", + "checkbox", + "checkbox-group", + "chip", + "code", + "divider", + "dropdown", + "form", + "image", + "input", + "input-otp", + "kbd", + "link", + "listbox", + "modal", + "navbar", + "numberinput", + "popover", + "radio", + "radio-group", + "scroll-shadow", + "select", + "skeleton", + "slider", + "snippet", + "spacer", + "spinner", + "switch", + "tabs", + "toast", + "tooltip", + "user", +]; + +function main() { + const componentsList = AVAILABLE_COMPONENTS.map((name) => ` - ${name}`).join("\n"); + + console.log( + `# Available Component Migration Guides\n\nFound ${AVAILABLE_COMPONENTS.length} component migration guides:\n\n${componentsList}\n\nUse get_component_migration_guides.mjs with component names to fetch specific guides.\nExample: node get_component_migration_guides.mjs button card modal`, + ); +} + +main(); diff --git a/.agents/skills/heroui-native/LICENSE.txt b/.agents/skills/heroui-native/LICENSE.txt new file mode 100644 index 0000000..bf4dfe2 --- /dev/null +++ b/.agents/skills/heroui-native/LICENSE.txt @@ -0,0 +1,199 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please also get an approval + from the project maintainers before using the Apache License. + + Copyright 2025 NextUI Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.agents/skills/heroui-native/SKILL.md b/.agents/skills/heroui-native/SKILL.md new file mode 100644 index 0000000..33ace07 --- /dev/null +++ b/.agents/skills/heroui-native/SKILL.md @@ -0,0 +1,229 @@ +--- +name: heroui-native +description: "HeroUI Native component library for React Native (Tailwind v4 via Uniwind). Use when building mobile UIs with HeroUI Native — creating Buttons, Cards, TextFields, Dialogs; installing heroui-native; configuring dark/light themes; or fetching component docs. Keywords: HeroUI Native, heroui-native, React Native UI, Uniwind, mobile components." +metadata: + author: heroui + version: "2.0.1" +--- + +# HeroUI Native Development Guide + +HeroUI Native is a component library built on **Uniwind (Tailwind CSS for React Native)** and **React Native**, providing accessible, customizable UI components for mobile applications. + +--- + +## Installation + +```bash +curl -fsSL https://heroui.com/install | bash -s heroui-native +``` + +--- + +## CRITICAL: Native Only - Do Not Use Web Patterns + +**This guide is for HeroUI Native ONLY.** Do NOT apply HeroUI React (web) patterns — the package, styling engine, and color format all differ: + +| Feature | React (Web) | Native (Mobile) | +| ------------ | -------------------- | ----------------------------------- | +| **Styling** | Tailwind CSS v4 | Uniwind (Tailwind for React Native) | +| **Colors** | oklch format | HSL format | +| **Package** | `@heroui/react` | `heroui-native` | +| **Platform** | Web browsers | iOS & Android | + +```tsx +// CORRECT — Native pattern +import { Button } from "heroui-native"; + +; +``` + +**Always fetch Native docs before implementing.** + +--- + +## Core Principles + +- Semantic variants (`primary`, `secondary`, `tertiary`) over visual descriptions +- Composition over configuration (compound components) +- Theme variables with HSL color format +- React Native StyleSheet patterns with Uniwind utilities + +--- + +## Accessing Documentation & Component Information + +**For component details, examples, props, and implementation patterns, always fetch documentation:** + +### Using Scripts + +```bash +# List all available components +node scripts/list_components.mjs + +# Get component documentation (MDX) +node scripts/get_component_docs.mjs Button +node scripts/get_component_docs.mjs Button Card TextField + +# Get theme variables +node scripts/get_theme.mjs + +# Get non-component docs (guides, releases) +node scripts/get_docs.mjs /docs/native/getting-started/theming +``` + +### Direct MDX URLs + +Component docs: `https://heroui.com/docs/native/components/{component-name}.mdx` + +Examples: + +- Button: `https://heroui.com/docs/native/components/button.mdx` +- Dialog: `https://heroui.com/docs/native/components/dialog.mdx` +- TextField: `https://heroui.com/docs/native/components/text-field.mdx` + +Getting started guides: `https://heroui.com/docs/native/getting-started/{topic}.mdx` + +**Important:** Always fetch component docs before implementing. The MDX docs include complete examples, props, anatomy, and API references. + +--- + +## Installation Essentials + +### Quick Install + +```bash +npm i heroui-native react-native-reanimated react-native-gesture-handler react-native-safe-area-context @gorhom/bottom-sheet react-native-svg react-native-worklets tailwind-merge tailwind-variants +``` + +### Framework Setup (Expo - Recommended) + +1. **Install dependencies:** + +```bash +npx create-expo-app MyApp +cd MyApp +npm i heroui-native uniwind tailwindcss +npm i react-native-reanimated react-native-gesture-handler react-native-safe-area-context @gorhom/bottom-sheet react-native-svg react-native-worklets tailwind-merge tailwind-variants +``` + +2. **Create `global.css`:** + +```css +@import "tailwindcss"; +@import "uniwind"; +@import "heroui-native/styles"; + +@source "./node_modules/heroui-native/lib"; +``` + +3. **Wrap app with providers:** + +```tsx +import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { HeroUINativeProvider } from "heroui-native"; +import "./global.css"; + +export default function Layout() { + return ( + + + + + + ); +} +``` + +### Critical Setup Requirements + +1. **Uniwind is Required** - HeroUI Native uses Uniwind (Tailwind CSS for React Native) +2. **HeroUINativeProvider Required** - Wrap your app with `HeroUINativeProvider` +3. **GestureHandlerRootView Required** - Wrap with `GestureHandlerRootView` from react-native-gesture-handler +4. **Use Compound Components** - Components use compound structure (e.g., `Card.Header`, `Card.Body`) +5. **Use onPress, not onClick** - React Native uses `onPress` event handlers +6. **Platform-Specific Code** - Use `Platform.OS` for iOS/Android differences + +--- + +## Component Patterns + +HeroUI Native uses **compound component patterns**. Each component has subcomponents accessed via dot notation. + +**Example - Card:** + +```tsx + + {/* Icons, badges */} + + Title + Description + + {/* Actions */} + +``` + +**Key Points:** + +- Always use compound structure - don't flatten to props +- Subcomponents are accessed via dot notation (e.g., `Card.Header`) +- Native Card uses `Card.Body` (not `Card.Content`); Title and Description go inside Body +- **Fetch component docs for complete anatomy and examples** + +--- + +## Semantic Variants + +HeroUI uses semantic naming to communicate functional intent: + +| Variant | Purpose | Usage | +| ------------- | --------------------------------- | -------------- | +| `primary` | Main action to move forward | 1 per context | +| `secondary` | Alternative actions | Multiple | +| `tertiary` | Dismissive actions (cancel, skip) | Sparingly | +| `danger` | Destructive actions | When needed | +| `danger-soft` | Soft destructive actions | Less prominent | +| `ghost` | Low-emphasis actions | Minimal weight | +| `outline` | Secondary actions | Bordered style | + +**Don't use raw colors** - semantic variants adapt to themes and accessibility. + +--- + +## Theming + +HeroUI Native uses CSS variables via Tailwind/Uniwind for theming. Theme colors are defined in `global.css`: + +```css +@theme { + --color-accent: hsl(260, 100%, 70%); + --color-accent-foreground: hsl(0, 0%, 100%); +} +``` + +**Get current theme variables:** + +```bash +node scripts/get_theme.mjs +``` + +**Access theme colors programmatically:** + +```tsx +import { useThemeColor } from "heroui-native"; + +const accentColor = useThemeColor("accent"); +``` + +**Theme switching (Light/Dark Mode):** + +```tsx +import { Uniwind, useUniwind } from "uniwind"; + +const { theme } = useUniwind(); +Uniwind.setTheme(theme === "light" ? "dark" : "light"); +``` + +For detailed theming, fetch: `https://heroui.com/docs/native/getting-started/theming.mdx` diff --git a/.agents/skills/heroui-native/scripts/get_component_docs.mjs b/.agents/skills/heroui-native/scripts/get_component_docs.mjs new file mode 100755 index 0000000..1cec325 --- /dev/null +++ b/.agents/skills/heroui-native/scripts/get_component_docs.mjs @@ -0,0 +1,157 @@ +#!/usr/bin/env node +/** + * Get complete component documentation (MDX) for HeroUI Native components. + * + * Usage: + * node get_component_docs.mjs Button + * node get_component_docs.mjs Button Card TextField + * + * Output: + * MDX documentation including imports, usage, variants, props, examples + */ + +const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com"; +const FALLBACK_BASE = "https://heroui.com"; +const APP_PARAM = "app=native-skills"; + +/** + * Convert PascalCase to kebab-case. + */ +function toKebabCase(name) { + return name + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") + .toLowerCase(); +} + +/** + * Fetch data from HeroUI Native API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Native-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + return null; + } + + return await response.json(); + } catch { + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(component) { + const kebabName = toKebabCase(component); + const url = `${FALLBACK_BASE}/docs/native/components/${kebabName}.mdx`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {component, error: `Failed to fetch docs for ${component}`}; + } + + const content = await response.text(); + + return { + component, + content, + contentType: "mdx", + source: "fallback", + url, + }; + } catch { + return {component, error: `Failed to fetch docs for ${component}`}; + } +} + +/** + * Main function to get component documentation. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_component_docs.mjs [Component2] ..."); + console.error("Example: node get_component_docs.mjs Button Card"); + process.exit(1); + } + + const components = args; + + // Try API first - use POST /v1/components/docs for batch requests + console.error(`# Fetching Native docs for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/docs", "POST", {components}); + + if (data && data.results) { + // Output results + if (data.results.length === 1) { + // Single component - output content directly for easier reading + const result = data.results[0]; + + if (result.content) { + console.log(result.content); + } else if (result.error) { + console.error(`# Error for ${result.component}: ${result.error}`); + console.log(JSON.stringify(result, null, 2)); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to individual component fetches + console.error("# API failed, using fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchFallback(component); + + results.push(result); + } + + // Output results + if (results.length === 1) { + // Single component - output content directly for easier reading + const result = results[0]; + + if (result.content) { + console.log(result.content); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(results, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-native/scripts/get_docs.mjs b/.agents/skills/heroui-native/scripts/get_docs.mjs new file mode 100755 index 0000000..cf3a47e --- /dev/null +++ b/.agents/skills/heroui-native/scripts/get_docs.mjs @@ -0,0 +1,154 @@ +#!/usr/bin/env node +/** + * Get non-component HeroUI Native documentation (guides, theming, releases). + * + * Usage: + * node get_docs.mjs /docs/native/getting-started/theming + * node get_docs.mjs /docs/native/releases/beta-12 + * + * Output: + * MDX documentation content + * + * Note: For component docs, use get_component_docs.mjs instead. + */ + +const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com"; +const FALLBACK_BASE = "https://heroui.com"; +const APP_PARAM = "app=native-skills"; + +/** + * Fetch documentation from HeroUI Native API. + * Uses v1 endpoint pattern: /v1/docs/:path + */ +async function fetchApi(path) { + // The v1 API expects path without /docs/ prefix + // Input: /docs/native/getting-started/theming + // API expects: native/getting-started/theming (route is /v1/docs/:path(*)) + let apiPath = path.startsWith("/docs/") + ? path.slice(6) // Remove /docs/ prefix + : path.startsWith("/") + ? path.slice(1) // Remove leading / + : path; + + const separator = "?"; + const url = `${API_BASE}/v1/docs/${apiPath}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(path) { + // Ensure path starts with /docs and ends with .mdx + let cleanPath = path.replace(/^\//, ""); + + if (!cleanPath.endsWith(".mdx")) { + cleanPath = `${cleanPath}.mdx`; + } + + const url = `${FALLBACK_BASE}/${cleanPath}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {error: `HTTP ${response.status}: ${response.statusText}`, path}; + } + + const content = await response.text(); + + return { + content, + contentType: "mdx", + path, + source: "fallback", + url, + }; + } catch (error) { + return {error: `Fetch Error: ${error.message}`, path}; + } +} + +/** + * Main function to get documentation for specified path. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_docs.mjs "); + console.error("Example: node get_docs.mjs /docs/native/getting-started/theming"); + console.error(); + console.error("Available paths include:"); + console.error(" /docs/native/getting-started/theming"); + console.error(" /docs/native/getting-started/colors"); + console.error(" /docs/native/getting-started/styling"); + console.error(" /docs/native/releases/beta-12"); + console.error(); + console.error("Note: For component docs, use get_component_docs.mjs instead."); + process.exit(1); + } + + const path = args[0]; + + // Check if user is trying to get component docs + if (path.includes("/components/")) { + console.error("# Warning: Use get_component_docs.mjs for component documentation."); + const componentName = path.split("/").pop().replace(".mdx", ""); + const titleCase = componentName.charAt(0).toUpperCase() + componentName.slice(1); + + console.error(`# Example: node get_component_docs.mjs ${titleCase}`); + } + + // Validate Native path + if (!path.startsWith("/docs/native/")) { + console.error("# Warning: Native documentation paths should start with /docs/native/"); + console.error(`# Provided path: ${path}`); + } + + console.error(`# Fetching Native documentation for ${path}...`); + + // Try API first + const data = await fetchApi(path); + + if (data && data.content) { + data.source = "api"; + console.log(data.content); + + return; + } + + // Fallback to direct fetch + console.error("# API failed, using fallback..."); + const fallbackData = await fetchFallback(path); + + if (fallbackData.content) { + console.log(fallbackData.content); + } else { + console.log(JSON.stringify(fallbackData, null, 2)); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-native/scripts/get_theme.mjs b/.agents/skills/heroui-native/scripts/get_theme.mjs new file mode 100755 index 0000000..88faf95 --- /dev/null +++ b/.agents/skills/heroui-native/scripts/get_theme.mjs @@ -0,0 +1,222 @@ +#!/usr/bin/env node +/** + * Get theme variables and design tokens for HeroUI Native. + * + * Usage: + * node get_theme.mjs + * + * Output: + * Theme variables organized by light/dark with HSL color format + */ + +const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com"; +const APP_PARAM = "app=native-skills"; + +// Fallback theme reference when API is unavailable +const FALLBACK_THEME = { + borderRadius: { + full: 9999, + lg: 12, + md: 8, + sm: 6, + }, + dark: { + colors: [ + { + category: "base", + name: "--color-background", + value: "hsl(0, 0%, 14.5%)", + }, + { + category: "semantic", + name: "--color-foreground", + value: "hsl(0, 0%, 98.4%)", + }, + { + category: "semantic", + name: "--color-accent", + value: "hsl(264.1, 100%, 55.1%)", + }, + { + category: "status", + name: "--color-danger", + value: "hsl(25.3, 100%, 63.7%)", + }, + { + category: "status", + name: "--color-success", + value: "hsl(163.2, 100%, 76.5%)", + }, + { + category: "status", + name: "--color-warning", + value: "hsl(86.0, 100%, 79.5%)", + }, + ], + }, + latestVersion: "beta", + light: { + colors: [ + { + category: "base", + name: "--color-background", + value: "hsl(0, 0%, 100%)", + }, + { + category: "semantic", + name: "--color-foreground", + value: "hsl(285.89, 5.9%, 21.03%)", + }, + { + category: "semantic", + name: "--color-accent", + value: "hsl(253.83, 100%, 62.04%)", + }, + { + category: "status", + name: "--color-danger", + value: "hsl(25.74, 100%, 65.32%)", + }, + { + category: "status", + name: "--color-success", + value: "hsl(150.81, 100%, 73.29%)", + }, + { + category: "status", + name: "--color-warning", + value: "hsl(72.33, 100%, 78.19%)", + }, + ], + }, + note: "This is a fallback. For complete theme variables, ensure the API is accessible.", + opacity: { + disabled: 0.4, + hover: 0.8, + pressed: 0.6, + }, + source: "fallback", + theme: "default", +}; + +/** + * Fetch data from HeroUI Native API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Format colors grouped by category. + */ +function formatColors(colors) { + const grouped = {}; + + for (const color of colors) { + const category = color.category || "semantic"; + + if (!grouped[category]) { + grouped[category] = []; + } + grouped[category].push(color); + } + + const lines = []; + + for (const [category, tokens] of Object.entries(grouped)) { + lines.push(` /* ${category.charAt(0).toUpperCase() + category.slice(1)} Colors */`); + for (const token of tokens) { + const name = token.name || ""; + const value = token.value || ""; + + lines.push(` ${name}: ${value};`); + } + lines.push(""); + } + + return lines.join("\n"); +} + +/** + * Main function to get theme variables. + */ +async function main() { + console.error("# Fetching Native theme variables..."); + + const rawData = await fetchApi("/v1/themes/variables?theme=default"); + + let data; + let version; + + if (!rawData) { + console.error("# API failed, using fallback theme reference..."); + data = FALLBACK_THEME; + version = FALLBACK_THEME.latestVersion || "unknown"; + } else { + // Handle API response format + data = rawData; + version = rawData.latestVersion || "unknown"; + } + + // Output as formatted structure for readability + console.log("/* HeroUI Native Theme Variables */"); + console.log(`/* Theme: ${data.theme || "default"} */`); + console.log(`/* Version: ${version} */`); + console.log(); + + // Light mode colors + if (data.light && data.light.colors) { + console.log("/* Light Mode Colors */"); + console.log(formatColors(data.light.colors)); + } + + // Dark mode colors + if (data.dark && data.dark.colors) { + console.log("/* Dark Mode Colors */"); + console.log(formatColors(data.dark.colors)); + } + + // Border radius + if (data.borderRadius) { + console.log("/* Border Radius */"); + for (const [key, value] of Object.entries(data.borderRadius)) { + console.log(` --radius-${key}: ${value};`); + } + console.log(); + } + + // Opacity + if (data.opacity) { + console.log("/* Opacity */"); + for (const [key, value] of Object.entries(data.opacity)) { + console.log(` --opacity-${key}: ${value};`); + } + console.log(); + } + + // Also output raw JSON to stderr for programmatic use + console.error("\n# Raw JSON output:"); + console.error(JSON.stringify(rawData || data, null, 2)); +} + +main(); diff --git a/.agents/skills/heroui-native/scripts/list_components.mjs b/.agents/skills/heroui-native/scripts/list_components.mjs new file mode 100755 index 0000000..5210eb4 --- /dev/null +++ b/.agents/skills/heroui-native/scripts/list_components.mjs @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/** + * List all available HeroUI Native components. + * + * Usage: + * node list_components.mjs + * + * Output: + * JSON with components array, latestVersion, and count + */ + +const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com"; +const APP_PARAM = "app=native-skills"; +const LLMS_TXT_URL = "https://heroui.com/native/llms.txt"; + +/** + * Fetch data from HeroUI Native API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`HTTP Error ${response.status}: ${response.statusText}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch component list from llms.txt fallback URL. + */ +async function fetchFallback() { + try { + const response = await fetch(LLMS_TXT_URL, { + headers: {"User-Agent": "HeroUI-Native-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return null; + } + + const content = await response.text(); + + // Parse markdown to extract component names from pattern: - [ComponentName](url) + // Look for links under the Components section (### Components) + const components = []; + let inComponentsSection = false; + + for (const line of content.split("\n")) { + // Check if we're entering the Components section (uses ### header) + if (line.trim() === "### Components") { + inComponentsSection = true; + continue; + } + + // Check if we're leaving the Components section (another ### header) + if (inComponentsSection && line.trim().startsWith("### ")) { + break; + } + + // Extract component name from markdown link pattern + // Match: - [ComponentName](https://www.heroui.com/docs/native/components/component-name) + // Skip "All Components" which links to /components without a specific component + if (inComponentsSection) { + const match = line.match( + /^\s*-\s*\[([^\]]+)\]\(https:\/\/www\.heroui\.com\/docs\/native\/components\/[a-z]/, + ); + + if (match) { + components.push(match[1]); + } + } + } + + if (components.length > 0) { + console.error(`# Using fallback: ${LLMS_TXT_URL}`); + + return { + components: components.sort(), + count: components.length, + latestVersion: "unknown", + }; + } + + return null; + } catch (error) { + console.error(`Fallback Error: ${error.message}`); + + return null; + } +} + +/** + * Main function to list all available HeroUI Native components. + */ +async function main() { + let data = await fetchApi("/v1/components"); + + // Check if API returned valid data with components + if (!data || !data.components || data.components.length === 0) { + console.error("# API returned no components, trying fallback..."); + data = await fetchFallback(); + } + + if (!data || !data.components || data.components.length === 0) { + console.error("Error: Failed to fetch component list from API and fallback"); + process.exit(1); + } + + // Output formatted JSON + console.log(JSON.stringify(data, null, 2)); + + // Print summary to stderr for human readability + console.error( + `\n# Found ${data.components.length} Native components (${data.latestVersion || "unknown"})`, + ); +} + +main(); diff --git a/.agents/skills/heroui-react/LICENSE.txt b/.agents/skills/heroui-react/LICENSE.txt new file mode 100644 index 0000000..bf4dfe2 --- /dev/null +++ b/.agents/skills/heroui-react/LICENSE.txt @@ -0,0 +1,199 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please also get an approval + from the project maintainers before using the Apache License. + + Copyright 2025 NextUI Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.agents/skills/heroui-react/SKILL.md b/.agents/skills/heroui-react/SKILL.md new file mode 100644 index 0000000..330da8f --- /dev/null +++ b/.agents/skills/heroui-react/SKILL.md @@ -0,0 +1,234 @@ +--- +name: heroui-react +description: "HeroUI v3 React component library (Tailwind CSS v4 + React Aria). Use when building UIs with HeroUI — creating Buttons, Modals, Forms, Cards; installing @heroui/react; configuring dark/light themes with oklch variables; or fetching component docs. Keywords: HeroUI, Hero UI, heroui, @heroui/react, @heroui/styles." +metadata: + author: heroui + version: "3.0.1" +--- + +# HeroUI v3 React Development Guide + +HeroUI v3 is a component library built on **Tailwind CSS v4** and **React Aria Components**, providing accessible, customizable UI components for React applications. + +--- + +## Installation + +```bash +curl -fsSL https://heroui.com/install | bash -s heroui-react +``` + +--- + +## CRITICAL: v3 Only - Ignore v2 Knowledge + +**This guide is for HeroUI v3 ONLY.** Do NOT apply v2 patterns — the provider, styling, and component API all changed: + +| Feature | v2 (DO NOT USE) | v3 (USE THIS) | +| ------------- | --------------------------------- | ------------------------------------------- | +| Provider | `` required | **No Provider needed** | +| Animations | `framer-motion` package | CSS-based, no extra deps | +| Component API | Flat props: `` | Compound: `` | +| Styling | Tailwind v3 + `@heroui/theme` | Tailwind v4 + `@heroui/styles` | +| Packages | `@heroui/system`, `@heroui/theme` | `@heroui/react`, `@heroui/styles` | + +```tsx +// DO NOT DO THIS - v2 pattern +import { HeroUIProvider } from "@heroui/react"; +import { motion } from "framer-motion"; + + + +; +``` + +### CORRECT (v3 patterns) + +```tsx +// DO THIS - v3 pattern (no provider, compound components) +import { Card } from "@heroui/react"; + + + + Product + A great product + +; +``` + +**Always fetch v3 docs before implementing.** + +--- + +## Core Principles + +- Semantic variants (`primary`, `secondary`, `tertiary`) over visual descriptions +- Composition over configuration (compound components) +- CSS variable-based theming with `oklch` color space +- BEM naming convention for predictable styling + +--- + +## Accessing Documentation & Component Information + +**For component details, examples, props, and implementation patterns, always fetch documentation:** + +### Using Scripts + +```bash +# List all available components +node scripts/list_components.mjs + +# Get component documentation (MDX) +node scripts/get_component_docs.mjs Button +node scripts/get_component_docs.mjs Button Card TextField + +# Get component source code +node scripts/get_source.mjs Button + +# Get component CSS styles (BEM classes) +node scripts/get_styles.mjs Button + +# Get theme variables +node scripts/get_theme.mjs + +# Get non-component docs (guides, releases) +node scripts/get_docs.mjs /docs/react/getting-started/theming +``` + +### Direct MDX URLs + +Component docs: `https://heroui.com/docs/react/components/{component-name}.mdx` + +Examples: + +- Button: `https://heroui.com/docs/react/components/button.mdx` +- Modal: `https://heroui.com/docs/react/components/modal.mdx` +- Form: `https://heroui.com/docs/react/components/form.mdx` + +Getting started guides: `https://heroui.com/docs/react/getting-started/{topic}.mdx` + +**Important:** Always fetch component docs before implementing. The MDX docs include complete examples, props, anatomy, and API references. + +--- + +## Installation Essentials + +### Quick Install + +```bash +npm i @heroui/styles @heroui/react tailwind-variants +``` + +### Framework Setup (Next.js App Router - Recommended) + +1. **Install dependencies:** + +```bash +npm i @heroui/styles @heroui/react tailwind-variants tailwindcss @tailwindcss/postcss postcss +``` + +2. **Create/update `app/globals.css`:** + +```css +/* Tailwind CSS v4 - Must be first */ +@import "tailwindcss"; + +/* HeroUI v3 styles - Must be after Tailwind */ +@import "@heroui/styles"; +``` + +3. **Import in `app/layout.tsx`:** + +```tsx +import "./globals.css"; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {/* No Provider needed in HeroUI v3! */} + {children} + + + ); +} +``` + +4. **Configure PostCSS (`postcss.config.mjs`):** + +```js +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; +``` + +### Critical Setup Requirements + +1. **Tailwind CSS v4 is MANDATORY** - HeroUI v3 will NOT work with Tailwind CSS v3 +2. **Use Compound Components** - Components use compound structure (e.g., `Card.Header`, `Card.Content`) +3. **Use onPress, not onClick** - For better accessibility, use `onPress` event handlers +4. **Import Order Matters** - Always import Tailwind CSS before HeroUI styles + +--- + +## Component Patterns + +All components use the **compound pattern** shown above (dot-notation subcomponents like `Card.Header`, `Card.Content`). Don't flatten to props — always compose with subcomponents. Fetch component docs for complete anatomy and examples. + +--- + +## Semantic Variants + +HeroUI uses semantic naming to communicate functional intent: + +| Variant | Purpose | Usage | +| ----------- | --------------------------------- | -------------- | +| `primary` | Main action to move forward | 1 per context | +| `secondary` | Alternative actions | Multiple | +| `tertiary` | Dismissive actions (cancel, skip) | Sparingly | +| `danger` | Destructive actions | When needed | +| `ghost` | Low-emphasis actions | Minimal weight | +| `outline` | Secondary actions | Bordered style | + +**Don't use raw colors** - semantic variants adapt to themes and accessibility. + +--- + +## Theming + +HeroUI v3 uses CSS variables with `oklch` color space: + +```css +:root { + --accent: oklch(0.6204 0.195 253.83); + --accent-foreground: var(--snow); + --background: oklch(0.9702 0 0); + --foreground: var(--eclipse); +} +``` + +**Get current theme variables:** + +```bash +node scripts/get_theme.mjs +``` + +**Color naming:** + +- Without suffix = background (e.g., `--accent`) +- With `-foreground` = text color (e.g., `--accent-foreground`) + +**Theme switching:** + +```html + +``` + +For detailed theming, fetch: `https://heroui.com/docs/react/getting-started/theming.mdx` diff --git a/.agents/skills/heroui-react/scripts/get_component_docs.mjs b/.agents/skills/heroui-react/scripts/get_component_docs.mjs new file mode 100644 index 0000000..41c6f05 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_component_docs.mjs @@ -0,0 +1,157 @@ +#!/usr/bin/env node +/** + * Get complete component documentation (MDX) for HeroUI v3 components. + * + * Usage: + * node get_component_docs.mjs Button + * node get_component_docs.mjs Button Card TextField + * + * Output: + * MDX documentation including imports, usage, variants, props, examples + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const FALLBACK_BASE = "https://heroui.com"; +const APP_PARAM = "app=react-skills"; + +/** + * Convert PascalCase to kebab-case. + */ +function toKebabCase(name) { + return name + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") + .toLowerCase(); +} + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + return null; + } + + return await response.json(); + } catch { + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(component) { + const kebabName = toKebabCase(component); + const url = `${FALLBACK_BASE}/docs/react/components/${kebabName}.mdx`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {component, error: `Failed to fetch docs for ${component}`}; + } + + const content = await response.text(); + + return { + component, + content, + contentType: "mdx", + source: "fallback", + url, + }; + } catch { + return {component, error: `Failed to fetch docs for ${component}`}; + } +} + +/** + * Main function to get component documentation. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_component_docs.mjs [Component2] ..."); + console.error("Example: node get_component_docs.mjs Button Card"); + process.exit(1); + } + + const components = args; + + // Try API first - use POST /v1/components/docs for batch requests + console.error(`# Fetching docs for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/docs", "POST", {components}); + + if (data && data.results) { + // Output results + if (data.results.length === 1) { + // Single component - output content directly for easier reading + const result = data.results[0]; + + if (result.content) { + console.log(result.content); + } else if (result.error) { + console.error(`# Error for ${result.component}: ${result.error}`); + console.log(JSON.stringify(result, null, 2)); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to individual component fetches + console.error("# API failed, using fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchFallback(component); + + results.push(result); + } + + // Output results + if (results.length === 1) { + // Single component - output content directly for easier reading + const result = results[0]; + + if (result.content) { + console.log(result.content); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(results, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_docs.mjs b/.agents/skills/heroui-react/scripts/get_docs.mjs new file mode 100644 index 0000000..8d3529b --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_docs.mjs @@ -0,0 +1,148 @@ +#!/usr/bin/env node +/** + * Get non-component HeroUI documentation (guides, theming, releases). + * + * Usage: + * node get_docs.mjs /docs/react/getting-started/theming + * node get_docs.mjs /docs/react/releases/v3-0-0-beta-3 + * + * Output: + * MDX documentation content + * + * Note: For component docs, use get_component_docs.mjs instead. + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const FALLBACK_BASE = "https://heroui.com"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch documentation from HeroUI API. + * Uses v1 endpoint pattern: /v1/docs/:path + */ +async function fetchApi(path) { + // The v1 API expects path without /docs/ prefix + // Input: /docs/react/getting-started/theming + // API expects: react/getting-started/theming (route is /v1/docs/:path(*)) + let apiPath = path.startsWith("/docs/") + ? path.slice(6) // Remove /docs/ prefix + : path.startsWith("/") + ? path.slice(1) // Remove leading / + : path; + + const separator = "?"; + const url = `${API_BASE}/v1/docs/${apiPath}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(path) { + // Ensure path starts with /docs and ends with .mdx + let cleanPath = path.replace(/^\//, ""); + + if (!cleanPath.endsWith(".mdx")) { + cleanPath = `${cleanPath}.mdx`; + } + + const url = `${FALLBACK_BASE}/${cleanPath}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {error: `HTTP ${response.status}: ${response.statusText}`, path}; + } + + const content = await response.text(); + + return { + content, + contentType: "mdx", + path, + source: "fallback", + url, + }; + } catch (error) { + return {error: `Fetch Error: ${error.message}`, path}; + } +} + +/** + * Main function to get documentation for specified path. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_docs.mjs "); + console.error("Example: node get_docs.mjs /docs/react/getting-started/theming"); + console.error(); + console.error("Available paths include:"); + console.error(" /docs/react/getting-started/theming"); + console.error(" /docs/react/getting-started/colors"); + console.error(" /docs/react/getting-started/animations"); + console.error(" /docs/react/releases/v3-0-0-beta-3"); + console.error(); + console.error("Note: For component docs, use get_component_docs.mjs instead."); + process.exit(1); + } + + const path = args[0]; + + // Check if user is trying to get component docs + if (path.includes("/components/")) { + console.error("# Warning: Use get_component_docs.mjs for component documentation."); + const componentName = path.split("/").pop().replace(".mdx", ""); + const titleCase = componentName.charAt(0).toUpperCase() + componentName.slice(1); + + console.error(`# Example: node get_component_docs.mjs ${titleCase}`); + } + + console.error(`# Fetching documentation for ${path}...`); + + // Try API first + const data = await fetchApi(path); + + if (data && data.content) { + data.source = "api"; + console.log(data.content); + + return; + } + + // Fallback to direct fetch + console.error("# API failed, using fallback..."); + const fallbackData = await fetchFallback(path); + + if (fallbackData.content) { + console.log(fallbackData.content); + } else { + console.log(JSON.stringify(fallbackData, null, 2)); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_source.mjs b/.agents/skills/heroui-react/scripts/get_source.mjs new file mode 100644 index 0000000..9dc3355 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_source.mjs @@ -0,0 +1,160 @@ +#!/usr/bin/env node +/** + * Get React/TypeScript source code implementation for HeroUI v3 components. + * + * Usage: + * node get_source.mjs Button + * node get_source.mjs Button Accordion Card + * + * Output: + * Full TSX source code with GitHub URL for each component + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch source code directly from GitHub as fallback. + */ +async function fetchGithubFallback(component) { + // Try common patterns for component paths + const patterns = [ + `packages/react/src/components/${component.toLowerCase()}/${component.toLowerCase()}.tsx`, + `packages/react/src/components/${component.toLowerCase()}/index.tsx`, + ]; + + for (const path of patterns) { + const url = `${GITHUB_RAW_BASE}/${path}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (response.ok) { + const content = await response.text(); + + return { + component, + filePath: path, + githubUrl: `https://github.com/heroui-inc/heroui/blob/v3/${path}`, + source: "fallback", + sourceCode: content, + }; + } + } catch { + continue; + } + } + + return {component, error: `Failed to fetch source for ${component}`}; +} + +/** + * Main function to get source code for specified components. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_source.mjs [Component2] ..."); + console.error("Example: node get_source.mjs Button Accordion"); + process.exit(1); + } + + const components = args; + + // Try API first + console.error(`# Fetching source code for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/source", "POST", {components}); + + if (data && data.results) { + for (const result of data.results) { + result.source = "api"; + } + + // Output results + if (data.results.length === 1) { + const result = data.results[0]; + + if (result.sourceCode) { + console.log(`// File: ${result.filePath || "unknown"}`); + console.log(`// GitHub: ${result.githubUrl || "unknown"}`); + console.log(); + console.log(result.sourceCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to GitHub direct fetch + console.error("# API failed, using GitHub fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchGithubFallback(component); + + results.push(result); + } + + if (results.length === 1) { + const result = results[0]; + + if (result.sourceCode) { + console.log(`// File: ${result.filePath || "unknown"}`); + console.log(`// GitHub: ${result.githubUrl || "unknown"}`); + console.log(); + console.log(result.sourceCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify({results}, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_styles.mjs b/.agents/skills/heroui-react/scripts/get_styles.mjs new file mode 100644 index 0000000..5323806 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_styles.mjs @@ -0,0 +1,160 @@ +#!/usr/bin/env node +/** + * Get CSS styles (BEM classes) for HeroUI v3 components. + * + * Usage: + * node get_styles.mjs Button + * node get_styles.mjs Button Card Chip + * + * Output: + * CSS file content with BEM classes and GitHub URL for each component + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch CSS styles directly from GitHub as fallback. + */ +async function fetchGithubFallback(component) { + // Try common patterns for style paths + const patterns = [ + `packages/styles/src/components/${component.toLowerCase()}.css`, + `packages/styles/components/${component.toLowerCase()}.css`, + ]; + + for (const path of patterns) { + const url = `${GITHUB_RAW_BASE}/${path}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (response.ok) { + const content = await response.text(); + + return { + component, + filePath: path, + githubUrl: `https://github.com/heroui-inc/heroui/blob/v3/${path}`, + source: "fallback", + stylesCode: content, + }; + } + } catch { + continue; + } + } + + return {component, error: `Failed to fetch styles for ${component}`}; +} + +/** + * Main function to get CSS styles for specified components. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_styles.mjs [Component2] ..."); + console.error("Example: node get_styles.mjs Button Card"); + process.exit(1); + } + + const components = args; + + // Try API first + console.error(`# Fetching styles for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/styles", "POST", {components}); + + if (data && data.results) { + for (const result of data.results) { + result.source = "api"; + } + + // Output results + if (data.results.length === 1) { + const result = data.results[0]; + + if (result.stylesCode) { + console.log(`/* File: ${result.filePath || "unknown"} */`); + console.log(`/* GitHub: ${result.githubUrl || "unknown"} */`); + console.log(); + console.log(result.stylesCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to GitHub direct fetch + console.error("# API failed, using GitHub fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchGithubFallback(component); + + results.push(result); + } + + if (results.length === 1) { + const result = results[0]; + + if (result.stylesCode) { + console.log(`/* File: ${result.filePath || "unknown"} */`); + console.log(`/* GitHub: ${result.githubUrl || "unknown"} */`); + console.log(); + console.log(result.stylesCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify({results}, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_theme.mjs b/.agents/skills/heroui-react/scripts/get_theme.mjs new file mode 100644 index 0000000..32bfe5e --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_theme.mjs @@ -0,0 +1,177 @@ +#!/usr/bin/env node +/** + * Get theme variables and design tokens for HeroUI v3. + * + * Usage: + * node get_theme.mjs + * + * Output: + * Theme variables organized by common/light/dark with oklch color format + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const APP_PARAM = "app=react-skills"; + +// Fallback theme reference when API is unavailable +const FALLBACK_THEME = { + common: { + base: [ + {name: "--font-sans", value: "ui-sans-serif, system-ui, sans-serif"}, + {name: "--font-mono", value: "ui-monospace, monospace"}, + {name: "--radius-sm", value: "0.375rem"}, + {name: "--radius-md", value: "0.5rem"}, + {name: "--radius-lg", value: "0.75rem"}, + {name: "--radius-full", value: "9999px"}, + ], + calculated: [{name: "--spacing-unit", value: "0.25rem"}], + }, + dark: { + semantic: [ + {name: "--color-background", value: "oklch(14.5% 0 0)"}, + {name: "--color-foreground", value: "oklch(98.4% 0 0)"}, + {name: "--color-accent", value: "oklch(55.1% 0.228 264.1)"}, + {name: "--color-danger", value: "oklch(63.7% 0.237 25.3)"}, + {name: "--color-success", value: "oklch(76.5% 0.177 163.2)"}, + {name: "--color-warning", value: "oklch(79.5% 0.184 86.0)"}, + ], + }, + latestVersion: "3.0.0-beta", + light: { + semantic: [ + {name: "--color-background", value: "oklch(100% 0 0)"}, + {name: "--color-foreground", value: "oklch(14.5% 0 0)"}, + {name: "--color-accent", value: "oklch(55.1% 0.228 264.1)"}, + {name: "--color-danger", value: "oklch(63.7% 0.237 25.3)"}, + {name: "--color-success", value: "oklch(76.5% 0.177 163.2)"}, + {name: "--color-warning", value: "oklch(79.5% 0.184 86.0)"}, + ], + }, + note: "This is a fallback. For complete theme variables, ensure the API is accessible.", + source: "fallback", + theme: "default", +}; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Format theme variables for display. + */ +function formatVariables(variables) { + const lines = []; + + for (const variable of variables) { + const name = variable.name || ""; + const value = variable.value || ""; + const desc = variable.description || ""; + + if (desc) { + lines.push(` ${name}: ${value}; /* ${desc} */`); + } else { + lines.push(` ${name}: ${value};`); + } + } + + return lines.join("\n"); +} + +/** + * Main function to get theme variables. + */ +async function main() { + console.error("# Fetching theme variables..."); + + const rawData = await fetchApi("/v1/themes/variables?theme=default"); + + let data; + let version; + + if (!rawData) { + console.error("# API failed, using fallback theme reference..."); + data = FALLBACK_THEME; + version = FALLBACK_THEME.latestVersion || "unknown"; + } else { + // Handle API response format: { themes: [...], latestVersion: "..." } + if (rawData.themes && rawData.themes.length > 0) { + data = rawData.themes[0]; // Get first theme (default) + version = rawData.latestVersion || rawData.version || "unknown"; + } else { + // Direct format + data = rawData; + version = rawData.latestVersion || "unknown"; + } + } + + // Output as formatted CSS-like structure for readability + console.log("/* HeroUI v3 Theme Variables */"); + console.log(`/* Theme: ${data.theme || "default"} */`); + console.log(`/* Version: ${version} */`); + console.log(); + + // Common variables + if (data.common) { + console.log(":root {"); + console.log(" /* Base Variables */"); + if (data.common.base) { + console.log(formatVariables(data.common.base)); + } + console.log(); + console.log(" /* Calculated Variables */"); + if (data.common.calculated) { + console.log(formatVariables(data.common.calculated)); + } + console.log("}"); + console.log(); + } + + // Light mode + if (data.light) { + console.log(":root, [data-theme='light'] {"); + console.log(" /* Light Mode Semantic Variables */"); + if (data.light.semantic) { + console.log(formatVariables(data.light.semantic)); + } + console.log("}"); + console.log(); + } + + // Dark mode + if (data.dark) { + console.log("[data-theme='dark'] {"); + console.log(" /* Dark Mode Semantic Variables */"); + if (data.dark.semantic) { + console.log(formatVariables(data.dark.semantic)); + } + console.log("}"); + } + + // Also output raw JSON to stderr for programmatic use + console.error("\n# Raw JSON output:"); + console.error(JSON.stringify(rawData || data, null, 2)); +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/list_components.mjs b/.agents/skills/heroui-react/scripts/list_components.mjs new file mode 100644 index 0000000..44eb499 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/list_components.mjs @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/** + * List all available HeroUI v3 components. + * + * Usage: + * node list_components.mjs + * + * Output: + * JSON with components array, latestVersion, and count + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const APP_PARAM = "app=react-skills"; +const LLMS_TXT_URL = "https://heroui.com/react/llms.txt"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`HTTP Error ${response.status}: ${response.statusText}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch component list from llms.txt fallback URL. + */ +async function fetchFallback() { + try { + const response = await fetch(LLMS_TXT_URL, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return null; + } + + const content = await response.text(); + + // Parse markdown to extract component names from pattern: - [ComponentName](url) + // Look for links under the Components section (### Components) + const components = []; + let inComponentsSection = false; + + for (const line of content.split("\n")) { + // Check if we're entering the Components section (uses ### header) + if (line.trim() === "### Components") { + inComponentsSection = true; + continue; + } + + // Check if we're leaving the Components section (another ### header) + if (inComponentsSection && line.trim().startsWith("### ")) { + break; + } + + // Extract component name from markdown link pattern + // Match: - [ComponentName](https://www.heroui.com/docs/react/components/component-name) + // Skip "All Components" which links to /components without a specific component + if (inComponentsSection) { + const match = line.match( + /^\s*-\s*\[([^\]]+)\]\(https:\/\/www\.heroui\.com\/docs\/react\/components\/[a-z]/, + ); + + if (match) { + components.push(match[1]); + } + } + } + + if (components.length > 0) { + console.error(`# Using fallback: ${LLMS_TXT_URL}`); + + return { + components: components.sort(), + count: components.length, + latestVersion: "unknown", + }; + } + + return null; + } catch (error) { + console.error(`Fallback Error: ${error.message}`); + + return null; + } +} + +/** + * Main function to list all available HeroUI v3 components. + */ +async function main() { + let data = await fetchApi("/v1/components"); + + // Check if API returned valid data with components + if (!data || !data.components || data.components.length === 0) { + console.error("# API returned no components, trying fallback..."); + data = await fetchFallback(); + } + + if (!data || !data.components || data.components.length === 0) { + console.error("Error: Failed to fetch component list from API and fallback"); + process.exit(1); + } + + // Output formatted JSON + console.log(JSON.stringify(data, null, 2)); + + // Print summary to stderr for human readability + console.error( + `\n# Found ${data.components.length} components (${data.latestVersion || "unknown"})`, + ); +} + +main(); diff --git a/.claude/skills/heroui-migration b/.claude/skills/heroui-migration new file mode 120000 index 0000000..00c642b --- /dev/null +++ b/.claude/skills/heroui-migration @@ -0,0 +1 @@ +../../.agents/skills/heroui-migration \ No newline at end of file diff --git a/.claude/skills/heroui-native b/.claude/skills/heroui-native new file mode 120000 index 0000000..8afe222 --- /dev/null +++ b/.claude/skills/heroui-native @@ -0,0 +1 @@ +../../.agents/skills/heroui-native \ No newline at end of file diff --git a/.claude/skills/heroui-react b/.claude/skills/heroui-react new file mode 120000 index 0000000..413da29 --- /dev/null +++ b/.claude/skills/heroui-react @@ -0,0 +1 @@ +../../.agents/skills/heroui-react \ No newline at end of file diff --git a/.mcp.json b/.mcp.json index bd98b4f..7319261 100644 --- a/.mcp.json +++ b/.mcp.json @@ -6,6 +6,16 @@ "shadcn@latest", "mcp" ] + }, + "XcodeBuildMCP": { + "type": "stdio", + "command": "npx", + "args": [ + "-y", + "xcodebuildmcp@latest", + "mcp" + ], + "env": {} } } -} +} \ No newline at end of file diff --git a/frontend/components/appointments/add-appointment-dialog.tsx b/frontend/components/appointments/add-appointment-dialog.tsx index 3bf4b00..5a7f25e 100644 --- a/frontend/components/appointments/add-appointment-dialog.tsx +++ b/frontend/components/appointments/add-appointment-dialog.tsx @@ -51,6 +51,14 @@ const TYPES = [ const controlClass = "h-9 w-full rounded-3xl border border-transparent bg-input/50 px-3 text-sm text-foreground outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30"; +// Local start-of-day so the calendar can disable days strictly before today +// (appointments can't be scheduled in the past). +const startOfToday = () => { + const d = new Date(); + d.setHours(0, 0, 0, 0); + return d; +}; + function Field({ label, children }: { label: string; children: ReactNode }) { return (