@intlayer/design-system 9.5.3 → 9.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +6 -6
  2. package/dist/esm/api/useAuth/useOAuth2.mjs +1 -1
  3. package/dist/esm/api/useAuth/useSession.mjs +2 -2
  4. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs +6 -6
  5. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs.map +1 -1
  6. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs +11 -11
  7. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs.map +1 -1
  8. package/dist/esm/components/Form/FormBase.mjs +17 -3
  9. package/dist/esm/components/Form/FormBase.mjs.map +1 -1
  10. package/dist/esm/components/Form/elements/FormElement.mjs +4 -1
  11. package/dist/esm/components/Form/elements/FormElement.mjs.map +1 -1
  12. package/dist/esm/components/Form/elements/FormElementWrapper.mjs +2 -2
  13. package/dist/esm/components/Form/elements/FormElementWrapper.mjs.map +1 -1
  14. package/dist/esm/components/Form/index.mjs +2 -2
  15. package/dist/esm/components/Status/index.mjs +2 -5
  16. package/dist/esm/components/Status/index.mjs.map +1 -1
  17. package/dist/esm/components/index.mjs +2 -2
  18. package/dist/esm/hooks/index.mjs +10 -8
  19. package/dist/esm/hooks/useRemoteMcpTools.mjs +56 -0
  20. package/dist/esm/hooks/useRemoteMcpTools.mjs.map +1 -0
  21. package/dist/esm/hooks/useWebMCPTools.mjs +44 -0
  22. package/dist/esm/hooks/useWebMCPTools.mjs.map +1 -0
  23. package/dist/esm/providers/index.mjs +1 -1
  24. package/dist/esm/structured-data/index.mjs +6 -6
  25. package/dist/esm/webmcp/createRemoteMcpTools.mjs +65 -0
  26. package/dist/esm/webmcp/createRemoteMcpTools.mjs.map +1 -0
  27. package/dist/esm/webmcp/getModelContext.mjs +28 -0
  28. package/dist/esm/webmcp/getModelContext.mjs.map +1 -0
  29. package/dist/esm/webmcp/index.mjs +5 -0
  30. package/dist/esm/webmcp/registerWebMCPTools.mjs +54 -0
  31. package/dist/esm/webmcp/registerWebMCPTools.mjs.map +1 -0
  32. package/dist/esm/webmcp/types.mjs +0 -0
  33. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts +7 -7
  34. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts.map +1 -1
  35. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.d.ts +10 -10
  36. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.d.ts.map +1 -1
  37. package/dist/types/components/Form/FormBase.d.ts +27 -4
  38. package/dist/types/components/Form/FormBase.d.ts.map +1 -1
  39. package/dist/types/components/Form/elements/FormElement.d.ts +1 -0
  40. package/dist/types/components/Form/elements/FormElement.d.ts.map +1 -1
  41. package/dist/types/components/Form/elements/FormElementWrapper.d.ts +3 -3
  42. package/dist/types/components/Form/index.d.ts +2 -2
  43. package/dist/types/components/index.d.ts +2 -2
  44. package/dist/types/hooks/index.d.ts +3 -1
  45. package/dist/types/hooks/useRemoteMcpTools.d.ts +18 -0
  46. package/dist/types/hooks/useRemoteMcpTools.d.ts.map +1 -0
  47. package/dist/types/hooks/useWebMCPTools.d.ts +22 -0
  48. package/dist/types/hooks/useWebMCPTools.d.ts.map +1 -0
  49. package/dist/types/webmcp/createRemoteMcpTools.d.ts +52 -0
  50. package/dist/types/webmcp/createRemoteMcpTools.d.ts.map +1 -0
  51. package/dist/types/webmcp/getModelContext.d.ts +18 -0
  52. package/dist/types/webmcp/getModelContext.d.ts.map +1 -0
  53. package/dist/types/webmcp/index.d.ts +5 -0
  54. package/dist/types/webmcp/registerWebMCPTools.d.ts +19 -0
  55. package/dist/types/webmcp/registerWebMCPTools.d.ts.map +1 -0
  56. package/dist/types/webmcp/types.d.ts +74 -0
  57. package/dist/types/webmcp/types.d.ts.map +1 -0
  58. package/package.json +33 -27
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://intlayer.org" rel="">
3
- <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.png" width="60%" alt="Intlayer Logo" />
3
+ <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.webp" width="60%" alt="Intlayer Logo" />
4
4
  </a>
5
5
  </p>
6
6
 
@@ -48,7 +48,7 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
48
48
 
49
49
  | Feature | Description |
50
50
  | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.png?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
51
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.webp?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
52
52
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/javascript_content_management.jpg?raw=true" alt="Feature" width="700"> | **JavaScript-Powered Content Management**<br><br>Harness the flexibility of JavaScript to define and manage your content efficiently. <br><br> - [Content declaration](https://intlayer.org/doc/concept/content) |
53
53
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/per_locale_content_declaration_file.png?raw=true" alt="Feature" width="700"> | **Per-Locale Content Declaration File**<br><br>Speed up your development by declaring your content once, before auto generation.<br><br> - [Per-Locale Content Declaration File](https://intlayer.org/doc/concept/per-locale-file) |
54
54
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true" alt="Feature" width="700"> | **Type-Safe Environment**<br><br>Leverage TypeScript to ensure your content definitions and code are error-free, while also benefiting from IDE autocompletion.<br><br> - [TypeScript configuration](https://intlayer.org/doc/environment/vite-and-react#configure-typescript) |
@@ -58,12 +58,12 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
58
58
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/file_tree.png?raw=true" alt="Feature" width="700"> | **Organized Codebase**<br><br>Keep your codebase more organized: 1 component = 1 dictionary in the same folder. Translations close to their respective components, enhance maintainability and clarity. <br><br> - [How Intlayer works](https://intlayer.org/doc/concept/how-works-intlayer) |
59
59
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/url_routing.png?raw=true" alt="Feature" width="700"> | **Enhanced Routing**<br><br>Full support of app routing, adapting seamlessly to complex application structures, for Next.js, React, Vite, Vue.js, etc.<br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
60
60
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/markdown.png?raw=true" alt="Feature" width="700"> | **Markdown Support**<br><br>Import and interpret, locale files and remote Markdown for multilingual content like privacy policies, documentation, etc. Interpret and make Markdown metadata accessible in your code.<br><br> - [Content files](https://intlayer.org/doc/concept/content/file) |
61
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.png?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
62
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
63
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.png?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
61
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.webp?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
62
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.webp?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
63
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.webp?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
64
64
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/AI_translation.png?raw=true" alt="Feature" width="700"> | **AI-Powered Translation**<br><br>Transform your website into 231 languages with just one click using Intlayer's advanced AI-powered translation tools using your own AI provider / API key. <br><br> - [CI/CD integration](https://intlayer.org/doc/concept/ci-cd) <br> - [Intlayer CLI](https://intlayer.org/doc/concept/cli) <br> - [Auto fill](https://intlayer.org/doc/concept/auto-fill) |
65
65
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/mcp.png?raw=true" alt="Feature" width="700"> | **MCP Server Integration**<br><br>Provides an MCP (Model Context Protocol) server for IDE automation, enabling seamless content management and i18n workflows directly within your development environment. <br><br> - [MCP Server](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md) |
66
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.png?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
66
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.webp?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
67
67
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, and react-intl. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer compat adapters](https://intlayer.org/doc/compatibility) |
68
68
 
69
69
  ## 📦 Installation
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { useIsAuthFetchEnabled } from "../../providers/DeferredAuthProvider.mjs";
4
+ import { editor } from "@intlayer/config/built";
4
5
  import { useQuery } from "@tanstack/react-query";
5
6
  import { getOAuthAPI } from "@intlayer/api";
6
7
  import { useConfiguration } from "@intlayer/editor-react";
7
- import { editor } from "@intlayer/config/built";
8
8
  import { defu } from "defu";
9
9
 
10
10
  //#region src/api/useAuth/useOAuth2.ts
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { useIsAuthFetchEnabled } from "../../providers/DeferredAuthProvider.mjs";
4
3
  import { getAuthAPI } from "../../libs/auth.mjs";
4
+ import { useIsAuthFetchEnabled } from "../../providers/DeferredAuthProvider.mjs";
5
+ import { editor } from "@intlayer/config/built";
5
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
6
7
  import { useConfiguration } from "@intlayer/editor-react";
7
- import { editor } from "@intlayer/config/built";
8
8
 
9
9
  //#region src/api/useAuth/useSession.ts
10
10
  const useSession = (sessionProp, intlayerConfiguration) => {
@@ -1,5 +1,5 @@
1
1
  import { useIntlayer } from "react-intlayer";
2
- import { z } from "zod";
2
+ import { z } from "zod/mini";
3
3
 
4
4
  //#region src/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.ts
5
5
  const QUALIFIER_TYPES = [
@@ -10,11 +10,11 @@ const QUALIFIER_TYPES = [
10
10
  const useDictionarySchema = (projectId) => {
11
11
  const { requiredErrorName, invalidTypeErrorName, requiredErrorProjectId, invalidTypeErrorProjectId } = useIntlayer("dictionary-form-schema");
12
12
  return z.object({
13
- key: z.string({ error: (issue) => issue.input === void 0 ? requiredErrorName.value : invalidTypeErrorName.value }).min(1, { error: invalidTypeErrorName.value }).default(""),
14
- projectIds: z.array(z.string({ error: (issue) => issue.input === void 0 ? requiredErrorProjectId.value : invalidTypeErrorProjectId.value })).default([projectId]),
15
- qualifierType: z.enum(QUALIFIER_TYPES).default("none"),
16
- item: z.coerce.number().int().positive().optional(),
17
- variant: z.string().optional()
13
+ key: z._default(z.string({ error: (issue) => issue.input === void 0 ? requiredErrorName.value : invalidTypeErrorName.value }).check(z.minLength(1, { error: invalidTypeErrorName.value })), ""),
14
+ projectIds: z._default(z.array(z.string({ error: (issue) => issue.input === void 0 ? requiredErrorProjectId.value : invalidTypeErrorProjectId.value })), [projectId]),
15
+ qualifierType: z._default(z.enum(QUALIFIER_TYPES), "none"),
16
+ item: z.optional(z.coerce.number().check(z.int(), z.positive())),
17
+ variant: z.optional(z.string())
18
18
  });
19
19
  };
20
20
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryFormSchema.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.ts"],"sourcesContent":["import { useIntlayer } from 'react-intlayer';\nimport { z } from 'zod';\n\nexport const QUALIFIER_TYPES = ['none', 'item', 'variant'] as const;\nexport type QualifierType = (typeof QUALIFIER_TYPES)[number];\n\nexport const useDictionarySchema = (projectId: string) => {\n const {\n requiredErrorName,\n invalidTypeErrorName,\n requiredErrorProjectId,\n invalidTypeErrorProjectId,\n } = useIntlayer('dictionary-form-schema');\n\n return z.object({\n key: z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorName.value\n : invalidTypeErrorName.value,\n })\n .min(1, { error: invalidTypeErrorName.value })\n .default(''),\n projectIds: z\n .array(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorProjectId.value\n : invalidTypeErrorProjectId.value,\n })\n )\n .default([projectId]),\n qualifierType: z.enum(QUALIFIER_TYPES).default('none'),\n item: z.coerce.number().int().positive().optional(),\n variant: z.string().optional(),\n });\n};\n\nexport type DictionaryFormData = z.infer<\n ReturnType<typeof useDictionarySchema>\n>;\n"],"mappings":";;;;AAGA,MAAa,kBAAkB;CAAC;CAAQ;CAAQ;AAAS;AAGzD,MAAa,uBAAuB,cAAsB;CACxD,MAAM,EACJ,mBACA,sBACA,wBACA,8BACE,YAAY,wBAAwB;CAExC,OAAO,EAAE,OAAO;EACd,KAAK,EACF,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,kBAAkB,QAClB,qBAAqB,MAC7B,CAAC,CAAC,CACD,IAAI,GAAG,EAAE,OAAO,qBAAqB,MAAM,CAAC,CAAC,CAC7C,QAAQ,EAAE;EACb,YAAY,EACT,MACC,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,uBAAuB,QACvB,0BAA0B,MAClC,CAAC,CACH,CAAC,CACA,QAAQ,CAAC,SAAS,CAAC;EACtB,eAAe,EAAE,KAAK,eAAe,CAAC,CAAC,QAAQ,MAAM;EACrD,MAAM,EAAE,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;EAClD,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,CAAC;AACH"}
1
+ {"version":3,"file":"useDictionaryFormSchema.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.ts"],"sourcesContent":["import { useIntlayer } from 'react-intlayer';\nimport { z } from 'zod/mini';\n\nexport const QUALIFIER_TYPES = ['none', 'item', 'variant'] as const;\nexport type QualifierType = (typeof QUALIFIER_TYPES)[number];\n\nexport const useDictionarySchema = (projectId: string) => {\n const {\n requiredErrorName,\n invalidTypeErrorName,\n requiredErrorProjectId,\n invalidTypeErrorProjectId,\n } = useIntlayer('dictionary-form-schema');\n\n return z.object({\n key: z._default(\n z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorName.value\n : invalidTypeErrorName.value,\n })\n .check(z.minLength(1, { error: invalidTypeErrorName.value })),\n ''\n ),\n projectIds: z._default(\n z.array(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorProjectId.value\n : invalidTypeErrorProjectId.value,\n })\n ),\n [projectId]\n ),\n qualifierType: z._default(z.enum(QUALIFIER_TYPES), 'none'),\n item: z.optional(z.coerce.number().check(z.int(), z.positive())),\n variant: z.optional(z.string()),\n });\n};\n\nexport type DictionaryFormData = z.infer<\n ReturnType<typeof useDictionarySchema>\n>;\n"],"mappings":";;;;AAGA,MAAa,kBAAkB;CAAC;CAAQ;CAAQ;AAAS;AAGzD,MAAa,uBAAuB,cAAsB;CACxD,MAAM,EACJ,mBACA,sBACA,wBACA,8BACE,YAAY,wBAAwB;CAExC,OAAO,EAAE,OAAO;EACd,KAAK,EAAE,SACL,EACG,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,kBAAkB,QAClB,qBAAqB,MAC7B,CAAC,CAAC,CACD,MAAM,EAAE,UAAU,GAAG,EAAE,OAAO,qBAAqB,MAAM,CAAC,CAAC,GAC9D,EACF;EACA,YAAY,EAAE,SACZ,EAAE,MACA,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,uBAAuB,QACvB,0BAA0B,MAClC,CAAC,CACH,GACA,CAAC,SAAS,CACZ;EACA,eAAe,EAAE,SAAS,EAAE,KAAK,eAAe,GAAG,MAAM;EACzD,MAAM,EAAE,SAAS,EAAE,OAAO,OAAO,CAAC,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,SAAS,CAAC,CAAC;EAC/D,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC;CAChC,CAAC;AACH"}
@@ -1,27 +1,27 @@
1
1
  import { useIntlayer } from "react-intlayer";
