@golemui/gui-mcp 1.4.0 → 1.5.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/cli.js +2 -2
- package/{get-concept-sCo1L4XA.js → get-concept-BWtZV7zN.js} +615 -475
- package/json.js +1 -1
- package/lib.js +2 -2
- package/{list-dx-factories-B4gwqN7w.js → list-dx-factories-q0Z0pNnF.js} +36 -8
- package/package.json +5 -5
package/json.js
CHANGED
package/lib.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G, J, a, b, c, g, d, e, f, v } from "./get-concept-
|
|
2
|
-
import { D, a as a2, b as b2, c as c2, d as d2, e as e2, g as g2, l, f as f2, t } from "./list-dx-factories-
|
|
1
|
+
import { G, J, a, b, c, g, d, e, f, v } from "./get-concept-BWtZV7zN.js";
|
|
2
|
+
import { D, a as a2, b as b2, c as c2, d as d2, e as e2, g as g2, l, f as f2, t } from "./list-dx-factories-q0Z0pNnF.js";
|
|
3
3
|
export {
|
|
4
4
|
D as DX_CHECK_CODE_TOOL,
|
|
5
5
|
a2 as DX_GET_SPEC_TOOL,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as checkReactiveExpression, i as checkBooleanValidatorRules } from "./get-concept-
|
|
1
|
+
import { h as checkReactiveExpression, i as checkBooleanValidatorRules } from "./get-concept-BWtZV7zN.js";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { resolve, join, dirname } from "node:path";
|
|
@@ -294,7 +294,6 @@ function objectWithSuffix(specs) {
|
|
|
294
294
|
return ok(out);
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
-
const shortUUID = () => crypto.randomUUID().slice(0, 8);
|
|
298
297
|
const isInputWidget = (widget) => typeof widget !== "function" && widget.kind === "input";
|
|
299
298
|
const isLayoutWidget = (widget) => typeof widget !== "function" && widget.kind === "layout";
|
|
300
299
|
const isFunctionWidget = (widget) => typeof widget === "function";
|
|
@@ -364,7 +363,7 @@ const functionWidgetDecoder = new Decoder((json) => {
|
|
|
364
363
|
touched: void 0,
|
|
365
364
|
translate: void 0
|
|
366
365
|
});
|
|
367
|
-
fnWidget.uid = fnWidget.uid || widget.uid
|
|
366
|
+
fnWidget.uid = fnWidget.uid || widget.uid;
|
|
368
367
|
fnWidget.type = widget.type;
|
|
369
368
|
fnWidget.path = widget.path;
|
|
370
369
|
return ok(fnWidget);
|
|
@@ -431,6 +430,9 @@ function assignDeterministicUids(root) {
|
|
|
431
430
|
}
|
|
432
431
|
function assignUidByPosition(widget, positionUid) {
|
|
433
432
|
if (isFunctionWidget(widget)) {
|
|
433
|
+
if (!widget.uid) {
|
|
434
|
+
widget.uid = `${positionUid}.f`;
|
|
435
|
+
}
|
|
434
436
|
return;
|
|
435
437
|
}
|
|
436
438
|
if (!widget.uid) {
|
|
@@ -476,14 +478,14 @@ function resolveDxFramework() {
|
|
|
476
478
|
return DX_FRAMEWORKS.includes(raw ?? "") ? raw : "react";
|
|
477
479
|
}
|
|
478
480
|
const FRAMEWORK_SETUP = {
|
|
479
|
-
react: "RENDER (React) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-react'; import type { FormSubmitEvent } from '@golemui/core';`, then render `<GuiForm config={{ formDef: form }} formSubmit={(e: FormSubmitEvent) => { /* e.data is the form data */ }} />`. A `gui.displays.display(() => <h2>…</h2>)` returns React JSX.",
|
|
480
|
-
angular: "RENDER (Angular) — `import { gui } from '@golemui/gui-shared'; import { FormComponent } from '@golemui/gui-angular';`, add `FormComponent` to the standalone component's `imports`, then in the template `<gui-form [config]=\"{ formDef: form }\" (formSubmit)=\"onSubmit($event)\"></gui-form>` — `$event` is a `FormSubmitEvent` (type from `@golemui/core`), `$event.data` is the form data.",
|
|
481
|
-
vue: "RENDER (Vue) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-vue';`, then `<GuiForm :config=\"{ formDef: form }\" @form-submit=\"onSubmit\" />` — the handler receives a `FormSubmitEvent` (`.data` is the form data). The event is `form-submit` (kebab-case), not `formSubmit`.",
|
|
482
|
-
lit: "RENDER (Lit) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers the `<gui-form>` custom element), then `<gui-form .config=${{ formDef: form }} @" + formEventNames.submit + "=${(e: CustomEvent) => { /* e.detail is the FormSubmitEvent; e.detail.data */ }}></gui-form>`. The event name is `" + formEventNames.submit + "` (camelCase) — Lit dispatches a raw CustomEvent, so there is no kebab-case alias.",
|
|
481
|
+
react: "RENDER (React) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-react'; import type { FormSubmitEvent } from '@golemui/core';`, then render `<GuiForm config={{ formDef: form }} formSubmit={(e: FormSubmitEvent) => { /* e.data is the form data */ }} />`. A `gui.displays.display(() => <h2>…</h2>)` returns React JSX. For SSR (Next.js App Router) await `preloadFormWidgets({ widgetLoaders })` from `@golemui/core` before the first render on both server and client (a `'use client'` provider that `use()`s a module-scope promise); `widgetLoaders` comes from `@golemui/gui-react`. Set `formName`.",
|
|
482
|
+
angular: "RENDER (Angular) — `import { gui } from '@golemui/gui-shared'; import { FormComponent } from '@golemui/gui-angular';`, add `FormComponent` to the standalone component's `imports`, then in the template `<gui-form [config]=\"{ formDef: form }\" (formSubmit)=\"onSubmit($event)\"></gui-form>` — `$event` is a `FormSubmitEvent` (type from `@golemui/core`), `$event.data` is the form data. For SSR (`@angular/platform-server`) await `preloadFormWidgets({ widgetLoaders })` from `@golemui/core` before bootstrap on both server and client; `widgetLoaders` comes from `@golemui/gui-angular`. An explicit `formName` is required and handlers run in the browser only.",
|
|
483
|
+
vue: "RENDER (Vue) — `import { gui } from '@golemui/gui-shared'; import { GuiForm } from '@golemui/gui-vue';`, then `<GuiForm :config=\"{ formDef: form }\" @form-submit=\"onSubmit\" />` — the handler receives a `FormSubmitEvent` (`.data` is the form data). The event is `form-submit` (kebab-case), not `formSubmit`. For SSR (Nuxt) await `preloadFormWidgets({ widgetLoaders })` from `@golemui/core` before the first render on both server and client (a Nuxt plugin); `widgetLoaders` comes from `@golemui/gui-vue`.",
|
|
484
|
+
lit: "RENDER (Lit) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers the `<gui-form>` custom element), then `<gui-form .config=${{ formDef: form }} @" + formEventNames.submit + "=${(e: CustomEvent) => { /* e.detail is the FormSubmitEvent; e.detail.data */ }}></gui-form>`. The event name is `" + formEventNames.submit + "` (camelCase) — Lit dispatches a raw CustomEvent, so there is no kebab-case alias. For SSR (Astro, plain Node) the server renders the whole form with `renderGuiHtml` from `@golemui/lit/ssr` (needs `@lit-labs/ssr`) after `preloadFormWidgets({ widgetLoaders })`; the client preloads again and calls `resumeServerRenderedForm` from `@golemui/lit`. `formName` is mandatory; custom widgets register with `safeDefine` from `@golemui/lit`, not `@customElement`.",
|
|
483
485
|
vanilla: "RENDER (vanilla JS) — `import { gui } from '@golemui/gui-shared'; import '@golemui/gui-lit';` (registers `<gui-form>`), then `const el = document.querySelector('gui-form'); el.config = { formDef: form }; el.addEventListener('" + formEventNames.submit + "', (e) => { /* e.detail.data */ });`. In TypeScript, type the element — `import type { FormElement } from '@golemui/gui-lit'; const el = document.querySelector<FormElement>('gui-form');` — the published types do not register `gui-form` in `HTMLElementTagNameMap`, so an untyped `querySelector` yields `Element` and `el.config` fails to compile."
|
|
484
486
|
};
|
|
485
487
|
function commonNote(fw = "react") {
|
|
486
|
-
return "GolemUI builds FORMS — data collection and validation. It is NOT a general-purpose UI toolkit: it never renders documents, page content, or markdown for display. A form is just an array of these items: `export const form = [ /* items */ ];`. " + FRAMEWORK_SETUP[fw] + " Import the component stylesheet ONCE — `@golemui/gui-components/index.css` — or the form renders unstyled. To RECEIVE A SUBMIT: add a `gui.actions.button({ label, actionType: 'submit' })` to the form and listen for the submit on the host component (the RENDER line above shows how for your framework) — the handler gets a `FormSubmitEvent` whose `.data` is the collected form data. To DISABLE submit until the form is valid, add `disabled: { when: '$formIsInvalid || $form.<requiredField> === undefined' }` to that button. `$formIsInvalid` is a built-in validity flag, but validation NEVER runs at mount, so on the pristine form it is `false` and `$formIsInvalid` ALONE leaves the button ENABLED while required fields are still empty — the extra data check covers that gap. See the conditional-and-state-props pattern. The SAME `formDef` renders in every framework (React/Angular/Vue/Lit/vanilla) — only the host wrapper changes. FORM-LEVEL CONFIG — `formDef` is ALWAYS the bare array. Anything form-wide (named `states`, `validateOn`) goes in a sibling `formConfig` on the config (`config={{ formDef: form, formConfig: { states, validateOn } }}`), NEVER inside `formDef`. Do NOT wrap the array as `{ states, form: [...] }` and pass THAT as `formDef` — `formDef` is typed `Record<string, any>` so it COMPILES, but the `gui.*` items are never resolved and the form renders BLANK with no error. See the form-level-states pattern. Common fields like `include`/`exclude` (conditional visibility) go INSIDE a factory’s config argument — never spread them onto the result (`{ ...gui.inputs.x(...), include }` compiles but silently does nothing). See the conditional-visibility pattern. STATIC CONTENT — a section heading or any non-input text/block is the HOST’s job, not GolemUI’s: use `gui.displays.display(() => <h2>…</h2>)` returning your framework’s own node (React JSX, Vue/Angular/Lit node) — it needs no dependency and always renders. MARKDOWN has exactly ONE use: `gui.inputs.markdown`, an INPUT where the user EDITS markdown (its value is their markdown string). There is NO markdown-for-display widget — never use markdown to render a heading or content; use `display` for that. VALIDATOR `type` — one rule, three cases (so you never have to guess): (1) choice widgets (`dropdown`, `radiogroup`, `select`) REQUIRE an explicit `type`: `validator: { type: 'string', required: true }`. (2) `repeater` (array) validators auto-supply `type
|
|
488
|
+
return "GolemUI builds FORMS — data collection and validation. It is NOT a general-purpose UI toolkit: it never renders documents, page content, or markdown for display. A form is just an array of these items: `export const form = [ /* items */ ];`. " + FRAMEWORK_SETUP[fw] + " Import the component stylesheet ONCE — `@golemui/gui-components/index.css` — or the form renders unstyled. To RECEIVE A SUBMIT: add a `gui.actions.button({ label, actionType: 'submit' })` to the form and listen for the submit on the host component (the RENDER line above shows how for your framework) — the handler gets a `FormSubmitEvent` whose `.data` is the collected form data. To DISABLE submit until the form is valid, add `disabled: { when: '$formIsInvalid || $form.<requiredField> === undefined' }` to that button. `$formIsInvalid` is a built-in validity flag, but validation NEVER runs at mount, so on the pristine form it is `false` and `$formIsInvalid` ALONE leaves the button ENABLED while required fields are still empty — the extra data check covers that gap. See the conditional-and-state-props pattern. The SAME `formDef` renders in every framework (React/Angular/Vue/Lit/vanilla) — only the host wrapper changes. FORM-LEVEL CONFIG — `formDef` is ALWAYS the bare array. Anything form-wide (named `states`, `validateOn`) goes in a sibling `formConfig` on the config (`config={{ formDef: form, formConfig: { states, validateOn } }}`), NEVER inside `formDef`. Do NOT wrap the array as `{ states, form: [...] }` and pass THAT as `formDef` — `formDef` is typed `Record<string, any>` so it COMPILES, but the `gui.*` items are never resolved and the form renders BLANK with no error. See the form-level-states pattern. Common fields like `include`/`exclude` (conditional visibility) go INSIDE a factory’s config argument — never spread them onto the result (`{ ...gui.inputs.x(...), include }` compiles but silently does nothing). See the conditional-visibility pattern. STATIC CONTENT — a section heading or any non-input text/block is the HOST’s job, not GolemUI’s: use `gui.displays.display(() => <h2>…</h2>)` returning your framework’s own node (React JSX, Vue/Angular/Lit node) — it needs no dependency and always renders. MARKDOWN has exactly ONE use: `gui.inputs.markdown`, an INPUT where the user EDITS markdown (its value is their markdown string). There is NO markdown-for-display widget — never use markdown to render a heading or content; use `display` for that. VALIDATOR `type` — one rule, three cases (so you never have to guess): (1) choice widgets (`dropdown`, `radiogroup`, `select`) REQUIRE an explicit `type`: `validator: { type: 'string', required: true }`. (2) `repeater` (array), `tags` (array), `fileUpload` (file) and `multiFileUpload` (files) validators auto-supply their `type` — supply only the rules, e.g. `validator: { required: true, minItems: 1 }`, never `type`. (3) everything else (text, number, date) takes the loose validator with NO `type`: `validator: { required: true }`. EVENT HANDLERS — `onChange`/`onLoad`/`onFilter`/`onBlur` (inputs/layouts) and `onClick` (actions) are FUNCTIONS, never bare strings: return a string to dispatch a host event by that name (`onChange: () => 'languageChanged'`), or take the event to push live changes (`onChange: (event) => event.update({ path: 'city', options: [...] })`).";
|
|
487
489
|
}
|
|
488
490
|
const PATTERNS = [
|
|
489
491
|
{
|
|
@@ -784,6 +786,32 @@ const INPUTS = [
|
|
|
784
786
|
"Selection is never silently blocked; cap it with an array validator — `{ type: 'array', maxItems }` — so the user is told why."
|
|
785
787
|
]
|
|
786
788
|
},
|
|
789
|
+
{
|
|
790
|
+
factory: "fileUpload",
|
|
791
|
+
namespace: "inputs",
|
|
792
|
+
docSlug: "file-upload",
|
|
793
|
+
call: "gui.inputs.fileUpload(path, { label?, accept?, maxSize?, buttonLabel?, validator? })",
|
|
794
|
+
example: "gui.inputs.fileUpload('cv', { label: 'CV', accept: ['application/pdf', '.docx'], maxSize: 5 * 1024 * 1024, validator: { required: true } })",
|
|
795
|
+
notes: [
|
|
796
|
+
"Single-file upload rendered as a one-line input: the box is the drop target and holds the upload button; while the file uploads the box itself becomes the progress bar, and once done it shows the file name with a remove button.",
|
|
797
|
+
"REQUIRES a host transport: pass `dependencies: { uploadService: { upload(file, { id, path, onProgress, signal }) => Promise<unknown>, remove?(item) => Promise<void> } }` in the init config (next to `markdown`). Keep the object reference stable (module level) — a new `config` identity re-initializes the form. Without it the widget renders disabled with an inline error.",
|
|
798
|
+
'The value is a plain envelope, never the `File`: `{ id, name, size, type, status: "uploading" | "uploaded" | "error", error?, data? }` where `data` is exactly what `upload` resolved with. Preload a value from the server with `status: "uploaded"`. Removing awaits `uploadService.remove(item)` (when provided) before clearing.',
|
|
799
|
+
"The validator auto-supplies `type: 'file'`. `blockPendingUploads` (default true) fails while the file is still uploading or failed, so a half-finished upload can never be submitted; message keys: `invalid`, `required`, `pendingUploads`.",
|
|
800
|
+
'`accept` (`[".pdf", "image/*", "application/pdf"]`) and `maxSize` (bytes) are checked BEFORE uploading; a refused or failed file stays in the box with the reason, a retry and a remove button — it is never dropped silently. Messages: `acceptMessage`, `maxSizeMessage`; accessible names: `removeAriaLabel`, `cancelAriaLabel`, `retryLabel` (`{name}` token).'
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
factory: "multiFileUpload",
|
|
805
|
+
namespace: "inputs",
|
|
806
|
+
docSlug: "multi-file-upload",
|
|
807
|
+
call: "gui.inputs.multiFileUpload(path, { label?, accept?, maxSize?, buttonLabel?, validator? })",
|
|
808
|
+
example: "gui.inputs.multiFileUpload('attachments', { label: 'Attachments', accept: ['image/*'], validator: { required: true, maxItems: 3 } })",
|
|
809
|
+
notes: [
|
|
810
|
+
"The array variant of `fileUpload` (same box, button, progress bar and `uploadService`). Files upload ONE AT A TIME; every finished file becomes a pill inside the box and the value is an array of envelopes.",
|
|
811
|
+
"A failed file pauses the queue until it is retried or removed. The count is never silently blocked: cap it with `maxItems` so the user is told why.",
|
|
812
|
+
"The validator auto-supplies `type: 'files'`; rules: `required` (non-empty), `minItems`, `maxItems`, `blockPendingUploads` (default true); message keys: `invalid`, `required`, `minItems`, `maxItems`, `pendingUploads`."
|
|
813
|
+
]
|
|
814
|
+
},
|
|
787
815
|
{
|
|
788
816
|
factory: "rangeCalendar",
|
|
789
817
|
namespace: "inputs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@golemui/gui-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0-rc.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"ajv": "^8.17.1",
|
|
44
44
|
"ajv-formats": "^3.0.1",
|
|
45
45
|
"typescript": "^5.9.2",
|
|
46
|
-
"@golemui/gui-schemas": "1.
|
|
47
|
-
"@golemui/gui-shared": "1.
|
|
48
|
-
"@golemui/gui-validators": "1.
|
|
49
|
-
"@golemui/core": "1.
|
|
46
|
+
"@golemui/gui-schemas": "1.5.0-rc.0",
|
|
47
|
+
"@golemui/gui-shared": "1.5.0-rc.0",
|
|
48
|
+
"@golemui/gui-validators": "1.5.0-rc.0",
|
|
49
|
+
"@golemui/core": "1.5.0-rc.0",
|
|
50
50
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
51
51
|
"zod": "^4.0.0",
|
|
52
52
|
"@standard-schema/spec": "^1.0.0"
|