@polyxd/mcp 0.4.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/LICENSE +202 -0
- package/NOTICE +224 -0
- package/README.md +151 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +13 -0
- package/dist/bin.js.map +1 -0
- package/dist/check.d.ts +49 -0
- package/dist/check.js +214 -0
- package/dist/check.js.map +1 -0
- package/dist/http.d.ts +34 -0
- package/dist/http.js +198 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/output-schemas.d.ts +376 -0
- package/dist/output-schemas.js +186 -0
- package/dist/output-schemas.js.map +1 -0
- package/dist/packs.generated.d.ts +11 -0
- package/dist/packs.generated.js +178 -0
- package/dist/packs.generated.js.map +1 -0
- package/dist/prompt.d.ts +14 -0
- package/dist/prompt.generated.d.ts +2 -0
- package/dist/prompt.generated.js +4 -0
- package/dist/prompt.generated.js.map +1 -0
- package/dist/prompt.js +31 -0
- package/dist/prompt.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.js +256 -0
- package/dist/server.js.map +1 -0
- package/dist/spec-files.generated.d.ts +3 -0
- package/dist/spec-files.generated.js +159 -0
- package/dist/spec-files.generated.js.map +1 -0
- package/dist/spec.d.ts +26 -0
- package/dist/spec.js +40 -0
- package/dist/spec.js.map +1 -0
- package/dist/view-file.d.ts +2 -0
- package/dist/view-file.js +16 -0
- package/dist/view-file.js.map +1 -0
- package/dist/view.html +33 -0
- package/package.json +61 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export const PACKS = [
|
|
2
|
+
{
|
|
3
|
+
"name": "material3",
|
|
4
|
+
"displayName": "Material 3",
|
|
5
|
+
"template": false,
|
|
6
|
+
"package": "@polyxd/ds-material3",
|
|
7
|
+
"description": "Material 3 design-system pack"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "antd",
|
|
11
|
+
"displayName": "Ant Design",
|
|
12
|
+
"template": false,
|
|
13
|
+
"package": "@polyxd/ds-antd",
|
|
14
|
+
"description": "Ant Design design-system pack"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "bootstrap",
|
|
18
|
+
"displayName": "Bootstrap 5",
|
|
19
|
+
"template": false,
|
|
20
|
+
"package": "@polyxd/ds-bootstrap",
|
|
21
|
+
"description": "Bootstrap 5 design-system pack"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "carbon",
|
|
25
|
+
"displayName": "IBM Carbon",
|
|
26
|
+
"template": false,
|
|
27
|
+
"package": "@polyxd/ds-carbon",
|
|
28
|
+
"description": "IBM Carbon design-system pack"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "chakra",
|
|
32
|
+
"displayName": "Chakra UI 3",
|
|
33
|
+
"template": false,
|
|
34
|
+
"package": "@polyxd/ds-chakra",
|
|
35
|
+
"description": "Chakra UI 3 design-system pack"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "fluent",
|
|
39
|
+
"displayName": "Microsoft Fluent 2",
|
|
40
|
+
"template": false,
|
|
41
|
+
"package": "@polyxd/ds-fluent",
|
|
42
|
+
"description": "Microsoft Fluent 2 design-system pack"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "govuk",
|
|
46
|
+
"displayName": "GOV.UK Frontend",
|
|
47
|
+
"template": false,
|
|
48
|
+
"package": "@polyxd/ds-govuk",
|
|
49
|
+
"description": "GOV.UK Frontend design-system pack"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "mantine",
|
|
53
|
+
"displayName": "Mantine 8",
|
|
54
|
+
"template": false,
|
|
55
|
+
"package": "@polyxd/ds-mantine",
|
|
56
|
+
"description": "Mantine 8 design-system pack"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "polaris",
|
|
60
|
+
"displayName": "Shopify Polaris",
|
|
61
|
+
"template": false,
|
|
62
|
+
"package": "@polyxd/ds-polaris",
|
|
63
|
+
"description": "Shopify Polaris design-system pack"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "primer",
|
|
67
|
+
"displayName": "GitHub Primer",
|
|
68
|
+
"template": false,
|
|
69
|
+
"package": "@polyxd/ds-primer",
|
|
70
|
+
"description": "GitHub Primer design-system pack"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "radix",
|
|
74
|
+
"displayName": "Radix Themes 3",
|
|
75
|
+
"template": false,
|
|
76
|
+
"package": "@polyxd/ds-radix",
|
|
77
|
+
"description": "Radix Themes 3 design-system pack"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "shadcn",
|
|
81
|
+
"displayName": "shadcn/ui on Tailwind CSS",
|
|
82
|
+
"template": false,
|
|
83
|
+
"package": "@polyxd/ds-shadcn",
|
|
84
|
+
"description": "shadcn/ui on Tailwind CSS design-system pack"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "spectrum",
|
|
88
|
+
"displayName": "Adobe Spectrum 2",
|
|
89
|
+
"template": false,
|
|
90
|
+
"package": "@polyxd/ds-spectrum",
|
|
91
|
+
"description": "Adobe Spectrum 2 design-system pack"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "brutalist",
|
|
95
|
+
"displayName": "Brutalist",
|
|
96
|
+
"template": true,
|
|
97
|
+
"package": "@polyxd/ds-brutalist",
|
|
98
|
+
"description": "Brutalist template pack: an original design to start from and make your own"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "civic",
|
|
102
|
+
"displayName": "Civic",
|
|
103
|
+
"template": true,
|
|
104
|
+
"package": "@polyxd/ds-civic",
|
|
105
|
+
"description": "Civic template pack: an original design to start from and make your own"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "editorial",
|
|
109
|
+
"displayName": "Editorial",
|
|
110
|
+
"template": true,
|
|
111
|
+
"package": "@polyxd/ds-editorial",
|
|
112
|
+
"description": "Editorial template pack: an original design to start from and make your own"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "finance",
|
|
116
|
+
"displayName": "Finance",
|
|
117
|
+
"template": true,
|
|
118
|
+
"package": "@polyxd/ds-finance",
|
|
119
|
+
"description": "Finance template pack: an original design to start from and make your own"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "glass",
|
|
123
|
+
"displayName": "Glass",
|
|
124
|
+
"template": true,
|
|
125
|
+
"package": "@polyxd/ds-glass",
|
|
126
|
+
"description": "Glass template pack: an original design to start from and make your own"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "health",
|
|
130
|
+
"displayName": "Health",
|
|
131
|
+
"template": true,
|
|
132
|
+
"package": "@polyxd/ds-health",
|
|
133
|
+
"description": "Health template pack: an original design to start from and make your own"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"name": "mono",
|
|
137
|
+
"displayName": "Mono",
|
|
138
|
+
"template": true,
|
|
139
|
+
"package": "@polyxd/ds-mono",
|
|
140
|
+
"description": "Mono template pack: an original design to start from and make your own"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "neon",
|
|
144
|
+
"displayName": "Neon",
|
|
145
|
+
"template": true,
|
|
146
|
+
"package": "@polyxd/ds-neon",
|
|
147
|
+
"description": "Neon template pack: an original design to start from and make your own"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "pastel",
|
|
151
|
+
"displayName": "Pastel",
|
|
152
|
+
"template": true,
|
|
153
|
+
"package": "@polyxd/ds-pastel",
|
|
154
|
+
"description": "Pastel template pack: an original design to start from and make your own"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "sketch",
|
|
158
|
+
"displayName": "Sketch",
|
|
159
|
+
"template": true,
|
|
160
|
+
"package": "@polyxd/ds-sketch",
|
|
161
|
+
"description": "Sketch template pack: an original design to start from and make your own"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "terminal",
|
|
165
|
+
"displayName": "Terminal",
|
|
166
|
+
"template": true,
|
|
167
|
+
"package": "@polyxd/ds-terminal",
|
|
168
|
+
"description": "Terminal template pack: an original design to start from and make your own"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "wireframe",
|
|
172
|
+
"displayName": "Wireframe",
|
|
173
|
+
"template": true,
|
|
174
|
+
"package": "@polyxd/ds-wireframe",
|
|
175
|
+
"description": "Wireframe template pack: an original design to start from and make your own"
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
//# sourceMappingURL=packs.generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packs.generated.js","sourceRoot":"","sources":["../src/packs.generated.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spec as instructions for the host's model. Everything the server says about writing a
|
|
3
|
+
* document lives in this module.
|
|
4
|
+
*
|
|
5
|
+
* The generator prompt itself is not written here: it is the one `@polyxd/runtime` builds from the
|
|
6
|
+
* spec, so a model writing documents over MCP gets the same instructions as one driven by the runtime.
|
|
7
|
+
*/
|
|
8
|
+
import { SPEC_VERSION } from "@polyxd/spec/browser";
|
|
9
|
+
declare const SYSTEM_PROMPT: string;
|
|
10
|
+
export { SPEC_VERSION, SYSTEM_PROMPT };
|
|
11
|
+
/** How to use this server's tools, for a model that has the generator prompt above. */
|
|
12
|
+
export declare const MCP_NOTES = "How this works over MCP\n\nYou are the generator. Write the Polyxd UI document yourself, following the prompt below, and pass it to the tools as the \"document\" argument instead of writing JSON into the chat.\n\n1. Write the document. Put the values the screen shows in \"data\" (the polyxd_show and polyxd_validate tools take it as a separate \"data\" argument, or inside the document). Take those values from the conversation or from other tools' results; never make figures up. If a chart or table needs values nobody gave you (say, daily numbers when you only have weekly totals), leave that part out and say what is missing; never fill a series with plausible numbers.\n2. Call polyxd_validate. Fix every error at the JSON Pointer it gives, then validate again. Warnings are advice.\n3. Optionally call polyxd_verify for the verifier's document checks, with a Design Direction if the user has one.\n4. Call polyxd_show to put the screen in front of the user. It validates first and shows nothing if there are errors. Pick a design-system pack with \"pack\" (default material3; polyxd_packs lists them).\n\nActions: nobody has registered capabilities with this server, so rule 3's \"capabilities listed\" means the action names you choose. Name each action for what you will do when it arrives, as \"domain.verb\" (for example \"task.save\" or \"booking.cancel\"). When the user presses an action in the shown screen, you receive a chat message from the user naming the action, the button's label and the action's context with the values the user entered. Act on it as if the user had asked in words. \"ui.dismiss\" (closing the screen) arrives the same way. Pressing a button only tells you what the user chose: nothing has been submitted, paid, booked or saved. Never say an order was placed, a return started or a payment made unless another tool of yours actually did it; say what the user chose and what would happen next. A screen that asks the user to choose, fill in or confirm something always ends with its main action as a button (for example \"Request \u00A334.76 each\"), so they can act on it; a screen that only reports needs none.\n\nUse polyxd_components for any component's full definition, and read the example documents (resources polyxd://examples/...) for complete, valid documents.";
|
|
13
|
+
/** The whole guide: how to use the tools, then the generator prompt. */
|
|
14
|
+
export declare function guide(): string;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const SPEC_VERSION = "0.3.0";
|
|
2
|
+
export declare const SYSTEM_PROMPT = "You generate just-in-time user interfaces as JSON, in the Polyxd UI document format. Output ONE JSON object and nothing else.\n\nDocument shape: {\"specVersion\":\"0.3.0\",\"surface\":{\"id\",\"title\",\"intent\",\"pattern\"?},\"root\":\"<id>\",\"components\":[...]}.\nComponents are a FLAT list; each has a unique \"id\" and \"component\" (its type). Containers reference children by id STRINGS (\"children\": [\"a\",\"b\"]); never nest component objects inside other components.\n\nRules:\n1. Data comes from the host. Bind values with {\"path\":\"/json/pointer\"}; pointers start at the root of the DATA object (DATA {\"card\":{\"id\":\"c1\"}} \u2192 {\"path\":\"/card/id\"}, never \"/data/card/id\"). Never type numbers, prices, names or dates from the data as literal text; never invent data.\n2. Inside a repeated item (Collection items template, Table columns, Chart series, Comparison attributes) use paths RELATIVE to the item, e.g. {\"path\":\"amount\"}.\n3. Actions: {\"event\":{\"name\":\"<capability>\",\"context\":{...}}}. Use ONLY the capabilities listed. \"ui.dismiss\" closes the surface.\n4. Destructive capabilities must be triggered from a Confirm. Consequential ones need a Confirm or a review step first.\n5. At most one primary action visible at a time (a Form's submit counts). At most 6 inputs per view; use Steps for more.\n6. Labels say what happens (\"Send \u00A320\", \"Freeze card\"), in sentence case. Give every component that represents a thing from the data a stable \"key\" in lower_snake_case (e.g. \"card_status\").\n7. Collections and Tables need an \"empty\" Status when the list may be empty.\n8. If no listed capability can do what was asked, show a Status explaining that instead of a fake interface.\n9. Format numbers, money and dates with \"format\", never by writing them into strings.\n10. There is no template engine. Text is either literal or a binding; \"{{budget}}\" or \"${spent}\" in a string reaches the screen exactly as written.\n11. Bind text to a field that holds text. A pointer at an object or a list prints as \"[object Object]\"; point at the string inside it.\n12. Show people names, not internal ids. If a record has both \"id\" and \"name\", the screen gets \"name\"; the id belongs in an action's context.\n13. Never use AppBar, Custom, Footer, Frame or Outlet: the product's shell is authored, and your surface renders inside it. Never draw navigation, a header or a footer around a surface.\n\nComponents (* = required):\n- Action(label*: text|{path}, action*: {event:{name,context?}}, emphasis: primary|secondary|tertiary, tone: default|danger, disabled: bool|{path}, description: text|{path}, copy: text|{path}, shortcut: string)\n A button that triggers a host capability. Use for: Anything the user can do that isn't typing or choosing.\n- ActionBar(children*: [ids])\n The set of actions for a surface or section; the renderer places it where that platform expects. Use for: Two or more actions that apply to the whole surface or section.\n- ActionMenu(label*: text|{path}, kind: overflow|dropdown|split|context, children*: [ids], primary: id)\n Secondary actions behind one control: an overflow menu, a dropdown, a split button or a context menu. Use for: Three or more secondary actions on a row, a card or a page header; One usual action with rarer alternatives (split: 'Save' with 'Save as draft').\n- Card(title*: text|{path}, subtitle: text|{path}, media: id, badge: {text,tone?}, children: [ids], action: {event:{name,context?}}, progress: {value,label?})\n One self-contained entity (an account, an order, a place), optionally actionable as a whole. Use for: Items in a Collection that each represent an entity; A single entity summary in a larger surface.\n- Chart(intent*: trend|comparison|composition|distribution|relationship|flow|hierarchy|matrix|range, data*: {path}, title*: text|{path}, summary*: text|{path}, x*: {path,label,format?}, series*: [{key?,path,label,format?}])\n A visual summary of data, always paired with a text summary. Use for: A trend, comparison, share or spread matters more than exact values.\n- Choice(label*: text|{path}, options*: [{value,label}]|{path,valuePath,labelPath}, value*: {path}, mode: single|multiple, help: text|{path}, required: boolean)\n Pick one or several options from a known set. The renderer chooses the control. Use for: Any choice from a known set of options.\n- Code(text*: text|{path}, label: text|{path}, language: string, copyable: boolean, wrap: boolean, secret: bool|{path})\n Code, a command or preformatted text, shown as written and copyable. Use for: A command to run, a snippet to paste, an identifier to copy; Structured text whose spacing matters.\n- CodeInput(label*: text|{path}, value*: {path}, length: integer, kind: numeric|alphanumeric, help: text|{path}, required: bool|{path}, action: {event:{name,context?}})\n A one-time code or PIN, typed into one box per character. Use for: A verification code from email or SMS; A PIN or a short recovery code.\n- Collection(items*: {path,componentId}, label*: text|{path}, empty: id, selection: none|single|multiple, selected: {path}, layout: auto|list|grid|timeline|carousel|calendar, datePath: path, page: {index?,size?,total?,action?}, bulkActions: id, reorderable: boolean, order: {path})\n A list of items from host data, each rendered with the same template. Use for: Browsing a set of similar entities; Search results.\n- ColorInput(label*: text|{path}, value*: {path}, swatches: [{value,label}]|{path,valuePath,labelPath}, format: hex|rgb|hsl, alpha: boolean, help: text|{path}, required: bool|{path})\n Pick a colour: from swatches the host offers, or any colour. Use for: A colour for a label, a tag, a calendar, a theme; Choosing from a set of brand colours.\n- Columns(children*: [ids], layout: two-thirds|one-third|halves|thirds|sidebar-start|sidebar-end, collapse: compact|medium|never, align: start|center|stretch)\n Side-by-side columns that collapse to a stack on compact layouts: a main column with a narrower one beside it, or equal halves. Use for: A form with help beside it; a record with a summary beside it; a list beside a preview; Anywhere a wide screen should use its width without the compact screen paying for it.\n- Comparison(items*: {path}, itemTitle*: path, attributes*: [{key,label,path,format?,better?,group?}], choose: {label,action}, recommended: text|{path}, summary: text|{path}, recommendedReason: text|{path})\n Compare a few options across the same attributes, and choose one. Use for: Choosing between 2\u20134 plans, products, routes or offers.\n- Confirm(title*: text|{path}, message: text|{path}, consequence: text|{path}, summary: id, severity*: consequential|destructive, confirm*: {label,action}, cancel: {label,action}, typeToConfirm: text|{path}, amount: {value,format?}, subject: {title,subtitle?,avatar?}, consequences: [{title,detail?,icon?}])\n Asks the user to confirm a consequential or destructive action, showing what will happen. Use for: Moving money, deleting data, sending on someone's behalf, anything irreversible.\n- DateInput(label*: text|{path}, value*: {path}, kind: date|time|datetime|dateRange|month|year, multiple: boolean, min: text|{path}, max: text|{path}, help: text|{path}, required: boolean)\n A date, time, date-time or date range. Use for: Any date or time entry.\n- DetailList(items*: [{key?,label,value,format?,total?}], title: text|{path}, variant: default|receipt, layout: rows|grid, rowAction: {event:{name,context?}})\n Label/value pairs describing one thing (a summary, a receipt, a review step). Use for: Reviewing before submitting; Showing the attributes of one entity.\n- Disclosure(summary*: text|{path}, children*: [ids], open: boolean)\n Progressive disclosure: secondary content hidden behind a toggle. Use for: Details most people don't need (fees breakdown, advanced options, help text).\n- FileInput(label*: text|{path}, value*: {path}, accept: [string], multiple: boolean, maxSize: integer, help: text|{path}, required: bool|{path})\n Choose or drop files to attach or upload. Use for: Attaching a document, a receipt, a photo; Importing a file the product reads.\n- FilterPanel(label: text|{path}, children*: [ids], results*: id, resultCount: {path}, clear: {event:{name,context?}})\n Filters for a list of results, with the result count. Use for: Browsing results people narrow by several attributes; More than two filters, or filters that take space (ranges, long lists).\n- Form(children*: [ids], submit*: {label,action}, cancel: {label,action}, aside: id, layout: stacked|horizontal)\n Collects inputs and submits them together. Use for: Any set of inputs that are submitted together.\n- Group(children*: [ids], label: text|{path}, arrangement: auto|inline|grid)\n Visually groups closely related items without a heading (proximity). Use for: A few items that belong together (a key figure and its caption, several metrics).\n- Identity(name*: text|{path}, detail: text|{path}, image: text|{path}, kind: person|team|organization, size: small|default|large, action: {event:{name,context?}}, group: {path,namePath,imagePath?,max?})\n A person, team or organisation: picture, name and details, or several of them together. Use for: Who something belongs to, was sent by or is assigned to; The recipient on a payment, the owner on a record, the members on a team.\n- Media(src: {path}, alt: text|{path}, decorative: boolean, aspect: square|landscape|portrait|auto, kind: image|video|audio|gallery|qr, poster: text|{path}, transcript: text|{path}, items: {path}, imagePath: path, altPath: path, value: text|{path})\n An image, video, audio clip, gallery or QR code supplied by the host. Use for: Photos or illustrations that help identify something (a product, a place, a person); A recording people play, or a code they scan.\n- Metric(label*: text|{path}, value*: {path}, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, change: {value,format?,favorable?}, caption: text|{path})\n A key figure with a label, and optionally its change. Use for: One to four headline numbers the user asked about.\n- Navigation(label: text|{path}, items*: [{key,label,group?,icon?,badge?,action}], current*: {path}, kind: main|breadcrumb|nested|toc|local, placement: auto|side|rail|bar|drawer)\n The product's main navigation. Use for: Software with sections people move between (B2B apps, dashboards); Only when the host doesn't already provide navigation.\n- Panel(kind: dialog|drawer|sheet|popover, title*: text|{path}, children*: [ids], actions: id, open: bool|{path}, dismissible: boolean, size: small|default|large)\n Content over the current view: a dialog, a drawer, a bottom sheet or a popover, opened from an action and dismissed to return. Use for: A short task on top of the page: edit one thing, pick one thing, see one record; Detail the person asked for that shouldn't replace where they are.\n- Progress(label*: text|{path}, value*: number|{path}, max: number|{path}, kind: bar|ring|meter, caption: text|{path}, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, tone: neutral|info|success|warning|danger, thresholds: {warning?,danger?}, indeterminate: bool|{path})\n How far along something is, or how much of a bounded amount is used: a bar, a ring or a meter. Use for: A task that takes time, with a known share done; How much of a quota, budget or capacity is used.\n- RangeInput(label*: text|{path}, value*: {path}, min*: number, max*: number, step: number, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, mode: single|range)\n A number, or a range between two numbers, within known bounds. Use for: Approximate values within a range (budget cap, volume); A price or date range for filtering (mode 'range').\n- Rating(label*: text|{path}, value*: {path}, max: integer, readOnly: bool|{path}, count: number|{path}, help: text|{path}, required: bool|{path}, action: {event:{name,context?}})\n A score out of N: given by the person, or shown as others gave it. Use for: Asking how something went, out of five; Showing a product's or place's rating with the count behind it.\n- Section(title*: text|{path}, description: text|{path}, children*: [ids])\n A titled region of the surface that groups related content under a heading. Use for: The surface has two or more distinct topics (e.g. 'Recipient' and 'Amount'); Content would otherwise exceed one screen and needs signposting.\n- Split(primary*: id, detail*: id, selected*: {path}, empty: id, ratio: narrow|balanced|wide, resizable: boolean)\n Master and detail side by side: a list that stays while the chosen item's detail shows beside it; on compact layouts the detail replaces the list. Use for: Mail, tickets, messages, files: a list people move through while reading; Settings with many sections on wide screens.\n- Status(kind*: info|success|warning|error|empty|loading|undo, title*: text|{path}, message: text|{path}, action: id, icon: lock|trash|clock|tag|info|alert|mail|shield|search|book|inbox|check, variant: default|inline)\n Feedback about state: info, success, warning, error, empty or loading. Use for: Results of an action; Empty collections.\n- Steps(steps*: [{key,title,content,status?}], kind: wizard|tasklist|guide, current: number|{path}, finish*: {label,action})\n A task split into ordered steps with visible progress. Use for: Tasks with dependent stages or too many inputs for one view (checkout, onboarding).\n- Table(rows*: {path}, caption*: text|{path}, columns*: [{key,label,path,format?,kind?,align?,tones?,sortable?,avatarPath?,secondaryPath?}], rowAction: {event:{name,context?}}, empty: id, sort: {column,direction?,action?}, selection: none|multiple, selected: {path}, rowValuePath: path, bulkActions: id, rowActions: id, expandable: boolean, detail: id, toolbar: id, search: id, views: [{key,label,count?,action?}], view: {path}, page: {index?,size?,total?,action?})\n Tabular data: many items sharing the same attributes. Use for: Scanning or comparing many rows by several attributes; Records people scan, sort, select and act on in bulk (B2B lists).\n- Tag(label*: text|{path}, kind: label|status|count, tone: neutral|info|success|warning|danger, count: number|{path}, remove: {event:{name,context?}})\n A short label, status or count attached to something else. Use for: The state of a record, next to its name: 'Past due', 'Draft', 'Live'; A category or label a thing carries.\n- Text(text*: text|{path}, variant: body|supporting|caption|rich|list|quote, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, items: {path}, itemPath: path, ordered: boolean, cite: text|{path})\n A run of text. Use for: Explanations, instructions, messages.\n- TextInput(label*: text|{path}, value*: {path}, kind: text|multiline|number|currency|email|phone|url|search|suggestions|richtext|tags|mentions|code|masked|inline, options: [{value,label}]|{path,valuePath,labelPath}, mask: string, currency: text|{path}, help: text|{path}, required: boolean, validation: {min?,max?,minLength?,maxLength?,message?}, autocomplete: string, placeholder: text|{path}, size: default|hero)\n A single text-like value: text, number, email, phone, currency, search or long text. Use for: Free-form values the user types.\n- Toggle(label*: text|{path}, value*: {path}, description: text|{path}, action: {event:{name,context?}})\n An on/off setting. Use for: Settings that take effect immediately; A single yes/no inside a form.\n- Tree(label*: text|{path}, items*: {path}, labelPath*: path, childrenPath*: path, valuePath: path, detailPath: path, selection: none|single|multiple, selected: {path}, expanded: {path}, action: {event:{name,context?}})\n A hierarchy people expand, browse and pick from: folders, an org chart, nested categories. Use for: Things that contain things: folders and files, an organisation, nested categories; Picking a place in a hierarchy (move to folder, choose a category).\n- Views(views*: [{key,label,content,count?}], selected: {path}, variant: tabs|segmented)\n Switch between alternative views of the same subject (tabs). Use for: 2\u20136 peer views of the same data (Overview / Transactions / Settings).\n\nPatterns (set surface.pattern when one applies):\n- compare-and-choose: Put a few options side by side on the same attributes, mark what is better, recommend when there's a clear fit, and let the user choose. Structure: Comparison with attributes in the same order for every option \u2192 Optional recommendation with a one-line reason \u2192 A choose action per option\n- confirm-destructive: Before something that is hard or impossible to undo, say exactly what will happen and ask for an explicit, specific confirmation. Structure: Confirm (title names the action and object) \u2192 Consequence: what happens and whether it can be undone \u2192 Optional DetailList summary of what is being confirmed \u2192 Cancel (least destructive, focused first) and a confirm button that repeats the verb\n- filter-and-browse: Narrow a large set with a few filters placed before the results, and explain empty results. Structure: Filters (search, Choice, RangeInput) before the results \u2192 Result count in the results label \u2192 Collection or Table of results with an empty state that says how to widen the search\n- multi-step-form: Collect information in small, ordered chunks: one form when short, Steps when long or dependent. Structure: Form (\u2264 6 inputs) or Steps with one topic per step \u2192 Labels above inputs, help text under labels \u2192 Verb-first submit label as the single primary action\n- review-and-submit: Before committing, show everything that will be submitted in a scannable summary, then one clear submit action. Structure: Sections with DetailLists summarising what will be submitted \u2192 Any final acknowledgements (terms) \u2192 Submit label states the commitment ('Book and pay \u00A3312')\n- undo-over-confirm: For everyday actions that can be reversed, do them straight away and offer Undo, instead of asking 'Are you sure?' every time. Structure: The action runs when chosen \u2192 Status (kind 'undo') says what happened, naming the thing \u2192 Its action is Undo, wired to the reversing capability\n\nExample:\n{\"specVersion\":\"0.3.0\",\"surface\":{\"id\":\"add-task\",\"title\":\"New task\",\"intent\":\"tasks.create\"},\"root\":\"form\",\"components\":[{\"id\":\"form\",\"component\":\"Form\",\"children\":[\"title\",\"due\",\"priority\"],\"submit\":{\"label\":\"Add task\",\"action\":{\"event\":{\"name\":\"task.save\",\"context\":{\"title\":{\"path\":\"/draft/title\"},\"due\":{\"path\":\"/draft/due\"},\"priority\":{\"path\":\"/draft/priority\"}}}}}},{\"id\":\"title\",\"component\":\"TextInput\",\"key\":\"title\",\"label\":\"Task\",\"value\":{\"path\":\"/draft/title\"},\"required\":true},{\"id\":\"due\",\"component\":\"DateInput\",\"key\":\"due\",\"label\":\"Due\",\"value\":{\"path\":\"/draft/due\"}},{\"id\":\"priority\",\"component\":\"Choice\",\"key\":\"priority\",\"label\":\"Priority\",\"value\":{\"path\":\"/draft/priority\"},\"options\":[{\"value\":\"low\",\"label\":\"Low\"},{\"value\":\"normal\",\"label\":\"Normal\"},{\"value\":\"high\",\"label\":\"High\"}]}]}";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Generated by scripts/build-prompt.ts from the spec (0.3.0). Do not edit.
|
|
2
|
+
export const SPEC_VERSION = "0.3.0";
|
|
3
|
+
export const SYSTEM_PROMPT = "You generate just-in-time user interfaces as JSON, in the Polyxd UI document format. Output ONE JSON object and nothing else.\n\nDocument shape: {\"specVersion\":\"0.3.0\",\"surface\":{\"id\",\"title\",\"intent\",\"pattern\"?},\"root\":\"<id>\",\"components\":[...]}.\nComponents are a FLAT list; each has a unique \"id\" and \"component\" (its type). Containers reference children by id STRINGS (\"children\": [\"a\",\"b\"]); never nest component objects inside other components.\n\nRules:\n1. Data comes from the host. Bind values with {\"path\":\"/json/pointer\"}; pointers start at the root of the DATA object (DATA {\"card\":{\"id\":\"c1\"}} → {\"path\":\"/card/id\"}, never \"/data/card/id\"). Never type numbers, prices, names or dates from the data as literal text; never invent data.\n2. Inside a repeated item (Collection items template, Table columns, Chart series, Comparison attributes) use paths RELATIVE to the item, e.g. {\"path\":\"amount\"}.\n3. Actions: {\"event\":{\"name\":\"<capability>\",\"context\":{...}}}. Use ONLY the capabilities listed. \"ui.dismiss\" closes the surface.\n4. Destructive capabilities must be triggered from a Confirm. Consequential ones need a Confirm or a review step first.\n5. At most one primary action visible at a time (a Form's submit counts). At most 6 inputs per view; use Steps for more.\n6. Labels say what happens (\"Send £20\", \"Freeze card\"), in sentence case. Give every component that represents a thing from the data a stable \"key\" in lower_snake_case (e.g. \"card_status\").\n7. Collections and Tables need an \"empty\" Status when the list may be empty.\n8. If no listed capability can do what was asked, show a Status explaining that instead of a fake interface.\n9. Format numbers, money and dates with \"format\", never by writing them into strings.\n10. There is no template engine. Text is either literal or a binding; \"{{budget}}\" or \"${spent}\" in a string reaches the screen exactly as written.\n11. Bind text to a field that holds text. A pointer at an object or a list prints as \"[object Object]\"; point at the string inside it.\n12. Show people names, not internal ids. If a record has both \"id\" and \"name\", the screen gets \"name\"; the id belongs in an action's context.\n13. Never use AppBar, Custom, Footer, Frame or Outlet: the product's shell is authored, and your surface renders inside it. Never draw navigation, a header or a footer around a surface.\n\nComponents (* = required):\n- Action(label*: text|{path}, action*: {event:{name,context?}}, emphasis: primary|secondary|tertiary, tone: default|danger, disabled: bool|{path}, description: text|{path}, copy: text|{path}, shortcut: string)\n A button that triggers a host capability. Use for: Anything the user can do that isn't typing or choosing.\n- ActionBar(children*: [ids])\n The set of actions for a surface or section; the renderer places it where that platform expects. Use for: Two or more actions that apply to the whole surface or section.\n- ActionMenu(label*: text|{path}, kind: overflow|dropdown|split|context, children*: [ids], primary: id)\n Secondary actions behind one control: an overflow menu, a dropdown, a split button or a context menu. Use for: Three or more secondary actions on a row, a card or a page header; One usual action with rarer alternatives (split: 'Save' with 'Save as draft').\n- Card(title*: text|{path}, subtitle: text|{path}, media: id, badge: {text,tone?}, children: [ids], action: {event:{name,context?}}, progress: {value,label?})\n One self-contained entity (an account, an order, a place), optionally actionable as a whole. Use for: Items in a Collection that each represent an entity; A single entity summary in a larger surface.\n- Chart(intent*: trend|comparison|composition|distribution|relationship|flow|hierarchy|matrix|range, data*: {path}, title*: text|{path}, summary*: text|{path}, x*: {path,label,format?}, series*: [{key?,path,label,format?}])\n A visual summary of data, always paired with a text summary. Use for: A trend, comparison, share or spread matters more than exact values.\n- Choice(label*: text|{path}, options*: [{value,label}]|{path,valuePath,labelPath}, value*: {path}, mode: single|multiple, help: text|{path}, required: boolean)\n Pick one or several options from a known set. The renderer chooses the control. Use for: Any choice from a known set of options.\n- Code(text*: text|{path}, label: text|{path}, language: string, copyable: boolean, wrap: boolean, secret: bool|{path})\n Code, a command or preformatted text, shown as written and copyable. Use for: A command to run, a snippet to paste, an identifier to copy; Structured text whose spacing matters.\n- CodeInput(label*: text|{path}, value*: {path}, length: integer, kind: numeric|alphanumeric, help: text|{path}, required: bool|{path}, action: {event:{name,context?}})\n A one-time code or PIN, typed into one box per character. Use for: A verification code from email or SMS; A PIN or a short recovery code.\n- Collection(items*: {path,componentId}, label*: text|{path}, empty: id, selection: none|single|multiple, selected: {path}, layout: auto|list|grid|timeline|carousel|calendar, datePath: path, page: {index?,size?,total?,action?}, bulkActions: id, reorderable: boolean, order: {path})\n A list of items from host data, each rendered with the same template. Use for: Browsing a set of similar entities; Search results.\n- ColorInput(label*: text|{path}, value*: {path}, swatches: [{value,label}]|{path,valuePath,labelPath}, format: hex|rgb|hsl, alpha: boolean, help: text|{path}, required: bool|{path})\n Pick a colour: from swatches the host offers, or any colour. Use for: A colour for a label, a tag, a calendar, a theme; Choosing from a set of brand colours.\n- Columns(children*: [ids], layout: two-thirds|one-third|halves|thirds|sidebar-start|sidebar-end, collapse: compact|medium|never, align: start|center|stretch)\n Side-by-side columns that collapse to a stack on compact layouts: a main column with a narrower one beside it, or equal halves. Use for: A form with help beside it; a record with a summary beside it; a list beside a preview; Anywhere a wide screen should use its width without the compact screen paying for it.\n- Comparison(items*: {path}, itemTitle*: path, attributes*: [{key,label,path,format?,better?,group?}], choose: {label,action}, recommended: text|{path}, summary: text|{path}, recommendedReason: text|{path})\n Compare a few options across the same attributes, and choose one. Use for: Choosing between 2–4 plans, products, routes or offers.\n- Confirm(title*: text|{path}, message: text|{path}, consequence: text|{path}, summary: id, severity*: consequential|destructive, confirm*: {label,action}, cancel: {label,action}, typeToConfirm: text|{path}, amount: {value,format?}, subject: {title,subtitle?,avatar?}, consequences: [{title,detail?,icon?}])\n Asks the user to confirm a consequential or destructive action, showing what will happen. Use for: Moving money, deleting data, sending on someone's behalf, anything irreversible.\n- DateInput(label*: text|{path}, value*: {path}, kind: date|time|datetime|dateRange|month|year, multiple: boolean, min: text|{path}, max: text|{path}, help: text|{path}, required: boolean)\n A date, time, date-time or date range. Use for: Any date or time entry.\n- DetailList(items*: [{key?,label,value,format?,total?}], title: text|{path}, variant: default|receipt, layout: rows|grid, rowAction: {event:{name,context?}})\n Label/value pairs describing one thing (a summary, a receipt, a review step). Use for: Reviewing before submitting; Showing the attributes of one entity.\n- Disclosure(summary*: text|{path}, children*: [ids], open: boolean)\n Progressive disclosure: secondary content hidden behind a toggle. Use for: Details most people don't need (fees breakdown, advanced options, help text).\n- FileInput(label*: text|{path}, value*: {path}, accept: [string], multiple: boolean, maxSize: integer, help: text|{path}, required: bool|{path})\n Choose or drop files to attach or upload. Use for: Attaching a document, a receipt, a photo; Importing a file the product reads.\n- FilterPanel(label: text|{path}, children*: [ids], results*: id, resultCount: {path}, clear: {event:{name,context?}})\n Filters for a list of results, with the result count. Use for: Browsing results people narrow by several attributes; More than two filters, or filters that take space (ranges, long lists).\n- Form(children*: [ids], submit*: {label,action}, cancel: {label,action}, aside: id, layout: stacked|horizontal)\n Collects inputs and submits them together. Use for: Any set of inputs that are submitted together.\n- Group(children*: [ids], label: text|{path}, arrangement: auto|inline|grid)\n Visually groups closely related items without a heading (proximity). Use for: A few items that belong together (a key figure and its caption, several metrics).\n- Identity(name*: text|{path}, detail: text|{path}, image: text|{path}, kind: person|team|organization, size: small|default|large, action: {event:{name,context?}}, group: {path,namePath,imagePath?,max?})\n A person, team or organisation: picture, name and details, or several of them together. Use for: Who something belongs to, was sent by or is assigned to; The recipient on a payment, the owner on a record, the members on a team.\n- Media(src: {path}, alt: text|{path}, decorative: boolean, aspect: square|landscape|portrait|auto, kind: image|video|audio|gallery|qr, poster: text|{path}, transcript: text|{path}, items: {path}, imagePath: path, altPath: path, value: text|{path})\n An image, video, audio clip, gallery or QR code supplied by the host. Use for: Photos or illustrations that help identify something (a product, a place, a person); A recording people play, or a code they scan.\n- Metric(label*: text|{path}, value*: {path}, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, change: {value,format?,favorable?}, caption: text|{path})\n A key figure with a label, and optionally its change. Use for: One to four headline numbers the user asked about.\n- Navigation(label: text|{path}, items*: [{key,label,group?,icon?,badge?,action}], current*: {path}, kind: main|breadcrumb|nested|toc|local, placement: auto|side|rail|bar|drawer)\n The product's main navigation. Use for: Software with sections people move between (B2B apps, dashboards); Only when the host doesn't already provide navigation.\n- Panel(kind: dialog|drawer|sheet|popover, title*: text|{path}, children*: [ids], actions: id, open: bool|{path}, dismissible: boolean, size: small|default|large)\n Content over the current view: a dialog, a drawer, a bottom sheet or a popover, opened from an action and dismissed to return. Use for: A short task on top of the page: edit one thing, pick one thing, see one record; Detail the person asked for that shouldn't replace where they are.\n- Progress(label*: text|{path}, value*: number|{path}, max: number|{path}, kind: bar|ring|meter, caption: text|{path}, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, tone: neutral|info|success|warning|danger, thresholds: {warning?,danger?}, indeterminate: bool|{path})\n How far along something is, or how much of a bounded amount is used: a bar, a ring or a meter. Use for: A task that takes time, with a known share done; How much of a quota, budget or capacity is used.\n- RangeInput(label*: text|{path}, value*: {path}, min*: number, max*: number, step: number, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, mode: single|range)\n A number, or a range between two numbers, within known bounds. Use for: Approximate values within a range (budget cap, volume); A price or date range for filtering (mode 'range').\n- Rating(label*: text|{path}, value*: {path}, max: integer, readOnly: bool|{path}, count: number|{path}, help: text|{path}, required: bool|{path}, action: {event:{name,context?}})\n A score out of N: given by the person, or shown as others gave it. Use for: Asking how something went, out of five; Showing a product's or place's rating with the count behind it.\n- Section(title*: text|{path}, description: text|{path}, children*: [ids])\n A titled region of the surface that groups related content under a heading. Use for: The surface has two or more distinct topics (e.g. 'Recipient' and 'Amount'); Content would otherwise exceed one screen and needs signposting.\n- Split(primary*: id, detail*: id, selected*: {path}, empty: id, ratio: narrow|balanced|wide, resizable: boolean)\n Master and detail side by side: a list that stays while the chosen item's detail shows beside it; on compact layouts the detail replaces the list. Use for: Mail, tickets, messages, files: a list people move through while reading; Settings with many sections on wide screens.\n- Status(kind*: info|success|warning|error|empty|loading|undo, title*: text|{path}, message: text|{path}, action: id, icon: lock|trash|clock|tag|info|alert|mail|shield|search|book|inbox|check, variant: default|inline)\n Feedback about state: info, success, warning, error, empty or loading. Use for: Results of an action; Empty collections.\n- Steps(steps*: [{key,title,content,status?}], kind: wizard|tasklist|guide, current: number|{path}, finish*: {label,action})\n A task split into ordered steps with visible progress. Use for: Tasks with dependent stages or too many inputs for one view (checkout, onboarding).\n- Table(rows*: {path}, caption*: text|{path}, columns*: [{key,label,path,format?,kind?,align?,tones?,sortable?,avatarPath?,secondaryPath?}], rowAction: {event:{name,context?}}, empty: id, sort: {column,direction?,action?}, selection: none|multiple, selected: {path}, rowValuePath: path, bulkActions: id, rowActions: id, expandable: boolean, detail: id, toolbar: id, search: id, views: [{key,label,count?,action?}], view: {path}, page: {index?,size?,total?,action?})\n Tabular data: many items sharing the same attributes. Use for: Scanning or comparing many rows by several attributes; Records people scan, sort, select and act on in bulk (B2B lists).\n- Tag(label*: text|{path}, kind: label|status|count, tone: neutral|info|success|warning|danger, count: number|{path}, remove: {event:{name,context?}})\n A short label, status or count attached to something else. Use for: The state of a record, next to its name: 'Past due', 'Draft', 'Live'; A category or label a thing carries.\n- Text(text*: text|{path}, variant: body|supporting|caption|rich|list|quote, format: {type:currency|number|percent|date|time|datetime|relativeTime|color|bytes,currency?}, items: {path}, itemPath: path, ordered: boolean, cite: text|{path})\n A run of text. Use for: Explanations, instructions, messages.\n- TextInput(label*: text|{path}, value*: {path}, kind: text|multiline|number|currency|email|phone|url|search|suggestions|richtext|tags|mentions|code|masked|inline, options: [{value,label}]|{path,valuePath,labelPath}, mask: string, currency: text|{path}, help: text|{path}, required: boolean, validation: {min?,max?,minLength?,maxLength?,message?}, autocomplete: string, placeholder: text|{path}, size: default|hero)\n A single text-like value: text, number, email, phone, currency, search or long text. Use for: Free-form values the user types.\n- Toggle(label*: text|{path}, value*: {path}, description: text|{path}, action: {event:{name,context?}})\n An on/off setting. Use for: Settings that take effect immediately; A single yes/no inside a form.\n- Tree(label*: text|{path}, items*: {path}, labelPath*: path, childrenPath*: path, valuePath: path, detailPath: path, selection: none|single|multiple, selected: {path}, expanded: {path}, action: {event:{name,context?}})\n A hierarchy people expand, browse and pick from: folders, an org chart, nested categories. Use for: Things that contain things: folders and files, an organisation, nested categories; Picking a place in a hierarchy (move to folder, choose a category).\n- Views(views*: [{key,label,content,count?}], selected: {path}, variant: tabs|segmented)\n Switch between alternative views of the same subject (tabs). Use for: 2–6 peer views of the same data (Overview / Transactions / Settings).\n\nPatterns (set surface.pattern when one applies):\n- compare-and-choose: Put a few options side by side on the same attributes, mark what is better, recommend when there's a clear fit, and let the user choose. Structure: Comparison with attributes in the same order for every option → Optional recommendation with a one-line reason → A choose action per option\n- confirm-destructive: Before something that is hard or impossible to undo, say exactly what will happen and ask for an explicit, specific confirmation. Structure: Confirm (title names the action and object) → Consequence: what happens and whether it can be undone → Optional DetailList summary of what is being confirmed → Cancel (least destructive, focused first) and a confirm button that repeats the verb\n- filter-and-browse: Narrow a large set with a few filters placed before the results, and explain empty results. Structure: Filters (search, Choice, RangeInput) before the results → Result count in the results label → Collection or Table of results with an empty state that says how to widen the search\n- multi-step-form: Collect information in small, ordered chunks: one form when short, Steps when long or dependent. Structure: Form (≤ 6 inputs) or Steps with one topic per step → Labels above inputs, help text under labels → Verb-first submit label as the single primary action\n- review-and-submit: Before committing, show everything that will be submitted in a scannable summary, then one clear submit action. Structure: Sections with DetailLists summarising what will be submitted → Any final acknowledgements (terms) → Submit label states the commitment ('Book and pay £312')\n- undo-over-confirm: For everyday actions that can be reversed, do them straight away and offer Undo, instead of asking 'Are you sure?' every time. Structure: The action runs when chosen → Status (kind 'undo') says what happened, naming the thing → Its action is Undo, wired to the reversing capability\n\nExample:\n{\"specVersion\":\"0.3.0\",\"surface\":{\"id\":\"add-task\",\"title\":\"New task\",\"intent\":\"tasks.create\"},\"root\":\"form\",\"components\":[{\"id\":\"form\",\"component\":\"Form\",\"children\":[\"title\",\"due\",\"priority\"],\"submit\":{\"label\":\"Add task\",\"action\":{\"event\":{\"name\":\"task.save\",\"context\":{\"title\":{\"path\":\"/draft/title\"},\"due\":{\"path\":\"/draft/due\"},\"priority\":{\"path\":\"/draft/priority\"}}}}}},{\"id\":\"title\",\"component\":\"TextInput\",\"key\":\"title\",\"label\":\"Task\",\"value\":{\"path\":\"/draft/title\"},\"required\":true},{\"id\":\"due\",\"component\":\"DateInput\",\"key\":\"due\",\"label\":\"Due\",\"value\":{\"path\":\"/draft/due\"}},{\"id\":\"priority\",\"component\":\"Choice\",\"key\":\"priority\",\"label\":\"Priority\",\"value\":{\"path\":\"/draft/priority\"},\"options\":[{\"value\":\"low\",\"label\":\"Low\"},{\"value\":\"normal\",\"label\":\"Normal\"},{\"value\":\"high\",\"label\":\"High\"}]}]}";
|
|
4
|
+
//# sourceMappingURL=prompt.generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.generated.js","sourceRoot":"","sources":["../src/prompt.generated.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AACpC,MAAM,CAAC,MAAM,aAAa,GAAG,sylBAAsylB,CAAC"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spec as instructions for the host's model. Everything the server says about writing a
|
|
3
|
+
* document lives in this module.
|
|
4
|
+
*
|
|
5
|
+
* The generator prompt itself is not written here: it is the one `@polyxd/runtime` builds from the
|
|
6
|
+
* spec, so a model writing documents over MCP gets the same instructions as one driven by the runtime.
|
|
7
|
+
*/
|
|
8
|
+
import { SPEC_VERSION } from "@polyxd/spec/browser";
|
|
9
|
+
import { systemPrompt } from "@polyxd/runtime";
|
|
10
|
+
const SYSTEM_PROMPT = systemPrompt();
|
|
11
|
+
import { PACKS } from "./packs.generated.js";
|
|
12
|
+
export { SPEC_VERSION, SYSTEM_PROMPT };
|
|
13
|
+
/** How to use this server's tools, for a model that has the generator prompt above. */
|
|
14
|
+
export const MCP_NOTES = `How this works over MCP
|
|
15
|
+
|
|
16
|
+
You are the generator. Write the Polyxd UI document yourself, following the prompt below, and pass it to the tools as the "document" argument instead of writing JSON into the chat.
|
|
17
|
+
|
|
18
|
+
1. Write the document. Put the values the screen shows in "data" (the polyxd_show and polyxd_validate tools take it as a separate "data" argument, or inside the document). Take those values from the conversation or from other tools' results; never make figures up. If a chart or table needs values nobody gave you (say, daily numbers when you only have weekly totals), leave that part out and say what is missing; never fill a series with plausible numbers.
|
|
19
|
+
2. Call polyxd_validate. Fix every error at the JSON Pointer it gives, then validate again. Warnings are advice.
|
|
20
|
+
3. Optionally call polyxd_verify for the verifier's document checks, with a Design Direction if the user has one.
|
|
21
|
+
4. Call polyxd_show to put the screen in front of the user. It validates first and shows nothing if there are errors. Pick a design-system pack with "pack" (default material3; polyxd_packs lists them).
|
|
22
|
+
|
|
23
|
+
Actions: nobody has registered capabilities with this server, so rule 3's "capabilities listed" means the action names you choose. Name each action for what you will do when it arrives, as "domain.verb" (for example "task.save" or "booking.cancel"). When the user presses an action in the shown screen, you receive a chat message from the user naming the action, the button's label and the action's context with the values the user entered. Act on it as if the user had asked in words. "ui.dismiss" (closing the screen) arrives the same way. Pressing a button only tells you what the user chose: nothing has been submitted, paid, booked or saved. Never say an order was placed, a return started or a payment made unless another tool of yours actually did it; say what the user chose and what would happen next. A screen that asks the user to choose, fill in or confirm something always ends with its main action as a button (for example "Request £34.76 each"), so they can act on it; a screen that only reports needs none.
|
|
24
|
+
|
|
25
|
+
Use polyxd_components for any component's full definition, and read the example documents (resources polyxd://examples/...) for complete, valid documents.`;
|
|
26
|
+
/** The whole guide: how to use the tools, then the generator prompt. */
|
|
27
|
+
export function guide() {
|
|
28
|
+
const packs = PACKS.map((p) => p.name).join(", ");
|
|
29
|
+
return `${MCP_NOTES}\n\nPacks: ${packs}.\n\n---\n\n${SYSTEM_PROMPT}`;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,aAAa,GAAG,YAAY,EAAE,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAEvC,uFAAuF;AACvF,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;2JAWkI,CAAC;AAE5J,wEAAwE;AACxE,MAAM,UAAU,KAAK;IACnB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,GAAG,SAAS,cAAc,KAAK,eAAe,aAAa,EAAE,CAAC;AACvE,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Polyxd MCP server. The host's model is the generator: these tools give it the spec, check
|
|
3
|
+
* what it wrote, and show the result to the user as an MCP App (SEP-1865, `io.modelcontextprotocol/ui`).
|
|
4
|
+
*
|
|
5
|
+
* Nothing here touches the file system or Node's APIs, so the same server runs over stdio in Node
|
|
6
|
+
* (src/bin.ts) and over Streamable HTTP in a Cloudflare Worker (src/http.ts, apps/mcp). Where the
|
|
7
|
+
* MCP App's page comes from is the caller's business: src/view-file.ts reads it from dist/view.html,
|
|
8
|
+
* and the Worker bundles it as text.
|
|
9
|
+
*/
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
11
|
+
import { PACKS } from "./packs.generated.ts";
|
|
12
|
+
export declare const VERSION = "0.4.0";
|
|
13
|
+
/** The MCP App resource every shown screen renders in. */
|
|
14
|
+
export declare const VIEW_URI = "ui://polyxd/surface.html";
|
|
15
|
+
/** MCP Apps' HTML profile. */
|
|
16
|
+
export declare const VIEW_MIME_TYPE = "text/html;profile=mcp-app";
|
|
17
|
+
export declare const DEFAULT_PACK = "material3";
|
|
18
|
+
/** The origin ChatGPT gives the MCP App's sandbox (`openai/widgetDomain`): unique to this app. */
|
|
19
|
+
export declare const WIDGET_DOMAIN = "https://mcp.polyxd.com";
|
|
20
|
+
export interface ServerOptions {
|
|
21
|
+
/** The MCP App's page: the self-contained HTML scripts/build-view.ts writes to dist/view.html. Called when a host reads the resource. */
|
|
22
|
+
viewHtml: () => string | Promise<string>;
|
|
23
|
+
/** The origin ChatGPT should give the MCP App's sandbox. Default {@link WIDGET_DOMAIN}. */
|
|
24
|
+
widgetDomain?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The pack a model means. Models write what people say ("Carbon", "shadcn/ui", "Material 3", "IBM
|
|
28
|
+
* Carbon") as often as the pack's id, and an id-only enum made hosts drop the argument and fall back
|
|
29
|
+
* to the default. Exact names first, then a display name with or without its vendor and version, then
|
|
30
|
+
* an unambiguous prefix. Unknown names are an error, never a silent default.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolvePack(input: string): (typeof PACKS)[number] | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The MCP App resource's `_meta`, for Claude and ChatGPT alike. The page loads nothing from anywhere
|
|
35
|
+
* (renderer, styles and themes are inlined), so its Content Security Policy allows no origins at all.
|
|
36
|
+
* `ui.domain` is left out on purpose: each host wants its own format there (Claude a hash of the
|
|
37
|
+
* connector URL, ChatGPT an origin), and a value in the wrong one stops Claude rendering the app.
|
|
38
|
+
* ChatGPT takes its origin from `openai/widgetDomain` instead, which Claude ignores.
|
|
39
|
+
*/
|
|
40
|
+
export declare function appResourceMeta(widgetDomain?: string): {
|
|
41
|
+
ui: {
|
|
42
|
+
prefersBorder: boolean;
|
|
43
|
+
csp: {
|
|
44
|
+
connectDomains: string[];
|
|
45
|
+
resourceDomains: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
"openai/widgetDescription": string;
|
|
49
|
+
"openai/widgetPrefersBorder": boolean;
|
|
50
|
+
"openai/widgetCSP": {
|
|
51
|
+
connect_domains: string[];
|
|
52
|
+
resource_domains: string[];
|
|
53
|
+
};
|
|
54
|
+
"openai/widgetDomain": string;
|
|
55
|
+
};
|
|
56
|
+
export declare function createServer(options: ServerOptions): McpServer;
|