2
- import { z } from "zod";
2
+ import { z } from "zod/mini";
3
3
 
4
4
  //#region src/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.ts
5
5
  const useDictionaryDetailsSchema = (projectId) => {
6
6
  const { titleRequiredError, titleInvalidTypeError, titleMinLengthError, keyRequiredError, keyInvalidTypeError, keySpaceError, keyMinLengthError, descriptionRequiredError, descriptionInvalidTypeError, requiredErrorProjectId, invalidTypeErrorProjectId, invalidTypeErrorTags, locationRequiredError, locationInvalidTypeError, filePathRequiredError, filePathInvalidTypeError } = useIntlayer("dictionary-detail-schema");
7
7
  return z.object({
8
- title: z.string({ error: (issue) => issue.input === void 0 ? titleRequiredError.value : titleInvalidTypeError.value }).refine((val) => val.length === 0 || val.length >= 4, { error: titleMinLengthError.value }).optional(),
9
- key: z.string({ error: (issue) => issue.input === void 0 ? keyRequiredError.value : keyInvalidTypeError.value }).min(4, { error: keyMinLengthError.value }).regex(/^[a-zA-Z0-9-_]+$/, { error: keySpaceError.value }).default(""),
10
- description: z.string({ error: (issue) => issue.input === void 0 ? descriptionRequiredError.value : descriptionInvalidTypeError.value }).optional(),
11
- projectIds: z.array(z.string({ error: (issue) => issue.input === void 0 ? requiredErrorProjectId.value : invalidTypeErrorProjectId.value })).default([projectId]),
12
- tags: z.array(z.string({ error: () => invalidTypeErrorTags.value })).default([]),
13
- location: z.enum([
8
+ title: z.optional(z.string({ error: (issue) => issue.input === void 0 ? titleRequiredError.value : titleInvalidTypeError.value }).check(z.refine((val) => val.length === 0 || val.length >= 4, { error: titleMinLengthError.value }))),
9
+ key: z._default(z.string({ error: (issue) => issue.input === void 0 ? keyRequiredError.value : keyInvalidTypeError.value }).check(z.minLength(4, { error: keyMinLengthError.value }), z.regex(/^[a-zA-Z0-9-_]+$/, { error: keySpaceError.value })), ""),
10
+ description: z.optional(z.string({ error: (issue) => issue.input === void 0 ? descriptionRequiredError.value : descriptionInvalidTypeError.value })),
11
+ projectIds: z._default(z.array(z.string({ error: (issue) => issue.input === void 0 ? requiredErrorProjectId.value : invalidTypeErrorProjectId.value })), [projectId]),
12
+ tags: z._default(z.array(z.string({ error: () => invalidTypeErrorTags.value })), []),
13
+ location: z.optional(z.enum([
14
14
  "local",
15
15
  "remote",
16
16
  "hybrid",
17
17
  "plugin"
18
- ], { error: (issue) => issue.input === void 0 ? locationRequiredError.value : locationInvalidTypeError.value }).optional(),
19
- importMode: z.enum([
18
+ ], { error: (issue) => issue.input === void 0 ? locationRequiredError.value : locationInvalidTypeError.value })),
19
+ importMode: z.optional(z.enum([
20
20
  "static",
21
21
  "dynamic",
22
22
  "fetch"
23
- ]).optional(),
24
- filePath: z.string({ error: (issue) => issue.input === void 0 ? filePathRequiredError.value : filePathInvalidTypeError.value }).optional()
23
+ ])),
24
+ filePath: z.optional(z.string({ error: (issue) => issue.input === void 0 ? filePathRequiredError.value : filePathInvalidTypeError.value }))
25
25
  });
26
26
  };
27
27
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDetailsSchema.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.ts"],"sourcesContent":["import { useIntlayer } from 'react-intlayer';\nimport { z } from 'zod';\n\nexport const useDictionaryDetailsSchema = (projectId: string) => {\n const {\n titleRequiredError,\n titleInvalidTypeError,\n titleMinLengthError,\n keyRequiredError,\n keyInvalidTypeError,\n keySpaceError,\n keyMinLengthError,\n descriptionRequiredError,\n descriptionInvalidTypeError,\n requiredErrorProjectId,\n invalidTypeErrorProjectId,\n invalidTypeErrorTags,\n locationRequiredError,\n locationInvalidTypeError,\n filePathRequiredError,\n filePathInvalidTypeError,\n } = useIntlayer('dictionary-detail-schema');\n\n return z.object({\n title: z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? titleRequiredError.value\n : titleInvalidTypeError.value,\n })\n // Can be length of 0 or > 4\n .refine((val) => val.length === 0 || val.length >= 4, {\n error: titleMinLengthError.value,\n })\n .optional(),\n key: z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? keyRequiredError.value\n : keyInvalidTypeError.value,\n })\n .min(4, { error: keyMinLengthError.value })\n /**\n * Valid :\n * my-key\n * my_key\n *\n * Invalid :\n * my key\n * my.key\n */\n .regex(/^[a-zA-Z0-9-_]+$/, { error: keySpaceError.value })\n .default(''),\n description: z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? descriptionRequiredError.value\n : descriptionInvalidTypeError.value,\n })\n .optional(),\n projectIds: z\n .array(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorProjectId.value\n : invalidTypeErrorProjectId.value,\n })\n )\n .default([projectId]),\n tags: z\n .array(\n z.string({\n error: () => invalidTypeErrorTags.value,\n })\n )\n .default([]),\n location: z\n .enum(['local', 'remote', 'hybrid', 'plugin'], {\n error: (issue) =>\n issue.input === undefined\n ? locationRequiredError.value\n : locationInvalidTypeError.value,\n })\n .optional(),\n importMode: z.enum(['static', 'dynamic', 'fetch']).optional(),\n filePath: z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? filePathRequiredError.value\n : filePathInvalidTypeError.value,\n })\n .optional(),\n });\n};\n\nexport type DictionaryDetailsFormData = z.infer<\n ReturnType<typeof useDictionaryDetailsSchema>\n>;\n"],"mappings":";;;;AAGA,MAAa,8BAA8B,cAAsB;CAC/D,MAAM,EACJ,oBACA,uBACA,qBACA,kBACA,qBACA,eACA,mBACA,0BACA,6BACA,wBACA,2BACA,sBACA,uBACA,0BACA,uBACA,6BACE,YAAY,0BAA0B;CAE1C,OAAO,EAAE,OAAO;EACd,OAAO,EACJ,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,mBAAmB,QACnB,sBAAsB,MAC9B,CAAC,CAAC,CAED,QAAQ,QAAQ,IAAI,WAAW,KAAK,IAAI,UAAU,GAAG,EACpD,OAAO,oBAAoB,MAC7B,CAAC,CAAC,CACD,SAAS;EACZ,KAAK,EACF,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,iBAAiB,QACjB,oBAAoB,MAC5B,CAAC,CAAC,CACD,IAAI,GAAG,EAAE,OAAO,kBAAkB,MAAM,CAAC,CAAC,CAU1C,MAAM,oBAAoB,EAAE,OAAO,cAAc,MAAM,CAAC,CAAC,CACzD,QAAQ,EAAE;EACb,aAAa,EACV,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,yBAAyB,QACzB,4BAA4B,MACpC,CAAC,CAAC,CACD,SAAS;EACZ,YAAY,EACT,MACC,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,uBAAuB,QACvB,0BAA0B,MAClC,CAAC,CACH,CAAC,CACA,QAAQ,CAAC,SAAS,CAAC;EACtB,MAAM,EACH,MACC,EAAE,OAAO,EACP,aAAa,qBAAqB,MACpC,CAAC,CACH,CAAC,CACA,QAAQ,CAAC,CAAC;EACb,UAAU,EACP,KAAK;GAAC;GAAS;GAAU;GAAU;EAAQ,GAAG,EAC7C,QAAQ,UACN,MAAM,UAAU,SACZ,sBAAsB,QACtB,yBAAyB,MACjC,CAAC,CAAC,CACD,SAAS;EACZ,YAAY,EAAE,KAAK;GAAC;GAAU;GAAW;EAAO,CAAC,CAAC,CAAC,SAAS;EAC5D,UAAU,EACP,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,sBAAsB,QACtB,yBAAyB,MACjC,CAAC,CAAC,CACD,SAAS;CACd,CAAC;AACH"}
1
+ {"version":3,"file":"useDictionaryDetailsSchema.mjs","names":[],"sources":["../../../../../src/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.ts"],"sourcesContent":["import { useIntlayer } from 'react-intlayer';\nimport { z } from 'zod/mini';\n\nexport const useDictionaryDetailsSchema = (projectId: string) => {\n const {\n titleRequiredError,\n titleInvalidTypeError,\n titleMinLengthError,\n keyRequiredError,\n keyInvalidTypeError,\n keySpaceError,\n keyMinLengthError,\n descriptionRequiredError,\n descriptionInvalidTypeError,\n requiredErrorProjectId,\n invalidTypeErrorProjectId,\n invalidTypeErrorTags,\n locationRequiredError,\n locationInvalidTypeError,\n filePathRequiredError,\n filePathInvalidTypeError,\n } = useIntlayer('dictionary-detail-schema');\n\n return z.object({\n title: z.optional(\n z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? titleRequiredError.value\n : titleInvalidTypeError.value,\n })\n .check(\n z.refine((val) => val.length === 0 || val.length >= 4, {\n error: titleMinLengthError.value,\n })\n )\n ),\n key: z._default(\n z\n .string({\n error: (issue) =>\n issue.input === undefined\n ? keyRequiredError.value\n : keyInvalidTypeError.value,\n })\n .check(\n z.minLength(4, { error: keyMinLengthError.value }),\n z.regex(/^[a-zA-Z0-9-_]+$/, { error: keySpaceError.value })\n ),\n ''\n ),\n description: z.optional(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? descriptionRequiredError.value\n : descriptionInvalidTypeError.value,\n })\n ),\n projectIds: z._default(\n z.array(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? requiredErrorProjectId.value\n : invalidTypeErrorProjectId.value,\n })\n ),\n [projectId]\n ),\n tags: z._default(\n z.array(\n z.string({\n error: () => invalidTypeErrorTags.value,\n })\n ),\n []\n ),\n location: z.optional(\n z.enum(['local', 'remote', 'hybrid', 'plugin'], {\n error: (issue) =>\n issue.input === undefined\n ? locationRequiredError.value\n : locationInvalidTypeError.value,\n })\n ),\n importMode: z.optional(z.enum(['static', 'dynamic', 'fetch'])),\n filePath: z.optional(\n z.string({\n error: (issue) =>\n issue.input === undefined\n ? filePathRequiredError.value\n : filePathInvalidTypeError.value,\n })\n ),\n });\n};\n\nexport type DictionaryDetailsFormData = z.infer<\n ReturnType<typeof useDictionaryDetailsSchema>\n>;\n"],"mappings":";;;;AAGA,MAAa,8BAA8B,cAAsB;CAC/D,MAAM,EACJ,oBACA,uBACA,qBACA,kBACA,qBACA,eACA,mBACA,0BACA,6BACA,wBACA,2BACA,sBACA,uBACA,0BACA,uBACA,6BACE,YAAY,0BAA0B;CAE1C,OAAO,EAAE,OAAO;EACd,OAAO,EAAE,SACP,EACG,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,mBAAmB,QACnB,sBAAsB,MAC9B,CAAC,CAAC,CACD,MACC,EAAE,QAAQ,QAAQ,IAAI,WAAW,KAAK,IAAI,UAAU,GAAG,EACrD,OAAO,oBAAoB,MAC7B,CAAC,CACH,CACJ;EACA,KAAK,EAAE,SACL,EACG,OAAO,EACN,QAAQ,UACN,MAAM,UAAU,SACZ,iBAAiB,QACjB,oBAAoB,MAC5B,CAAC,CAAC,CACD,MACC,EAAE,UAAU,GAAG,EAAE,OAAO,kBAAkB,MAAM,CAAC,GACjD,EAAE,MAAM,oBAAoB,EAAE,OAAO,cAAc,MAAM,CAAC,CAC5D,GACF,EACF;EACA,aAAa,EAAE,SACb,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,yBAAyB,QACzB,4BAA4B,MACpC,CAAC,CACH;EACA,YAAY,EAAE,SACZ,EAAE,MACA,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,uBAAuB,QACvB,0BAA0B,MAClC,CAAC,CACH,GACA,CAAC,SAAS,CACZ;EACA,MAAM,EAAE,SACN,EAAE,MACA,EAAE,OAAO,EACP,aAAa,qBAAqB,MACpC,CAAC,CACH,GACA,CAAC,CACH;EACA,UAAU,EAAE,SACV,EAAE,KAAK;GAAC;GAAS;GAAU;GAAU;EAAQ,GAAG,EAC9C,QAAQ,UACN,MAAM,UAAU,SACZ,sBAAsB,QACtB,yBAAyB,MACjC,CAAC,CACH;EACA,YAAY,EAAE,SAAS,EAAE,KAAK;GAAC;GAAU;GAAW;EAAO,CAAC,CAAC;EAC7D,UAAU,EAAE,SACV,EAAE,OAAO,EACP,QAAQ,UACN,MAAM,UAAU,SACZ,sBAAsB,QACtB,yBAAyB,MACjC,CAAC,CACH;CACF,CAAC;AACH"}
@@ -6,13 +6,22 @@ import { FormProvider, useForm as useForm$1, useFormState } from "react-hook-for
6
6
  import { zodResolver } from "@hookform/resolvers/zod";
7
7
 
8
8
  //#region src/components/Form/FormBase.tsx
9
+ /**
10
+ * Maps the camelCase props onto the lowercase attributes the browser reads.
11
+ * Typed as a plain record: React's JSX types do not know these attributes yet.
12
+ */
13
+ const getFormWebMCPAttributes = ({ toolName, toolDescription, toolAutoSubmit }) => toolName ? {
14
+ toolname: toolName,
15
+ tooldescription: toolDescription,
16
+ toolautosubmit: toolAutoSubmit ? "" : void 0
17
+ } : {};
9
18
  const awaitFunction = async (fn) => {
10
19
  if (fn && typeof fn.then === "function") return await fn;
11
20
  return fn;
12
21
  };
13
- const Form = ({ schema, onSubmit: onSubmitProp, onSubmitSuccess: onSubmitSuccessProp, onSubmitError: onSubmitErrorProp, className, children, autoComplete, method, ...props }) => {
22
+ const Form = ({ schema, onSubmit: onSubmitProp, onSubmitSuccess: onSubmitSuccessProp, onSubmitError: onSubmitErrorProp, className, children, autoComplete, method, toolName, toolDescription, toolAutoSubmit, ...props }) => {
14
23
  const onSubmit = async (values) => {
15
- const parsedValues = schema?.safeParse(values) ?? {
24
+ const parsedValues = schema.safeParse(values) ?? {
16
25
  success: true,
17
26
  data: void 0
18
27
  };
@@ -39,6 +48,11 @@ const Form = ({ schema, onSubmit: onSubmitProp, onSubmitSuccess: onSubmitSuccess
39
48
  autoComplete: autoComplete ? "on" : "off",
40
49
  noValidate: true,
41
50
  method,
51
+ ...getFormWebMCPAttributes({
52
+ toolName,
53
+ toolDescription,
54
+ toolAutoSubmit
55
+ }),
42
56
  children
43
57
  })
44
58
  });
@@ -59,5 +73,5 @@ const useForm = (schema, props) => {
59
73
  };
60
74
 
61
75
  //#endregion
62
- export { Form, useForm };
76
+ export { Form, getFormWebMCPAttributes, useForm };
63
77
  //# sourceMappingURL=FormBase.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormBase.mjs","names":["useFormReactHookForm"],"sources":["../../../../src/components/Form/FormBase.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { cn } from '@utils/cn';\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\nimport {\n FormProvider,\n type FormProviderProps,\n type UseFormProps,\n useForm as useFormReactHookForm,\n useFormState,\n} from 'react-hook-form';\nimport type { ZodObject, z } from 'zod';\n\ntype FormProps<T extends ZodObject> = HTMLAttributes<HTMLFormElement> &\n FormProviderProps<z.infer<T>> & {\n schema?: T;\n onSubmit?: (data: z.infer<T>) => void | Promise<void>;\n onSubmitSuccess?: (data: z.infer<T>) => void | Promise<void>;\n onSubmitError?: (error: Error) => void | Promise<void>;\n autoComplete?: boolean;\n };\n\nconst awaitFunction = async (fn: any) => {\n // Check if result is a Promise (Thenable)\n\n if (fn && typeof fn.then === 'function') {\n // It's a Promise, so wait for it to resolve\n return await fn;\n }\n // If not a Promise, it will just execute without awaiting\n return fn;\n};\n\nexport const Form = <T extends ZodObject>({\n schema,\n onSubmit: onSubmitProp,\n onSubmitSuccess: onSubmitSuccessProp,\n onSubmitError: onSubmitErrorProp,\n className,\n children,\n autoComplete,\n method,\n ...props\n}: FormProps<T> & { method?: string }) => {\n const onSubmit = async (values: z.infer<T>) => {\n const parsedValues = schema?.safeParse(values) ?? {\n success: true,\n data: undefined,\n };\n\n // onSubmitProp?.(values);\n await awaitFunction(onSubmitProp?.(values));\n\n if (parsedValues.success) {\n await awaitFunction(onSubmitSuccessProp?.(parsedValues.data!));\n } else {\n await awaitFunction(\n onSubmitErrorProp?.(\n new Error(\n parsedValues.error.issues.map((error) => error.message).join(', ')\n )\n )\n );\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLFormElement>) => {\n if (e.key !== 'Enter') return;\n const target = e.target as HTMLElement;\n if (target.tagName !== 'INPUT') return;\n e.preventDefault();\n const formEl = e.currentTarget;\n const focusable = Array.from(\n formEl.querySelectorAll<HTMLElement>(\n 'input:not([disabled]):not([type=\"hidden\"]), textarea:not([disabled])'\n )\n );\n const idx = focusable.indexOf(target);\n if (idx >= 0 && idx < focusable.length - 1) {\n focusable[idx + 1]?.focus();\n }\n };\n\n return (\n <FormProvider {...props}>\n <form\n className={cn('flex flex-col gap-y-6', className)}\n onSubmit={props.handleSubmit(onSubmit)}\n onKeyDown={handleKeyDown}\n autoComplete={autoComplete ? 'on' : 'off'}\n noValidate\n method={method}\n >\n {children}\n </form>\n </FormProvider>\n );\n};\n\nexport const useForm = <T extends ZodObject>(\n schema: T,\n props?: UseFormProps<z.infer<T>>\n) => {\n const form = useFormReactHookForm<z.infer<T>>({\n resolver: zodResolver(schema as any),\n ...props,\n });\n\n const { isSubmitting, isSubmitted, isLoading, isValid } = useFormState({\n control: form.control,\n });\n\n return {\n form,\n isSubmitting,\n isSubmitted,\n isLoading,\n isValid,\n };\n};\n"],"mappings":";;;;;;;;AAuBA,MAAM,gBAAgB,OAAO,OAAY;CAGvC,IAAI,MAAM,OAAO,GAAG,SAAS,YAE3B,OAAO,MAAM;CAGf,OAAO;AACT;AAEA,MAAa,QAA6B,EACxC,QACA,UAAU,cACV,iBAAiB,qBACjB,eAAe,mBACf,WACA,UACA,cACA,QACA,GAAG,YACqC;CACxC,MAAM,WAAW,OAAO,WAAuB;EAC7C,MAAM,eAAe,QAAQ,UAAU,MAAM,KAAK;GAChD,SAAS;GACT,MAAM;EACR;EAGA,MAAM,cAAc,eAAe,MAAM,CAAC;EAE1C,IAAI,aAAa,SACf,MAAM,cAAc,sBAAsB,aAAa,IAAK,CAAC;OAE7D,MAAM,cACJ,oBACE,IAAI,MACF,aAAa,MAAM,OAAO,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,IAAI,CACnE,CACF,CACF;CAEJ;CAEA,MAAM,iBAAiB,MAAsC;EAC3D,IAAI,EAAE,QAAQ,SAAS;EACvB,MAAM,SAAS,EAAE;EACjB,IAAI,OAAO,YAAY,SAAS;EAChC,EAAE,eAAe;EACjB,MAAM,SAAS,EAAE;EACjB,MAAM,YAAY,MAAM,KACtB,OAAO,iBACL,wEACF,CACF;EACA,MAAM,MAAM,UAAU,QAAQ,MAAM;EACpC,IAAI,OAAO,KAAK,MAAM,UAAU,SAAS,GACvC,UAAU,MAAM,EAAE,EAAE,MAAM;CAE9B;CAEA,OACE,oBAAC,cAAD;EAAc,GAAI;YAChB,oBAAC,QAAD;GACE,WAAW,GAAG,yBAAyB,SAAS;GAChD,UAAU,MAAM,aAAa,QAAQ;GACrC,WAAW;GACX,cAAc,eAAe,OAAO;GACpC;GACQ;GAEP;EACG;CACM;AAElB;AAEA,MAAa,WACX,QACA,UACG;CACH,MAAM,OAAOA,UAAiC;EAC5C,UAAU,YAAY,MAAa;EACnC,GAAG;CACL,CAAC;CAED,MAAM,EAAE,cAAc,aAAa,WAAW,YAAY,aAAa,EACrE,SAAS,KAAK,QAChB,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"FormBase.mjs","names":["useFormReactHookForm"],"sources":["../../../../src/components/Form/FormBase.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { cn } from '@utils/cn';\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\nimport {\n FormProvider,\n type FormProviderProps,\n type UseFormProps,\n useForm as useFormReactHookForm,\n useFormState,\n} from 'react-hook-form';\nimport type { ZodMiniObject, z } from 'zod/mini';\n\n/**\n * Declarative WebMCP registration: a form carrying `toolname` becomes a tool\n * browser agents can fill and submit, its controls' `name` attributes forming\n * the input schema.\n *\n * @see https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md\n */\nexport type FormWebMCPProps = {\n /** Tool name exposed to agents (verb + noun, e.g. `subscribeToNewsletter`). */\n toolName?: string;\n /** What submitting the form does, for the agent. */\n toolDescription?: string;\n /**\n * Lets an agent submit without the user reviewing the filled form first.\n * Leave unset for anything consequential (payments, messages, sign-ups).\n */\n toolAutoSubmit?: boolean;\n};\n\ntype FormProps<T extends ZodMiniObject> = HTMLAttributes<HTMLFormElement> &\n FormProviderProps<z.infer<T>> &\n FormWebMCPProps & {\n schema?: T;\n onSubmit?: (data: z.infer<T>) => void | Promise<void>;\n onSubmitSuccess?: (data: z.infer<T>) => void | Promise<void>;\n onSubmitError?: (error: Error) => void | Promise<void>;\n autoComplete?: boolean;\n };\n\n/**\n * Maps the camelCase props onto the lowercase attributes the browser reads.\n * Typed as a plain record: React's JSX types do not know these attributes yet.\n */\nexport const getFormWebMCPAttributes = ({\n toolName,\n toolDescription,\n toolAutoSubmit,\n}: FormWebMCPProps): Record<string, string | undefined> =>\n toolName\n ? {\n toolname: toolName,\n tooldescription: toolDescription,\n // A boolean attribute: present (empty) when enabled, absent otherwise.\n toolautosubmit: toolAutoSubmit ? '' : undefined,\n }\n : {};\n\nconst awaitFunction = async (fn: any) => {\n // Check if result is a Promise (Thenable)\n\n if (fn && typeof fn.then === 'function') {\n // It's a Promise, so wait for it to resolve\n return await fn;\n }\n // If not a Promise, it will just execute without awaiting\n return fn;\n};\n\nexport const Form = <T extends ZodMiniObject>({\n schema,\n onSubmit: onSubmitProp,\n onSubmitSuccess: onSubmitSuccessProp,\n onSubmitError: onSubmitErrorProp,\n className,\n children,\n autoComplete,\n method,\n toolName,\n toolDescription,\n toolAutoSubmit,\n ...props\n}: FormProps<T> & { method?: string }) => {\n const onSubmit = async (values: z.infer<T>) => {\n const parsedValues = schema.safeParse(values) ?? {\n success: true,\n data: undefined,\n };\n\n // onSubmitProp?.(values);\n await awaitFunction(onSubmitProp?.(values));\n\n if (parsedValues.success) {\n await awaitFunction(onSubmitSuccessProp?.(parsedValues.data!));\n } else {\n await awaitFunction(\n onSubmitErrorProp?.(\n new Error(\n parsedValues.error.issues.map((error) => error.message).join(', ')\n )\n )\n );\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLFormElement>) => {\n if (e.key !== 'Enter') return;\n const target = e.target as HTMLElement;\n if (target.tagName !== 'INPUT') return;\n e.preventDefault();\n const formEl = e.currentTarget;\n const focusable = Array.from(\n formEl.querySelectorAll<HTMLElement>(\n 'input:not([disabled]):not([type=\"hidden\"]), textarea:not([disabled])'\n )\n );\n const idx = focusable.indexOf(target);\n if (idx >= 0 && idx < focusable.length - 1) {\n focusable[idx + 1]?.focus();\n }\n };\n\n return (\n <FormProvider {...props}>\n <form\n className={cn('flex flex-col gap-y-6', className)}\n onSubmit={props.handleSubmit(onSubmit)}\n onKeyDown={handleKeyDown}\n autoComplete={autoComplete ? 'on' : 'off'}\n noValidate\n method={method}\n {...getFormWebMCPAttributes({\n toolName,\n toolDescription,\n toolAutoSubmit,\n })}\n >\n {children}\n </form>\n </FormProvider>\n );\n};\n\nexport const useForm = <T extends ZodMiniObject>(\n schema: T,\n props?: UseFormProps<z.infer<T>>\n) => {\n const form = useFormReactHookForm<z.infer<T>>({\n resolver: zodResolver(schema as any),\n ...props,\n });\n\n const { isSubmitting, isSubmitted, isLoading, isValid } = useFormState({\n control: form.control,\n });\n\n return {\n form,\n isSubmitting,\n isSubmitted,\n isLoading,\n isValid,\n };\n};\n"],"mappings":";;;;;;;;;;;;AA+CA,MAAa,2BAA2B,EACtC,UACA,iBACA,qBAEA,WACI;CACE,UAAU;CACV,iBAAiB;CAEjB,gBAAgB,iBAAiB,KAAK;AACxC,IACA,CAAC;AAEP,MAAM,gBAAgB,OAAO,OAAY;CAGvC,IAAI,MAAM,OAAO,GAAG,SAAS,YAE3B,OAAO,MAAM;CAGf,OAAO;AACT;AAEA,MAAa,QAAiC,EAC5C,QACA,UAAU,cACV,iBAAiB,qBACjB,eAAe,mBACf,WACA,UACA,cACA,QACA,UACA,iBACA,gBACA,GAAG,YACqC;CACxC,MAAM,WAAW,OAAO,WAAuB;EAC7C,MAAM,eAAe,OAAO,UAAU,MAAM,KAAK;GAC/C,SAAS;GACT,MAAM;EACR;EAGA,MAAM,cAAc,eAAe,MAAM,CAAC;EAE1C,IAAI,aAAa,SACf,MAAM,cAAc,sBAAsB,aAAa,IAAK,CAAC;OAE7D,MAAM,cACJ,oBACE,IAAI,MACF,aAAa,MAAM,OAAO,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,IAAI,CACnE,CACF,CACF;CAEJ;CAEA,MAAM,iBAAiB,MAAsC;EAC3D,IAAI,EAAE,QAAQ,SAAS;EACvB,MAAM,SAAS,EAAE;EACjB,IAAI,OAAO,YAAY,SAAS;EAChC,EAAE,eAAe;EACjB,MAAM,SAAS,EAAE;EACjB,MAAM,YAAY,MAAM,KACtB,OAAO,iBACL,wEACF,CACF;EACA,MAAM,MAAM,UAAU,QAAQ,MAAM;EACpC,IAAI,OAAO,KAAK,MAAM,UAAU,SAAS,GACvC,UAAU,MAAM,EAAE,EAAE,MAAM;CAE9B;CAEA,OACE,oBAAC,cAAD;EAAc,GAAI;YAChB,oBAAC,QAAD;GACE,WAAW,GAAG,yBAAyB,SAAS;GAChD,UAAU,MAAM,aAAa,QAAQ;GACrC,WAAW;GACX,cAAc,eAAe,OAAO;GACpC;GACQ;GACR,GAAI,wBAAwB;IAC1B;IACA;IACA;GACF,CAAC;GAEA;EACG;CACM;AAElB;AAEA,MAAa,WACX,QACA,UACG;CACH,MAAM,OAAOA,UAAiC;EAC5C,UAAU,YAAY,MAAa;EACnC,GAAG;CACL,CAAC;CAED,MAAM,EAAE,cAAc,aAAa,WAAW,YAAY,aAAa,EACrE,SAAS,KAAK,QAChB,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -6,7 +6,9 @@ import { jsx } from "react/jsx-runtime";
6
6
  import { useFormContext } from "react-hook-form";
7
7
 
8
8
  //#region src/components/Form/elements/FormElement.tsx
9
- const FormFieldElement = ({ field, name, label, Element, isRequired = false, info, description, showErrorMessage = true, layoutClassName, ...props }) => {
9
+ /** Lowercase attribute the browser reads; React's JSX types do not know it yet. */
10
+ const getWebMCPParamAttributes = (toolParamDescription) => toolParamDescription ? { toolparamdescription: toolParamDescription } : {};
11
+ const FormFieldElement = ({ field, name, label, Element, isRequired = false, info, description, showErrorMessage = true, layoutClassName, toolParamDescription, ...props }) => {
10
12
  const { error } = useFormField();
11
13
  const { value: fieldValue, onChange: fieldOnChange, ...fieldRest } = field ?? {};
12
14
  const propsAny = props;
@@ -29,6 +31,7 @@ const FormFieldElement = ({ field, name, label, Element, isRequired = false, inf
29
31
  id: name,
30
32
  ...fieldRest,
31
33
  ...props,
34
+ ...getWebMCPParamAttributes(toolParamDescription),
32
35
  value: mergedValue,
33
36
  onChange: mergedOnChange,
34
37
  children: props.children
@@ -1 +1 @@
1
- {"version":3,"file":"FormElement.mjs","names":[],"sources":["../../../../../src/components/Form/elements/FormElement.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentProps, ElementType, ReactNode } from 'react';\nimport {\n type ControllerRenderProps,\n type FieldValues,\n useFormContext,\n} from 'react-hook-form';\nimport { FormField, useFormField } from '../FormField';\nimport { FormItemLayout, type FormItemLayoutProps } from '../layout';\n\nexport type FormElementProps<T extends ElementType> = {\n name: string;\n Element: T;\n label?: ReactNode;\n isRequired?: boolean;\n info?: string;\n showErrorMessage?: boolean;\n focus?: boolean;\n layoutClassName?: string;\n} & Omit<FormItemLayoutProps, 'children'>;\n\ntype FormFieldElementProps<T extends ElementType> = FormElementProps<T> &\n ComponentProps<T> & {\n field: ControllerRenderProps<FieldValues, string>;\n };\n\nconst FormFieldElement = <T extends ElementType>({\n field,\n name,\n label,\n Element,\n isRequired = false,\n info,\n description,\n showErrorMessage = true,\n layoutClassName,\n ...props\n}: FormFieldElementProps<T>) => {\n const { error } = useFormField();\n\n // Ensure controlled inputs and merge onChange with RHF controller\n const {\n value: fieldValue,\n onChange: fieldOnChange,\n ...fieldRest\n } = (field as unknown as {\n value: unknown;\n onChange: (...args: unknown[]) => void;\n }) ?? {};\n\n const propsAny = props as unknown as {\n value?: unknown;\n onChange?: (...args: unknown[]) => void;\n children?: unknown;\n };\n\n const mergedValue =\n propsAny.value !== undefined ? propsAny.value : (fieldValue ?? '');\n\n const mergedOnChange = (...args: unknown[]) => {\n if (typeof fieldOnChange === 'function') fieldOnChange(...args);\n if (typeof propsAny.onChange === 'function') propsAny.onChange(...args);\n };\n\n return (\n <FormItemLayout\n htmlFor={name}\n label={label}\n description={description}\n isRequired={isRequired}\n info={info}\n showErrorMessage={showErrorMessage}\n aria-invalid={!!error}\n className={layoutClassName}\n >\n <Element\n data-testid=\"element\"\n id={name}\n {...fieldRest}\n {...props}\n // Force controlled value to avoid uncontrolled-to-controlled warnings\n value={mergedValue as never}\n // Merge onChange so RHF stays in sync while allowing custom handlers\n onChange={mergedOnChange as never}\n >\n {props.children}\n </Element>\n </FormItemLayout>\n );\n};\n\n/**\n * FormElement is a component that allows you to create a form element with a label, description, error message, and validation.\n *\n * The Element prop is the type of the element that will be rendered.\n * This element will interact with the FormContext and will be controlled by the FormControl component.\n * The props used to control the element will be `value` and `onChange`.\n */\nexport const FormElement = <T extends ElementType>(\n props: FormElementProps<T> & ComponentProps<T>\n) => {\n const { control } = useFormContext();\n\n return (\n <FormField\n control={control}\n name={props.name}\n render={({ field }) => <FormFieldElement {...props} field={field} />}\n />\n );\n};\n"],"mappings":";;;;;;;;AA2BA,MAAM,oBAA2C,EAC/C,OACA,MACA,OACA,SACA,aAAa,OACb,MACA,aACA,mBAAmB,MACnB,iBACA,GAAG,YAC2B;CAC9B,MAAM,EAAE,UAAU,aAAa;CAG/B,MAAM,EACJ,OAAO,YACP,UAAU,eACV,GAAG,cACA,SAGC,CAAC;CAEP,MAAM,WAAW;CAMjB,MAAM,cACJ,SAAS,UAAU,SAAY,SAAS,QAAS,cAAc;CAEjE,MAAM,kBAAkB,GAAG,SAAoB;EAC7C,IAAI,OAAO,kBAAkB,YAAY,cAAc,GAAG,IAAI;EAC9D,IAAI,OAAO,SAAS,aAAa,YAAY,SAAS,SAAS,GAAG,IAAI;CACxE;CAEA,OACE,oBAAC,gBAAD;EACE,SAAS;EACF;EACM;EACD;EACN;EACY;EAClB,gBAAc,CAAC,CAAC;EAChB,WAAW;YAEX,oBAAC,SAAD;GACE,eAAY;GACZ,IAAI;GACJ,GAAI;GACJ,GAAI;GAEJ,OAAO;GAEP,UAAU;aAET,MAAM;EACA;CACK;AAEpB;;;;;;;;AASA,MAAa,eACX,UACG;CACH,MAAM,EAAE,YAAY,eAAe;CAEnC,OACE,oBAAC,WAAD;EACW;EACT,MAAM,MAAM;EACZ,SAAS,EAAE,YAAY,oBAAC,kBAAD;GAAkB,GAAI;GAAc;EAAQ;CACpE;AAEL"}
1
+ {"version":3,"file":"FormElement.mjs","names":[],"sources":["../../../../../src/components/Form/elements/FormElement.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentProps, ElementType, ReactNode } from 'react';\nimport {\n type ControllerRenderProps,\n type FieldValues,\n useFormContext,\n} from 'react-hook-form';\nimport { FormField, useFormField } from '../FormField';\nimport { FormItemLayout, type FormItemLayoutProps } from '../layout';\n\nexport type FormElementProps<T extends ElementType> = {\n name: string;\n Element: T;\n label?: ReactNode;\n isRequired?: boolean;\n info?: string;\n showErrorMessage?: boolean;\n focus?: boolean;\n layoutClassName?: string;\n toolParamDescription?: string;\n} & Omit<FormItemLayoutProps, 'children'>;\n\n/** Lowercase attribute the browser reads; React's JSX types do not know it yet. */\nconst getWebMCPParamAttributes = (\n toolParamDescription?: string\n): Record<string, string> =>\n toolParamDescription ? { toolparamdescription: toolParamDescription } : {};\n\ntype FormFieldElementProps<T extends ElementType> = FormElementProps<T> &\n ComponentProps<T> & {\n field: ControllerRenderProps<FieldValues, string>;\n };\n\nconst FormFieldElement = <T extends ElementType>({\n field,\n name,\n label,\n Element,\n isRequired = false,\n info,\n description,\n showErrorMessage = true,\n layoutClassName,\n toolParamDescription,\n ...props\n}: FormFieldElementProps<T>) => {\n const { error } = useFormField();\n\n // Ensure controlled inputs and merge onChange with RHF controller\n const {\n value: fieldValue,\n onChange: fieldOnChange,\n ...fieldRest\n } = (field as unknown as {\n value: unknown;\n onChange: (...args: unknown[]) => void;\n }) ?? {};\n\n const propsAny = props as unknown as {\n value?: unknown;\n onChange?: (...args: unknown[]) => void;\n children?: unknown;\n };\n\n const mergedValue =\n propsAny.value !== undefined ? propsAny.value : (fieldValue ?? '');\n\n const mergedOnChange = (...args: unknown[]) => {\n if (typeof fieldOnChange === 'function') fieldOnChange(...args);\n if (typeof propsAny.onChange === 'function') propsAny.onChange(...args);\n };\n\n return (\n <FormItemLayout\n htmlFor={name}\n label={label}\n description={description}\n isRequired={isRequired}\n info={info}\n showErrorMessage={showErrorMessage}\n aria-invalid={!!error}\n className={layoutClassName}\n >\n <Element\n data-testid=\"element\"\n id={name}\n {...fieldRest}\n {...props}\n {...getWebMCPParamAttributes(toolParamDescription)}\n // Force controlled value to avoid uncontrolled-to-controlled warnings\n value={mergedValue as never}\n // Merge onChange so RHF stays in sync while allowing custom handlers\n onChange={mergedOnChange as never}\n >\n {props.children}\n </Element>\n </FormItemLayout>\n );\n};\n\n/**\n * FormElement is a component that allows you to create a form element with a label, description, error message, and validation.\n *\n * The Element prop is the type of the element that will be rendered.\n * This element will interact with the FormContext and will be controlled by the FormControl component.\n * The props used to control the element will be `value` and `onChange`.\n */\nexport const FormElement = <T extends ElementType>(\n props: FormElementProps<T> & ComponentProps<T>\n) => {\n const { control } = useFormContext();\n\n return (\n <FormField\n control={control}\n name={props.name}\n render={({ field }) => <FormFieldElement {...props} field={field} />}\n />\n );\n};\n"],"mappings":";;;;;;;;;AAwBA,MAAM,4BACJ,yBAEA,uBAAuB,EAAE,sBAAsB,qBAAqB,IAAI,CAAC;AAO3E,MAAM,oBAA2C,EAC/C,OACA,MACA,OACA,SACA,aAAa,OACb,MACA,aACA,mBAAmB,MACnB,iBACA,sBACA,GAAG,YAC2B;CAC9B,MAAM,EAAE,UAAU,aAAa;CAG/B,MAAM,EACJ,OAAO,YACP,UAAU,eACV,GAAG,cACA,SAGC,CAAC;CAEP,MAAM,WAAW;CAMjB,MAAM,cACJ,SAAS,UAAU,SAAY,SAAS,QAAS,cAAc;CAEjE,MAAM,kBAAkB,GAAG,SAAoB;EAC7C,IAAI,OAAO,kBAAkB,YAAY,cAAc,GAAG,IAAI;EAC9D,IAAI,OAAO,SAAS,aAAa,YAAY,SAAS,SAAS,GAAG,IAAI;CACxE;CAEA,OACE,oBAAC,gBAAD;EACE,SAAS;EACF;EACM;EACD;EACN;EACY;EAClB,gBAAc,CAAC,CAAC;EAChB,WAAW;YAEX,oBAAC,SAAD;GACE,eAAY;GACZ,IAAI;GACJ,GAAI;GACJ,GAAI;GACJ,GAAI,yBAAyB,oBAAoB;GAEjD,OAAO;GAEP,UAAU;aAET,MAAM;EACA;CACK;AAEpB;;;;;;;;AASA,MAAa,eACX,UACG;CACH,MAAM,EAAE,YAAY,eAAe;CAEnC,OACE,oBAAC,WAAD;EACW;EACT,MAAM,MAAM;EACZ,SAAS,EAAE,YAAY,oBAAC,kBAAD;GAAkB,GAAI;GAAc;EAAQ;CACpE;AAEL"}
@@ -3,10 +3,10 @@
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { FormProvider, useForm } from "react-hook-form";
5
5
  import { zodResolver } from "@hookform/resolvers/zod";
6
- import { z } from "zod";
6
+ import { z } from "zod/mini";
7
7
 
8
8
  //#region src/components/Form/elements/FormElementWrapper.tsx
9
- const Schema = z.object({ test: z.string().min(3, "test error") });
9
+ const Schema = z.object({ test: z.string().check(z.minLength(3, "test error")) });
10
10
  const FormElementWrapper = (props) => {
11
11
  const objectFormSchema = props.schema ?? Schema;
12
12
  const form = useForm({
@@ -1 +1 @@
1
- {"version":3,"file":"FormElementWrapper.mjs","names":[],"sources":["../../../../../src/components/Form/elements/FormElementWrapper.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport type { FC, HTMLProps, SubmitEvent } from 'react';\nimport { FormProvider, useForm } from 'react-hook-form';\nimport { z } from 'zod';\n\nconst Schema = z.object({\n test: z.string().min(3, 'test error'),\n});\n\ntype FormElementWrapperProps = HTMLProps<HTMLFormElement> & {\n schema?: z.ZodObject;\n values?: z.infer<z.ZodObject>;\n};\n\nexport const FormElementWrapper: FC<FormElementWrapperProps> = (props) => {\n const objectFormSchema = props.schema ?? Schema;\n\n const form = useForm<z.infer<typeof objectFormSchema>>({\n resolver: zodResolver(objectFormSchema as any),\n values: props.values,\n });\n\n const { children, onSubmit, ...rest } = props;\n\n const onSubmitForm = (\n values: z.infer<typeof objectFormSchema>,\n event: SubmitEvent<HTMLFormElement>\n ) => {\n const parsedValues = objectFormSchema.safeParse(values);\n if (parsedValues.success) {\n onSubmit?.(event);\n }\n };\n\n return (\n <FormProvider {...form}>\n <form\n {...rest}\n onSubmit={(e) => {\n e.preventDefault();\n form.handleSubmit((values) => onSubmitForm(values, e));\n }}\n >\n {children}\n </form>\n </FormProvider>\n );\n};\n"],"mappings":";;;;;;;;AAOA,MAAM,SAAS,EAAE,OAAO,EACtB,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,YAAY,EACtC,CAAC;AAOD,MAAa,sBAAmD,UAAU;CACxE,MAAM,mBAAmB,MAAM,UAAU;CAEzC,MAAM,OAAO,QAA0C;EACrD,UAAU,YAAY,gBAAuB;EAC7C,QAAQ,MAAM;CAChB,CAAC;CAED,MAAM,EAAE,UAAU,UAAU,GAAG,SAAS;CAExC,MAAM,gBACJ,QACA,UACG;EAEH,IADqB,iBAAiB,UAAU,MACjC,CAAC,CAAC,SACf,WAAW,KAAK;CAEpB;CAEA,OACE,oBAAC,cAAD;EAAc,GAAI;YAChB,oBAAC,QAAD;GACE,GAAI;GACJ,WAAW,MAAM;IACf,EAAE,eAAe;IACjB,KAAK,cAAc,WAAW,aAAa,QAAQ,CAAC,CAAC;GACvD;GAEC;EACG;CACM;AAElB"}
1
+ {"version":3,"file":"FormElementWrapper.mjs","names":[],"sources":["../../../../../src/components/Form/elements/FormElementWrapper.tsx"],"sourcesContent":["'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport type { FC, HTMLProps, SubmitEvent } from 'react';\nimport { FormProvider, useForm } from 'react-hook-form';\nimport { z } from 'zod/mini';\n\nconst Schema = z.object({\n test: z.string().check(z.minLength(3, 'test error')),\n});\n\ntype FormElementWrapperProps = HTMLProps<HTMLFormElement> & {\n schema?: z.ZodMiniObject;\n values?: z.infer<z.ZodMiniObject>;\n};\n\nexport const FormElementWrapper: FC<FormElementWrapperProps> = (props) => {\n const objectFormSchema = props.schema ?? Schema;\n\n const form = useForm<z.infer<typeof objectFormSchema>>({\n resolver: zodResolver(objectFormSchema as any),\n values: props.values,\n });\n\n const { children, onSubmit, ...rest } = props;\n\n const onSubmitForm = (\n values: z.infer<typeof objectFormSchema>,\n event: SubmitEvent<HTMLFormElement>\n ) => {\n const parsedValues = objectFormSchema.safeParse(values);\n if (parsedValues.success) {\n onSubmit?.(event);\n }\n };\n\n return (\n <FormProvider {...form}>\n <form\n {...rest}\n onSubmit={(e) => {\n e.preventDefault();\n form.handleSubmit((values) => onSubmitForm(values, e));\n }}\n >\n {children}\n </form>\n </FormProvider>\n );\n};\n"],"mappings":";;;;;;;;AAOA,MAAM,SAAS,EAAE,OAAO,EACtB,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAAC,EACrD,CAAC;AAOD,MAAa,sBAAmD,UAAU;CACxE,MAAM,mBAAmB,MAAM,UAAU;CAEzC,MAAM,OAAO,QAA0C;EACrD,UAAU,YAAY,gBAAuB;EAC7C,QAAQ,MAAM;CAChB,CAAC;CAED,MAAM,EAAE,UAAU,UAAU,GAAG,SAAS;CAExC,MAAM,gBACJ,QACA,UACG;EAEH,IADqB,iBAAiB,UAAU,MACjC,CAAC,CAAC,SACf,WAAW,KAAK;CAEpB;CAEA,OACE,oBAAC,cAAD;EAAc,GAAI;YAChB,oBAAC,QAAD;GACE,GAAI;GACJ,WAAW,MAAM;IACf,EAAE,eAAe;IACjB,KAAK,cAAc,WAAW,aAAa,QAAQ,CAAC,CAAC;GACvD;GAEC;EACG;CACM;AAElB"}
@@ -18,7 +18,7 @@ import { SearchInputElement } from "./elements/SearchInputElement.mjs";
18
18
  import { SelectElement } from "./elements/SelectElement.mjs";
19
19
  import { SwitchSelectorElement } from "./elements/SwitchSelectorElement.mjs";
20
20
  import { TextAreaElement } from "./elements/TextAreaElement.mjs";
21
- import { useForm } from "./FormBase.mjs";
21
+ import { getFormWebMCPAttributes, useForm } from "./FormBase.mjs";
22
22
  import { Form } from "./Form.mjs";
23
23
 
24
- export { Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, useForm, useFormField };
24
+ export { Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, getFormWebMCPAttributes, useForm, useFormField };
@@ -4,7 +4,7 @@ import cn from "cnfast";
4
4
 
5
5
  //#region src/components/Status/index.tsx
6
6
  const Status = ({ className, ...props }) => {
7
- const { theme, resolvedTheme } = useTheme();
7
+ const { resolvedTheme } = useTheme();
8
8
  return /* @__PURE__ */ jsx("div", {
9
9
  ...props,
10
10
  className: cn("w-full max-w-[200px] overflow-hidden rounded", className),
@@ -12,13 +12,10 @@ const Status = ({ className, ...props }) => {
12
12
  title: "Badge",
13
13
  src: `https://status.intlayer.org/badge?theme=${resolvedTheme}`,
14
14
  height: "30",
15
- frameBorder: "0",
16
- scrolling: "no",
17
15
  style: {
18
16
  width: "100%",
19
17
  colorScheme: "light"
20
- },
21
- allowTransparency: true
18
+ }
22
19
  })
23
20
  });
24
21
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Status/index.tsx"],"sourcesContent":["import { useTheme } from '@providers/ThemeProvider';\nimport cn from 'cnfast';\nimport type { FC } from 'react';\n\nexport const Status: FC<{ className?: string }> = ({ className, ...props }) => {\n const { theme, resolvedTheme } = useTheme();\n return (\n <div\n {...props}\n className={cn('w-full max-w-[200px] overflow-hidden rounded', className)}\n >\n <iframe\n title=\"Badge\"\n src={`https://status.intlayer.org/badge?theme=${resolvedTheme}`}\n height=\"30\"\n frameBorder=\"0\"\n scrolling=\"no\"\n style={{\n width: '100%',\n colorScheme: 'light',\n }}\n allowTransparency\n />\n </div>\n );\n};\n"],"mappings":";;;;;AAIA,MAAa,UAAsC,EAAE,WAAW,GAAG,YAAY;CAC7E,MAAM,EAAE,OAAO,kBAAkB,SAAS;CAC1C,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,WAAW,GAAG,gDAAgD,SAAS;YAEvE,oBAAC,UAAD;GACE,OAAM;GACN,KAAK,2CAA2C;GAChD,QAAO;GACP,aAAY;GACZ,WAAU;GACV,OAAO;IACL,OAAO;IACP,aAAa;GACf;GACA;EACD;CACE;AAET"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Status/index.tsx"],"sourcesContent":["import { useTheme } from '@providers/ThemeProvider';\nimport cn from 'cnfast';\nimport type { FC } from 'react';\n\nexport const Status: FC<{ className?: string }> = ({ className, ...props }) => {\n const { resolvedTheme } = useTheme();\n return (\n <div\n {...props}\n className={cn('w-full max-w-[200px] overflow-hidden rounded', className)}\n >\n <iframe\n title=\"Badge\"\n src={`https://status.intlayer.org/badge?theme=${resolvedTheme}`}\n height=\"30\"\n style={{\n width: '100%',\n colorScheme: 'light',\n }}\n />\n </div>\n );\n};\n"],"mappings":";;;;;AAIA,MAAa,UAAsC,EAAE,WAAW,GAAG,YAAY;CAC7E,MAAM,EAAE,kBAAkB,SAAS;CACnC,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,WAAW,GAAG,gDAAgD,SAAS;YAEvE,oBAAC,UAAD;GACE,OAAM;GACN,KAAK,2CAA2C;GAChD,QAAO;GACP,OAAO;IACL,OAAO;IACP,aAAa;GACf;EACD;CACE;AAET"}
@@ -67,7 +67,7 @@ import { SearchInputElement } from "./Form/elements/SearchInputElement.mjs";
67
67
  import { SelectElement } from "./Form/elements/SelectElement.mjs";
68
68
  import { SwitchSelectorElement } from "./Form/elements/SwitchSelectorElement.mjs";
69
69
  import { TextAreaElement } from "./Form/elements/TextAreaElement.mjs";
70
- import { useForm } from "./Form/FormBase.mjs";
70
+ import { getFormWebMCPAttributes, useForm } from "./Form/FormBase.mjs";
71
71
  import { Form } from "./Form/Form.mjs";
72
72
  import { DictionaryCreationForm } from "./DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs";
73
73
  import { TabSelector } from "./TabSelector/TabSelector.mjs";
@@ -143,4 +143,4 @@ import { Terminal } from "./Terminal/Terminal.mjs";
143
143
  import { DesktopThemeSwitcher } from "./ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs";
144
144
  import { MobileThemeSwitcher } from "./ThemeSwitcherDropDown/MobileThemeSwitcher.mjs";
145
145
 
146
- export { Accordion, AutoCompleteTextarea, AutoSizedTextArea, Avatar, Badge, BitbucketLogo, Breadcrumb, Browser, Burger, Button, Carousel, Checkbox, ClickOutsideDiv, Code, CodeBlock, CodeDefault, CodeHighlighted, CollapsibleTable, Command, CommandRoot, Container, ContentEditableTextArea, ContentEditor, ContentEditorInput, ContentEditorTextArea, ContentSelector, CopyButton, CopyToClipboard, DesktopNavbar, DesktopThemeSwitcher, Detail, DictionaryCreationForm, DictionaryEditor, DictionaryFieldEditor, DiscordLogo, DotPattern, DropDown, EditableFieldInput, EditableFieldTextArea, ExpandCollapse, FacebookLogo, FileList, Flag, flags_exports as Flags, Footer, Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, GitHubLogo, GitLabLogo, GoogleLogo, GridDistortionPattern, GridPattern, H1, H2, H3, H4, H5, H6, HTMLRenderer, HeightResizer, HideShow, Hr, IDE, InformationTag, Input, InputIndicator, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, InputPassword, InstagramLogo, KeyPathBreadcrumb, KeyboardScreenAdapter, KeyboardShortcut, Label, LanguageBackground, LanguageSection, Link, LinkedInLogo, Loader, LocalCardList, LocaleSwitcher, LocaleSwitcherContent, LocaleSwitcherContentProvider, Logo, LogoTextOnly, LogoWithText, LogoWithTextBelow, MarkDownIframe, MarkdownEditor, MarkdownRenderer, MaxHeightSmoother, MaxWidthSmoother, MobileNavbar, MobileThemeSwitcher, Modal, MultiSelect, Navbar, NodeEditor, NumberItemsSelector, OTPInput, OTPInputContext, Pagination, Popover, PopoverStatic, PressableSpan, ProductHuntLogo, Radio, RightDrawer, SaveForm, SearchInput, Select, SelectContent, SelectLabel, SelectSeparator, ShowingResultsNumberItems, SmartTable, SocialNetworks, Spotlight, Step, Steps, SwitchSelector, Tab, TabSelector, Table, Tag, Td, TechLogo, techLogos_exports as TechLogos, Terminal, TextArea, TextEditor, TextEditorContainer, Th, TiktokLogo, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Tr, VersionSwitcher, VersionSwitcherProvider, VerticalSwitchSelector, WithResizer, XLogo, YoutubeLogo, badgeVariants, baseMarkdownComponents, buttonVariants, checkIsExternalLink, checkboxVariants, choiceVariant, containerVariants, createEditorUploadFn, defaultChoices, drawerManager, getCapitals, getIntlayerHTMLOptions, getIntlayerMarkdownOptions, getYoutubeEmbedUrl, getYoutubeVideoId, getYoutubeWatchUrl, indicatorVariant, inputSlotVariants, inputVariants, isTextChildren, linkVariants, paginationVariants, radioVariants, reducer, shikiWrapperClassName, switchSelectorVariant, toast, toastVariants, traceKeys, useContentEditable, useCopyToClipboard, useForm, useFormField, useIsRightDrawerOpen, useLocaleSwitcherContent, usePasswordManagerBadge, usePopoverIds, usePrevious, useRightDrawer, useRightDrawerActions, useToast, useVersionSwitcher };
146
+ export { Accordion, AutoCompleteTextarea, AutoSizedTextArea, Avatar, Badge, BitbucketLogo, Breadcrumb, Browser, Burger, Button, Carousel, Checkbox, ClickOutsideDiv, Code, CodeBlock, CodeDefault, CodeHighlighted, CollapsibleTable, Command, CommandRoot, Container, ContentEditableTextArea, ContentEditor, ContentEditorInput, ContentEditorTextArea, ContentSelector, CopyButton, CopyToClipboard, DesktopNavbar, DesktopThemeSwitcher, Detail, DictionaryCreationForm, DictionaryEditor, DictionaryFieldEditor, DiscordLogo, DotPattern, DropDown, EditableFieldInput, EditableFieldTextArea, ExpandCollapse, FacebookLogo, FileList, Flag, flags_exports as Flags, Footer, Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, GitHubLogo, GitLabLogo, GoogleLogo, GridDistortionPattern, GridPattern, H1, H2, H3, H4, H5, H6, HTMLRenderer, HeightResizer, HideShow, Hr, IDE, InformationTag, Input, InputIndicator, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, InputPassword, InstagramLogo, KeyPathBreadcrumb, KeyboardScreenAdapter, KeyboardShortcut, Label, LanguageBackground, LanguageSection, Link, LinkedInLogo, Loader, LocalCardList, LocaleSwitcher, LocaleSwitcherContent, LocaleSwitcherContentProvider, Logo, LogoTextOnly, LogoWithText, LogoWithTextBelow, MarkDownIframe, MarkdownEditor, MarkdownRenderer, MaxHeightSmoother, MaxWidthSmoother, MobileNavbar, MobileThemeSwitcher, Modal, MultiSelect, Navbar, NodeEditor, NumberItemsSelector, OTPInput, OTPInputContext, Pagination, Popover, PopoverStatic, PressableSpan, ProductHuntLogo, Radio, RightDrawer, SaveForm, SearchInput, Select, SelectContent, SelectLabel, SelectSeparator, ShowingResultsNumberItems, SmartTable, SocialNetworks, Spotlight, Step, Steps, SwitchSelector, Tab, TabSelector, Table, Tag, Td, TechLogo, techLogos_exports as TechLogos, Terminal, TextArea, TextEditor, TextEditorContainer, Th, TiktokLogo, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Tr, VersionSwitcher, VersionSwitcherProvider, VerticalSwitchSelector, WithResizer, XLogo, YoutubeLogo, badgeVariants, baseMarkdownComponents, buttonVariants, checkIsExternalLink, checkboxVariants, choiceVariant, containerVariants, createEditorUploadFn, defaultChoices, drawerManager, getCapitals, getFormWebMCPAttributes, getIntlayerHTMLOptions, getIntlayerMarkdownOptions, getYoutubeEmbedUrl, getYoutubeVideoId, getYoutubeWatchUrl, indicatorVariant, inputSlotVariants, inputVariants, isTextChildren, linkVariants, paginationVariants, radioVariants, reducer, shikiWrapperClassName, switchSelectorVariant, toast, toastVariants, traceKeys, useContentEditable, useCopyToClipboard, useForm, useFormField, useIsRightDrawerOpen, useLocaleSwitcherContent, usePasswordManagerBadge, usePopoverIds, usePrevious, useRightDrawer, useRightDrawerActions, useToast, useVersionSwitcher };
@@ -1,17 +1,19 @@
1
- import { useKeyboardDetector } from "./useKeyboardDetector.mjs";
1
+ import { useStickToBottom } from "./useStickToBottom.mjs";
2
+ import { useWebMCPTools } from "./useWebMCPTools.mjs";
3
+ import { useSearch } from "./useSearch.mjs";
2
4
  import { useGetElementOrWindow } from "./useGetElementOrWindow.mjs";
3
5
  import { useScrollY } from "./useScrollY.mjs";
4
- import { usePersistedStore } from "./usePersistedStore.mjs";
5
- import { useHorizontalSwipe } from "./useHorizontalSwipe.mjs";
6
- import { useItemSelector } from "./useItemSelector.mjs";
7
6
  import { NO_HIGHLIGHTED_INDEX, useListKeyboardNavigation } from "./useListKeyboardNavigation.mjs";
7
+ import { useGetElementById } from "./useGetElementById.mjs";
8
8
  import { calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice } from "./useDevice.mjs";
9
- import { useSearch } from "./useSearch.mjs";
9
+ import { useHorizontalSwipe } from "./useHorizontalSwipe.mjs";
10
10
  import { useIsMounted } from "./useIsMounted.mjs";
11
- import { useGetElementById } from "./useGetElementById.mjs";
11
+ import { useItemSelector } from "./useItemSelector.mjs";
12
+ import { useKeyboardDetector } from "./useKeyboardDetector.mjs";
13
+ import { usePersistedStore } from "./usePersistedStore.mjs";
14
+ import { useRemoteMcpTools } from "./useRemoteMcpTools.mjs";
12
15
  import { useScreenWidth } from "./useScreenWidth.mjs";
13
16
  import { useScrollBlockage } from "./useScrollBlockage/index.mjs";
14
17
  import { useScrollDetection } from "./useScrollDetection.mjs";
15
- import { useStickToBottom } from "./useStickToBottom.mjs";
16
18
 
17
- export { NO_HIGHLIGHTED_INDEX, calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice, useGetElementById, useGetElementOrWindow, useHorizontalSwipe, useIsMounted, useItemSelector, useKeyboardDetector, useListKeyboardNavigation, usePersistedStore, useScreenWidth, useScrollBlockage, useScrollDetection, useScrollY, useSearch, useStickToBottom };
19
+ export { NO_HIGHLIGHTED_INDEX, calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice, useGetElementById, useGetElementOrWindow, useHorizontalSwipe, useIsMounted, useItemSelector, useKeyboardDetector, useListKeyboardNavigation, usePersistedStore, useRemoteMcpTools, useScreenWidth, useScrollBlockage, useScrollDetection, useScrollY, useSearch, useStickToBottom, useWebMCPTools };
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { Mcp_Root } from "../routes.mjs";
4
+ import { isWebMCPAvailable } from "../webmcp/getModelContext.mjs";
5
+ import { createRemoteMcpTools } from "../webmcp/createRemoteMcpTools.mjs";
6
+ import { useEffect, useRef, useState } from "react";
7
+
8
+ //#region src/hooks/useRemoteMcpTools.ts
9
+ /**
10
+ * Tools of the hosted `@intlayer/mcp` server worth mirroring in the browser.
11
+ * The rest either needs API credentials or duplicates a page tool.
12
+ */
13
+ const INTLAYER_DOC_TOOL_NAMES = /* @__PURE__ */ new Set(["fetch-doc-chunks", "get-doc-by-slug"]);
14
+ const isIntlayerDocTool = (tool) => INTLAYER_DOC_TOOL_NAMES.has(tool.name);
15
+ /** `fetch-doc-chunks` → `fetchDocChunks`, matching the page's own tool names. */
16
+ const toCamelCase = (name) => name.replace(/-(\w)/g, (_match, character) => character.toUpperCase());
17
+ /**
18
+ * Bridges the tools of a remote MCP server into the page, so an agent in the
19
+ * browser gets the same retrieval an IDE agent has, without the page
20
+ * re-implementing them.
21
+ *
22
+ * Defaults to the documentation tools of the Intlayer MCP server. Discovery
23
+ * runs once per `serverUrl`; the other options are read at that moment.
24
+ *
25
+ * @returns The wrapped tools, empty until discovery completes or when no
26
+ * browser agent could use them.
27
+ */
28
+ const useRemoteMcpTools = (options = {}) => {
29
+ const [remoteTools, setRemoteTools] = useState([]);
30
+ const optionsRef = useRef(options);
31
+ const serverUrl = options.serverUrl ?? "https://mcp.intlayer.org";
32
+ useEffect(() => {
33
+ optionsRef.current = options;
34
+ });
35
+ useEffect(() => {
36
+ if (!isWebMCPAvailable()) return;
37
+ const abortController = new AbortController();
38
+ const { filter = isIntlayerDocTool, renameTool = toCamelCase, ...discoveryOptions } = optionsRef.current;
39
+ createRemoteMcpTools({
40
+ ...discoveryOptions,
41
+ serverUrl,
42
+ filter,
43
+ renameTool,
44
+ signal: abortController.signal
45
+ }).then(setRemoteTools).catch((error) => {
46
+ if (abortController.signal.aborted) return;
47
+ console.warn(`[WebMCP] MCP server unreachable: ${serverUrl}`, error);
48
+ });
49
+ return () => abortController.abort();
50
+ }, [serverUrl]);
51
+ return remoteTools;
52
+ };
53
+
54
+ //#endregion
55
+ export { useRemoteMcpTools };
56
+ //# sourceMappingURL=useRemoteMcpTools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRemoteMcpTools.mjs","names":[],"sources":["../../../src/hooks/useRemoteMcpTools.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Mcp_Root } from '../routes';\nimport {\n type CreateRemoteMcpToolsOptions,\n createRemoteMcpTools,\n} from '../webmcp/createRemoteMcpTools';\nimport { isWebMCPAvailable } from '../webmcp/getModelContext';\nimport type { WebMCPTool } from '../webmcp/types';\n\n/**\n * Tools of the hosted `@intlayer/mcp` server worth mirroring in the browser.\n * The rest either needs API credentials or duplicates a page tool.\n */\nconst INTLAYER_DOC_TOOL_NAMES = new Set([\n 'fetch-doc-chunks',\n 'get-doc-by-slug',\n]);\n\nconst isIntlayerDocTool: NonNullable<CreateRemoteMcpToolsOptions['filter']> = (\n tool\n) => INTLAYER_DOC_TOOL_NAMES.has(tool.name);\n\n/** `fetch-doc-chunks` → `fetchDocChunks`, matching the page's own tool names. */\nconst toCamelCase = (name: string): string =>\n name.replace(/-(\\w)/g, (_match, character: string) =>\n character.toUpperCase()\n );\n\nexport type UseRemoteMcpToolsOptions = Partial<\n Omit<CreateRemoteMcpToolsOptions, 'signal'>\n>;\n\n/**\n * Bridges the tools of a remote MCP server into the page, so an agent in the\n * browser gets the same retrieval an IDE agent has, without the page\n * re-implementing them.\n *\n * Defaults to the documentation tools of the Intlayer MCP server. Discovery\n * runs once per `serverUrl`; the other options are read at that moment.\n *\n * @returns The wrapped tools, empty until discovery completes or when no\n * browser agent could use them.\n */\nexport const useRemoteMcpTools = (\n options: UseRemoteMcpToolsOptions = {}\n): WebMCPTool[] => {\n const [remoteTools, setRemoteTools] = useState<WebMCPTool[]>([]);\n const optionsRef = useRef(options);\n const serverUrl = options.serverUrl ?? Mcp_Root;\n\n useEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n // Skip the discovery request when no agent could use its result.\n if (!isWebMCPAvailable()) return;\n\n const abortController = new AbortController();\n const {\n filter = isIntlayerDocTool,\n renameTool = toCamelCase,\n ...discoveryOptions\n } = optionsRef.current;\n\n createRemoteMcpTools({\n ...discoveryOptions,\n serverUrl,\n filter,\n renameTool,\n signal: abortController.signal,\n })\n .then(setRemoteTools)\n .catch((error: unknown) => {\n if (abortController.signal.aborted) return;\n console.warn(`[WebMCP] MCP server unreachable: ${serverUrl}`, error);\n });\n\n return () => abortController.abort();\n }, [serverUrl]);\n\n return remoteTools;\n};\n"],"mappings":";;;;;;;;;;;;AAeA,MAAM,0CAA0B,IAAI,IAAI,CACtC,oBACA,iBACF,CAAC;AAED,MAAM,qBACJ,SACG,wBAAwB,IAAI,KAAK,IAAI;;AAG1C,MAAM,eAAe,SACnB,KAAK,QAAQ,WAAW,QAAQ,cAC9B,UAAU,YAAY,CACxB;;;;;;;;;;;;AAiBF,MAAa,qBACX,UAAoC,CAAC,MACpB;CACjB,MAAM,CAAC,aAAa,kBAAkB,SAAuB,CAAC,CAAC;CAC/D,MAAM,aAAa,OAAO,OAAO;CACjC,MAAM,YAAY,QAAQ;CAE1B,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,gBAAgB;EAEd,IAAI,CAAC,kBAAkB,GAAG;EAE1B,MAAM,kBAAkB,IAAI,gBAAgB;EAC5C,MAAM,EACJ,SAAS,mBACT,aAAa,aACb,GAAG,qBACD,WAAW;EAEf,qBAAqB;GACnB,GAAG;GACH;GACA;GACA;GACA,QAAQ,gBAAgB;EAC1B,CAAC,CAAC,CACC,KAAK,cAAc,CAAC,CACpB,OAAO,UAAmB;GACzB,IAAI,gBAAgB,OAAO,SAAS;GACpC,QAAQ,KAAK,oCAAoC,aAAa,KAAK;EACrE,CAAC;EAEH,aAAa,gBAAgB,MAAM;CACrC,GAAG,CAAC,SAAS,CAAC;CAEd,OAAO;AACT"}
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { registerWebMCPTools } from "../webmcp/registerWebMCPTools.mjs";
4
+ import { useEffect, useRef } from "react";
5
+
6
+ //#region src/hooks/useWebMCPTools.ts
7
+ /** Identifies the tool set the browser currently sees. */
8
+ const getToolsSignature = (tools) => JSON.stringify(tools.map((tool) => [
9
+ tool.name,
10
+ tool.description,
11
+ tool.inputSchema ?? null,
12
+ tool.annotations ?? null
13
+ ]));
14
+ /**
15
+ * Exposes tools to browser agents for as long as the calling component is
16
+ * mounted, which scopes page-specific tools (a formatter, a scanner) to the
17
+ * page they belong to.
18
+ *
19
+ * Registration happens once per distinct tool set; `execute` handlers are
20
+ * read through a ref, so tools may close over fresh React state without being
21
+ * re-registered on every render. Renders nothing on the server.
22
+ *
23
+ * @param tools - Tools to expose while mounted.
24
+ */
25
+ const useWebMCPTools = (tools) => {
26
+ const toolsRef = useRef(tools);
27
+ const signature = getToolsSignature(tools);
28
+ useEffect(() => {
29
+ toolsRef.current = tools;
30
+ });
31
+ useEffect(() => {
32
+ const proxies = toolsRef.current.map((tool) => ({
33
+ ...tool,
34
+ execute: (input, options) => {
35
+ return (toolsRef.current.find((candidate) => candidate.name === tool.name) ?? tool).execute(input, options);
36
+ }
37
+ }));
38
+ return registerWebMCPTools(proxies);
39
+ }, [signature]);
40
+ };
41
+
42
+ //#endregion
43
+ export { useWebMCPTools };
44
+ //# sourceMappingURL=useWebMCPTools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWebMCPTools.mjs","names":[],"sources":["../../../src/hooks/useWebMCPTools.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { registerWebMCPTools } from '../webmcp/registerWebMCPTools';\nimport type { WebMCPTool, WebMCPToolExecuteOptions } from '../webmcp/types';\n\n/**\n * A tool of any input type. `never` is assignable from every input, so tools\n * declared with a precise input type fit in one list; the browser hands each\n * `execute` the object it parsed from the agent's JSON.\n */\nexport type AnyWebMCPTool = WebMCPTool<never>;\n\n/** Identifies the tool set the browser currently sees. */\nconst getToolsSignature = (tools: AnyWebMCPTool[]): string =>\n JSON.stringify(\n tools.map((tool) => [\n tool.name,\n tool.description,\n tool.inputSchema ?? null,\n tool.annotations ?? null,\n ])\n );\n\n/**\n * Exposes tools to browser agents for as long as the calling component is\n * mounted, which scopes page-specific tools (a formatter, a scanner) to the\n * page they belong to.\n *\n * Registration happens once per distinct tool set; `execute` handlers are\n * read through a ref, so tools may close over fresh React state without being\n * re-registered on every render. Renders nothing on the server.\n *\n * @param tools - Tools to expose while mounted.\n */\nexport const useWebMCPTools = (tools: AnyWebMCPTool[]): void => {\n const toolsRef = useRef<AnyWebMCPTool[]>(tools);\n const signature = getToolsSignature(tools);\n\n useEffect(() => {\n toolsRef.current = tools;\n });\n\n useEffect(() => {\n const proxies: AnyWebMCPTool[] = toolsRef.current.map((tool) => ({\n ...tool,\n execute: (input: unknown, options?: WebMCPToolExecuteOptions) => {\n const latest =\n toolsRef.current.find((candidate) => candidate.name === tool.name) ??\n tool;\n return latest.execute(input as never, options);\n },\n }));\n\n return registerWebMCPTools(proxies);\n }, [signature]);\n};\n"],"mappings":";;;;;;;AAcA,MAAM,qBAAqB,UACzB,KAAK,UACH,MAAM,KAAK,SAAS;CAClB,KAAK;CACL,KAAK;CACL,KAAK,eAAe;CACpB,KAAK,eAAe;AACtB,CAAC,CACH;;;;;;;;;;;;AAaF,MAAa,kBAAkB,UAAiC;CAC9D,MAAM,WAAW,OAAwB,KAAK;CAC9C,MAAM,YAAY,kBAAkB,KAAK;CAEzC,gBAAgB;EACd,SAAS,UAAU;CACrB,CAAC;CAED,gBAAgB;EACd,MAAM,UAA2B,SAAS,QAAQ,KAAK,UAAU;GAC/D,GAAG;GACH,UAAU,OAAgB,YAAuC;IAI/D,QAFE,SAAS,QAAQ,MAAM,cAAc,UAAU,SAAS,KAAK,IAAI,KACjE,KACW,CAAC,QAAQ,OAAgB,OAAO;GAC/C;EACF,EAAE;EAEF,OAAO,oBAAoB,OAAO;CACpC,GAAG,CAAC,SAAS,CAAC;AAChB"}
@@ -1,6 +1,6 @@
1
- import { DeferredAuthProvider, useIsAuthFetchEnabled } from "./DeferredAuthProvider.mjs";
2
1
  import { ThemeProvider, useTheme } from "./ThemeProvider.mjs";
3
2
  import { ReactQueryProvider, getQueryClient } from "./ReactQueryProvider.mjs";
3
+ import { DeferredAuthProvider, useIsAuthFetchEnabled } from "./DeferredAuthProvider.mjs";
4
4
  import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
5
5
 
6
6
  export { DeferredAuthProvider, ReactQueryDevtools, ReactQueryProvider, ThemeProvider, getQueryClient, useIsAuthFetchEnabled, useTheme };
@@ -1,12 +1,12 @@
1
1
  import { normalizeJsonLdUrl, normalizeJsonLdUrls } from "./normalizeJsonLdUrl.mjs";
2
- import { buildOrganizationJsonLd } from "./buildOrganizationJsonLd.mjs";
3
- import { buildSoftwareApplicationJsonLd } from "./buildSoftwareApplicationJsonLd.mjs";
4
- import { buildBreadcrumbsJsonLd } from "./buildBreadcrumbsJsonLd.mjs";
5
- import { buildWebsiteJsonLd } from "./buildWebsiteJsonLd.mjs";
6
2
  import { buildProductJsonLd } from "./buildProductJsonLd.mjs";
7
- import { buildItemListJsonLd } from "./buildItemListJsonLd.mjs";
8
- import { buildCreativeWorkJsonLd } from "./buildCreativeWorkJsonLd.mjs";
9
3
  import { buildAuthorJsonLd } from "./buildAuthorJsonLd.mjs";
4
+ import { buildBreadcrumbsJsonLd } from "./buildBreadcrumbsJsonLd.mjs";
5
+ import { buildCreativeWorkJsonLd } from "./buildCreativeWorkJsonLd.mjs";
10
6
  import { buildFAQPageJsonLd } from "./buildFAQPageJsonLd.mjs";
7
+ import { buildItemListJsonLd } from "./buildItemListJsonLd.mjs";
8
+ import { buildOrganizationJsonLd } from "./buildOrganizationJsonLd.mjs";
9
+ import { buildSoftwareApplicationJsonLd } from "./buildSoftwareApplicationJsonLd.mjs";
10
+ import { buildWebsiteJsonLd } from "./buildWebsiteJsonLd.mjs";
11
11
 
12
12
  export { buildAuthorJsonLd, buildBreadcrumbsJsonLd, buildCreativeWorkJsonLd, buildFAQPageJsonLd, buildItemListJsonLd, buildOrganizationJsonLd, buildProductJsonLd, buildSoftwareApplicationJsonLd, buildWebsiteJsonLd, normalizeJsonLdUrl, normalizeJsonLdUrls };