@plitzi/sdk-mcp 0.38.13 → 0.38.14
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 +153 -0
- package/README.md +19 -0
- package/dist/modules/mcp/helpers/guide.js +4 -0
- package/dist/modules/mcp/resources/renderGuide.d.ts +2 -0
- package/dist/modules/mcp/resources/renderGuide.js +2 -1
- package/dist/modules/mcp/server.js +1 -1
- package/dist/modules/mcp/tools/apply/index.d.ts +4 -0
- package/dist/modules/mcp/tools/operations/index.d.ts +16 -0
- package/dist/modules/mcp/tools/operations/schema/operations.d.ts +4 -0
- package/dist/modules/mcp/tools/operations/schema/pages/upsertPage.d.ts +4 -0
- package/dist/modules/mcp/tools/operations/schema/pages/upsertPage.js +6 -2
- package/dist/modules/mcp/tools/operations/schema/translator.js +2 -1
- package/dist/modules/mcp/tools/render.d.ts +8 -35
- package/dist/modules/mcp/tools/render.js +4 -56
- package/dist/modules/mcp/tools/renderWidget.d.ts +1467 -0
- package/dist/modules/mcp/tools/renderWidget.js +67 -0
- package/dist/modules/mcp/tools/shared/lintDraft.js +1 -0
- package/dist/modules/mcp/types/aiSchema.d.ts +3 -1
- package/dist/render.d.ts +10 -0
- package/dist/render.js +3 -0
- package/package.json +12 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,158 @@
|
|
|
1
1
|
# @plitzi/sdk-mcp
|
|
2
2
|
|
|
3
|
+
## 0.38.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 493955a: ## SDK
|
|
8
|
+
|
|
9
|
+
- **An embedded space leaves the page's theme alone** (`@plitzi/plitzi-sdk`, `@plitzi/plitzi-builder`): plitzi-ui's
|
|
10
|
+
`Provider` stamps `dark` on `<html>` by default — and, being `light`, takes it off — so every space mounted with
|
|
11
|
+
`themeScope: 'container'` turned the page around it light, and nothing put it back. The SDK and the builder now leave
|
|
12
|
+
the class to their own theme provider (`applyColorModeClass={false}`).
|
|
13
|
+
- **A space inside a space** (`@plitzi/plitzi-sdk`, `@plitzi/sdk-shared`): one drawn inside another — the `plitziSdk`
|
|
14
|
+
element, a plugin rendering one, the builder's canvas — knows it (`render.enclosed`). It is `container` unless it
|
|
15
|
+
says otherwise, wears its theme on its own root and starts from the page's when its own is `system`, keeps its
|
|
16
|
+
stylesheet to its own root (`@scope` — its `:root` palette and `.dark` rules repainted the outer page, its
|
|
17
|
+
`.plitzi-sdk` rule restyled the outer root), writes no `<title>` (`ownsHead`) and no `theme` cookie.
|
|
18
|
+
- **The `plitziSdk` element draws** (`@plitzi/plitzi-sdk`): it rendered in `widget` mode with no page to show, so it
|
|
19
|
+
drew nothing. A widget with no `currentPageId` now shows the page its home address would. And it takes a space's
|
|
20
|
+
documents as well as a key: `offlineData`, bound whole (an `apiContainer`'s answer, a runtime's route, what
|
|
21
|
+
`plitzi_render` returns) or as `offlineData.schema` and `offlineData.style` from two elements — waiting, without an
|
|
22
|
+
error, for the half that has not answered; one that cannot be drawn is said why, in the console and on the canvas.
|
|
23
|
+
- **`<PlitziSdk>` inside a plugin draws** (`@plitzi/plitzi-sdk`): it mounted a router of its own inside the page's, and
|
|
24
|
+
React Router refused it — `routing: 'memory'` or not. A space inside another is a widget: `raw` becomes `widget`
|
|
25
|
+
there, one page and no router (a frame of its own, `iframe` or `shadow`, is left as asked).
|
|
26
|
+
- **A space inside another is as tall as what it holds** (`@plitzi/plitzi-sdk`): its root was `min-h-screen`, as a
|
|
27
|
+
space that IS the page is, so a card of three lines drawn in a panel took the window's height.
|
|
28
|
+
- **`<PlitziSdk.Plugin component>` takes a component with its own props** (`@plitzi/plitzi-sdk`), as `render()`'s
|
|
29
|
+
plugins do: typed `ComponentPluginFC`, it refused any component that read its attributes.
|
|
30
|
+
- **An edited page keeps its address in step** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
|
|
31
|
+
elements, and those were read again only when the list of pages changed — an edit to one page (its slug, its flag)
|
|
32
|
+
leaves that list the same, and the routes kept its old address until something else rebuilt them.
|
|
33
|
+
- **Packages declare only what they use** (`@plitzi/sdk-dev-tools`, `@plitzi/sdk-shared`): `@plitzi/sdk-plugins`, which
|
|
34
|
+
the dev tools no longer import, and `terser`, which no build here runs, are no longer dependencies.
|
|
35
|
+
- **An edited page keeps its route up to date** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
|
|
36
|
+
elements, which were read again only when the list of pages changed — an edit to one page (its slug, its flag) left
|
|
37
|
+
the list the same and the route at the old address. They follow every edit to a page now, and stay the same object
|
|
38
|
+
while no page changes.
|
|
39
|
+
- **`@plitzi/sdk-dev-tools` no longer depends on `@plitzi/sdk-plugins`**, which nothing in it imports.
|
|
40
|
+
- **A dropdown's menu opens inside a card that lifts or clips** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`): drawn
|
|
41
|
+
inside the dropdown, an ancestor with a `transform` — a card lifting on hover — made itself the containing block of
|
|
42
|
+
the menu's `position: fixed`, and one with `overflow: hidden` cut it off: the menu opened and nobody saw it. The open
|
|
43
|
+
menu and its backdrop are drawn at the space's root (`spaceRootOf`, `@plitzi/sdk-shared/helpers/spaceRoot`); a
|
|
44
|
+
style written for the popup by an ancestor's selector no longer reaches it — its own classes do.
|
|
45
|
+
- **A copy button says when it could not copy** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`,
|
|
46
|
+
`@plitzi/sdk-interactions`, `@plitzi/sdk-variables`, `@plitzi/sdk-dev-tools`): a page not served over https has no
|
|
47
|
+
clipboard — a phone opening a development server by its address — and a code block's "Copy" threw there. It reads
|
|
48
|
+
"Not copied", and says why. `clipboardWriter` and `copyText` (`@plitzi/sdk-shared/helpers/clipboard`) are the one
|
|
49
|
+
check, the `copyToClipboard` step's too.
|
|
50
|
+
|
|
51
|
+
## Authoring
|
|
52
|
+
- **`plitziSdk` is authored like any element** (`@plitzi/sdk-authoring`, `@plitzi/sdk-elements`): its declaration moved
|
|
53
|
+
to `@plitzi/sdk-elements` beside the others, so it has a factory — `plitziSdk({ id, bind: { offlineData: 'view.data'
|
|
54
|
+
} })` —, its attributes typed, and `plitzi explain` knows it.
|
|
55
|
+
- **A binding may write part of an attribute** (`@plitzi/sdk-authoring`): `offlineData.schema` writes into
|
|
56
|
+
`offlineData`, as the runtime always did; authoring refused it as an attribute nothing reads.
|
|
57
|
+
- **`choosesChildren`** (`@plitzi/sdk-shared`, `@plitzi/sdk-authoring`): a plugin that shows only some of the elements
|
|
58
|
+
put inside it — a dashboard's panels, a wizard's step — says so in its declaration, and the page checks owe none of
|
|
59
|
+
them on screen; what it draws is still read by the checks of the whole page (images, sideways scroll, legibility).
|
|
60
|
+
`verify` was red on every such page.
|
|
61
|
+
|
|
62
|
+
- **A page says how the document scrolls** (`@plitzi/sdk-elements`, `@plitzi/sdk-authoring`, `@plitzi/sdk-mcp`):
|
|
63
|
+
`documentScroll: 'locked'` holds the document still while the page is open — a dashboard or a board used with a
|
|
64
|
+
finger, where the whole page was dragged and bounced back though nothing in it scrolls — and `'noBounce'` keeps the
|
|
65
|
+
scroll without the rubber band or pull-to-refresh. The page draws the rule itself, so it is in the server's HTML and
|
|
66
|
+
leaves with the page; never inside a widget, a space drawn in another, or the builder's canvas. A Settings select,
|
|
67
|
+
`PageSpec.documentScroll`, `upsertPage`'s `documentScroll` (`null` undoes it) and the page read; an unknown value is
|
|
68
|
+
refused (`attribute-value`). `upsertPage` is now held to the lint of the page it writes, as element writes are.
|
|
69
|
+
|
|
70
|
+
## MCP
|
|
71
|
+
- **`@plitzi/sdk-mcp/render`** (`@plitzi/sdk-mcp`): `plitzi_render`'s widget render on its own — no MCP server, no SSR,
|
|
72
|
+
no OAuth — for a project whose own agent draws in its own pages: `render`, `renderWidgetShape` to declare it as a
|
|
73
|
+
tool, `renderGuideText` for the agent. `base`, the style of the project's space, brings its tokens, classes and fonts
|
|
74
|
+
to the widget.
|
|
75
|
+
|
|
76
|
+
## Server
|
|
77
|
+
- **`publicUrl` says where people reach the server** (`@plitzi/sdk-server`): it was `http://127.0.0.1:<port>` whatever
|
|
78
|
+
the server was. It is `https` once `serverOptions.tls` gives it a certificate, and this machine's network address
|
|
79
|
+
with `HOST` open to the network (what a tablet opens, what an OAuth provider sends it back to); `PUBLIC_URL` still
|
|
80
|
+
wins. The network addresses it prints say `https` too.
|
|
81
|
+
- **A server serving TLS reaches itself** (`@plitzi/sdk-server`): with a local certificate (mkcert) Node trusts nothing
|
|
82
|
+
the browsers were told to, so a runtime calling its own MCP failed. Its requests to its own address now connect to its
|
|
83
|
+
listener and are taken when it presents exactly its certificate (`reachOwnServer`, exported from `/runtime`).
|
|
84
|
+
- **A runtime reads compressed answers again** (`@plitzi/sdk-server`): the dispatcher `reachOwnServer` (and the
|
|
85
|
+
platform's `reachSpaceInside`) installs reached every other host with an undici connector that offers HTTP/2, and an
|
|
86
|
+
answer that came back over h2 reached Node's `fetch` without its `content-encoding` — every API that compresses read
|
|
87
|
+
as Brotli bytes. Other hosts are reached over HTTP/1.1 now, as Node's own `fetch` reaches them.
|
|
88
|
+
- **A project's server serves HTTPS from `.env`** (`@plitzi/sdk-server`, `@plitzi/sdk-shared`): `TLS_CERT` and
|
|
89
|
+
`TLS_KEY`, the certificate's and the key's paths — what `plitzi cert` writes — with no code of the project's;
|
|
90
|
+
`serverOptions.tls` still wins. A network address the certificate does not name is said at boot, the machine's
|
|
91
|
+
`.local` name is printed among the addresses when it is named, and a server on loopback alone says how to open it to
|
|
92
|
+
the network.
|
|
93
|
+
- **A session survives a phone on the Wi-Fi** (`@plitzi/sdk-server`): a private network address (`192.168.…`, `10.…`,
|
|
94
|
+
Tailscale's `100.64/10`, IPv6 unique- and link-local) and a machine's mDNS name (`studio.local`) get the local
|
|
95
|
+
cookie profile — `SameSite=Lax`, no `Secure`, no `Domain`. They got `SameSite=None; Secure`, which a browser drops
|
|
96
|
+
over http: nobody could sign in from a tablet. A deployment's own `.local` sub-domains (`app.example.local`) keep the
|
|
97
|
+
strict profile.
|
|
98
|
+
- **Hiding the dev tools on a server over HTTPS hydrates** (`@plitzi/sdk-server`): a server with a certificate speaks
|
|
99
|
+
HTTP/2, which sends no `Host` — only `:authority` — and the cookies named for the origin's port (the dev tools'
|
|
100
|
+
`plitzi_debug_<port>`, forced flags, painted state) were looked for without it. Shift+F12 hid the dev tools in the
|
|
101
|
+
browser, the server rendered them back, and the page failed to hydrate. Every one is read off `requestAuthority`.
|
|
102
|
+
- **A rebuild of plugins says it finished** (`@plitzi/sdk-server`): bundles removed under a running server (`tmp/`
|
|
103
|
+
deleted) were each a warning, "rebuilding…", and their "ready" at a level a project's terminal does not show. It is
|
|
104
|
+
one episode now: said when noticed, and again when the last is built — how many, how long, which failed.
|
|
105
|
+
- **`tmp/dev-server.json` records the scheme** (`@plitzi/sdk-server`): `url` is `https` with a certificate, and the
|
|
106
|
+
file adds the network addresses and the `publicUrl`.
|
|
107
|
+
- **`public/` is asked about every time** (`@plitzi/sdk-server`): it was cached for an hour — a `.js` or `.css` in it
|
|
108
|
+
for a year, `immutable` — so a file a project rewrites while it runs arrived stale. It is `no-cache` now, answered
|
|
109
|
+
`304` by its `ETag` while unchanged; `immutable` stays for what is versioned (the SDK, the plugins).
|
|
110
|
+
|
|
111
|
+
## CLI
|
|
112
|
+
- **`check`, `shot`, `verify` and `visual` find a server on HTTPS** (`@plitzi/cli`): they rebuilt
|
|
113
|
+
`http://127.0.0.1:<port>`, so a project serving a certificate had no page checked. They read the URL `npm start`
|
|
114
|
+
recorded, and take a local certificate on this machine only. A project's `playwright.config.ts` comes from
|
|
115
|
+
`plitzi upgrade`.
|
|
116
|
+
- **`verify` runs the project's tests** (`@plitzi/cli`): a step `test`, after the lint, when `package.json` has a
|
|
117
|
+
`test` script.
|
|
118
|
+
- **`tsconfig.json` is the project's** (`@plitzi/cli`): the compiler options moved to `plitzi/tsconfig.base.json`, which
|
|
119
|
+
the CLI keeps up and `tsconfig.json` extends — so a folder of the project's own (`scripts/`, `tools/`) goes in its
|
|
120
|
+
`include` without changing a file of the CLI's. `plitzi doctor --fix` makes an older `tsconfig.json` extend it,
|
|
121
|
+
keeping only the options the project changed.
|
|
122
|
+
- **`doctor --fix` and `upgrade` write as the project's Prettier does** (`@plitzi/cli`): a repaired `tsconfig.json` or
|
|
123
|
+
`package.json` left `verify`'s format step red.
|
|
124
|
+
- **`upgrade` renames `usePlitziServiceContext`** (`@plitzi/cli`): with `PlitziServiceProvider` and
|
|
125
|
+
`PlitziServiceContextValue`, renamed in 0.38.12 with no alias — import, use and the module path that named it.
|
|
126
|
+
- **`skills update` compares every file** (`@plitzi/cli`): only `SKILL.md` was, so a change in a reference alone was
|
|
127
|
+
"up to date".
|
|
128
|
+
- **`NOTES.md`, the project's notes for agents** (`@plitzi/cli`): `AGENTS.md` and `CLAUDE.md` are the CLI's and
|
|
129
|
+
`upgrade` replaces them, so what a project's own sessions had to know had nowhere to live. `CLAUDE.md` imports
|
|
130
|
+
`NOTES.md`, `AGENTS.md` sends every agent to it, and nothing the CLI does touches it; `doctor --fix` writes one for
|
|
131
|
+
a project that has none.
|
|
132
|
+
|
|
133
|
+
- **`start:dev-inspect`** (`@plitzi/cli`): a server project's `start:dev` with Node's inspector open on
|
|
134
|
+
127.0.0.1:9229, so a debugger (VS Code, `chrome://inspect`) attaches to the server, and again after each restart.
|
|
135
|
+
`plitzi upgrade` adds it; `plitzi runtime add` has it restart on the runtime too.
|
|
136
|
+
- **`plitzi cert`** (`@plitzi/cli`): HTTPS while developing, for a phone or a tablet on the Wi-Fi — over http its
|
|
137
|
+
browser gives a page no microphone, camera or clipboard. A certificate from mkcert's local authority for this machine,
|
|
138
|
+
its network addresses and its `.local` name, in `tmp/tls/`, named in `.env`; what to install on the phone, said.
|
|
139
|
+
- **A browser project opens to the network** (`@plitzi/cli`): its `vite.config.ts` reads `HOST`, `TLS_CERT` and
|
|
140
|
+
`TLS_KEY` from `.env`, as a server project does, and writes `tmp/dev-server.json` — the port Vite took and the scheme
|
|
141
|
+
— so `page check`, `page shot` and `visual` find it on another port or over HTTPS. `plitzi upgrade` brings it.
|
|
142
|
+
|
|
143
|
+
## Docs
|
|
144
|
+
- **Two plugins share through `state`** (`@plitzi/sdk-authoring` skill): each plugin is a bundle of its own, so a module
|
|
145
|
+
both import is two copies, and what one keeps there the other never sees.
|
|
146
|
+
- **`verify --help`** names the project's tests among its steps.
|
|
147
|
+
|
|
148
|
+
- Updated dependencies [493955a]
|
|
149
|
+
- @plitzi/plitzi-sdk@0.38.14
|
|
150
|
+
- @plitzi/sdk-server@0.38.14
|
|
151
|
+
- @plitzi/sdk-authoring@0.38.14
|
|
152
|
+
- @plitzi/sdk-schema@0.38.14
|
|
153
|
+
- @plitzi/sdk-shared@0.38.14
|
|
154
|
+
- @plitzi/sdk-style@0.38.14
|
|
155
|
+
|
|
3
156
|
## 0.38.13
|
|
4
157
|
|
|
5
158
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -133,6 +133,24 @@ hand-build a URI — every write and search response hands back the URI to use n
|
|
|
133
133
|
The tool functions are exported directly (`apply`, `search`, `read`, `validate`, `tools`), so a consumer can run
|
|
134
134
|
them in-process instead of speaking MCP over HTTP.
|
|
135
135
|
|
|
136
|
+
### A project's own agent drawing widgets
|
|
137
|
+
|
|
138
|
+
`plitzi_render` hands its widget to a host that draws `ui://` resources — claude.ai, Desktop. An agent working for a
|
|
139
|
+
project (a voice assistant, a terminal session) draws in the project's own page instead, so the render comes apart:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { render, renderGuideText, renderWidgetShape } from '@plitzi/sdk-mcp/render';
|
|
143
|
+
|
|
144
|
+
// A tool of the project's own MCP server, its input `renderWidgetShape`, its guide `renderGuideText`.
|
|
145
|
+
const answer = render({ operations }, { base: space.style }); // the project's authored space
|
|
146
|
+
if (answer.rendered) {
|
|
147
|
+
publish(answer.offlineData); // what a `plitziSdk` element is bound to (`offlineData`)
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`base` is the style of the space that shows the widget: its tokens in both themes, its classes and its fonts come
|
|
152
|
+
with it, so the widget looks like the page around it. Left out, the widget starts from nothing, as one in a chat does.
|
|
153
|
+
|
|
136
154
|
## Draft preview
|
|
137
155
|
|
|
138
156
|
The two halves live in different packages, joined by a one-shot token:
|
|
@@ -204,6 +222,7 @@ MCP server, [self-hosting/07](../../examples/self-hosting/07-ssr-preview) is the
|
|
|
204
222
|
|---|---|
|
|
205
223
|
| `@plitzi/sdk-mcp/server` | The dedicated MCP server and its clients. What an MCP deployment wires. |
|
|
206
224
|
| `@plitzi/sdk-mcp` | Everything above plus the pipeline stages, `createPreview` and the tool engine. |
|
|
225
|
+
| `@plitzi/sdk-mcp/render` | `plitzi_render`'s widget render alone — no MCP server, no SSR, no OAuth — for a project's own agent. |
|
|
207
226
|
|
|
208
227
|
The split is about weight, not taste: ESM re-exports load eagerly, so the barrel pulls the draft-preview path and
|
|
209
228
|
the renderer it reaches into. A `no-restricted-imports` rule and a test in `src/packageBoundary.test.ts` keep this
|
|
@@ -977,6 +977,10 @@ Pages can be grouped into **folders** (the sidebar tree). A folder is \`{ ref, n
|
|
|
977
977
|
a page read reports its current \`enabled\`). Disabling only affects the **published SDK runtime** — the page stops
|
|
978
978
|
being routable/accessible to end users. It stays fully **editable here**: you can still read it and apply any op to
|
|
979
979
|
a disabled page. This does not delete it — \`deletePage\` does.
|
|
980
|
+
- **A page that fills the window** (a dashboard, a board) takes \`upsertPage\`'s \`documentScroll: "locked"\`: the
|
|
981
|
+
document holds still and only parts with \`overflow: auto\` scroll, so a finger on a tablet no longer drags and
|
|
982
|
+
bounces the page. \`"noBounce"\` keeps the scroll without the rubber band; \`null\` undoes either. Never an
|
|
983
|
+
\`html, body\` rule in \`customCss\`: that holds every page, and the site the space is a widget on.
|
|
980
984
|
|
|
981
985
|
## Settings
|
|
982
986
|
Space-level configuration lives in \`plitzi://settings/{env}\` and is edited with a single **\`patchSettings\`** op
|
|
@@ -3,6 +3,8 @@ import { ResourceEnvelope } from '../types';
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
export declare const RENDER_GUIDE_URI = "plitzi://render/guide";
|
|
5
5
|
export declare const RENDER_TYPES_URI = "plitzi://render/types";
|
|
6
|
+
/** How a widget is authored: what an agent reads before its first render, whichever server hands it the tool. */
|
|
7
|
+
export declare const renderGuideText: string;
|
|
6
8
|
/** The same two documents through the READ path (the router, hence plitzi_read), not only as MCP resources: an
|
|
7
9
|
* agent that reaches for the batch-read tool — the reflex the guide itself teaches — must not be told its own
|
|
8
10
|
* authoring guide does not exist. Space-independent, like the core singletons. */
|
|
@@ -34,6 +34,7 @@ var renderTypes = () => {
|
|
|
34
34
|
types
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
+
/** How a widget is authored: what an agent reads before its first render, whichever server hands it the tool. */
|
|
37
38
|
var renderGuideText = `# plitzi_render — authoring guide
|
|
38
39
|
|
|
39
40
|
\`plitzi_render\` shows the user a live UI widget. You build it as a list of \`operations\` (applied IN ORDER) that
|
|
@@ -504,4 +505,4 @@ var registerRenderResources = (server, log) => {
|
|
|
504
505
|
});
|
|
505
506
|
};
|
|
506
507
|
//#endregion
|
|
507
|
-
export { RENDER_GUIDE_URI, RENDER_TYPES_URI, readRenderResource, registerRenderResources };
|
|
508
|
+
export { RENDER_GUIDE_URI, RENDER_TYPES_URI, readRenderResource, registerRenderResources, renderGuideText };
|
|
@@ -72,7 +72,7 @@ var createMcpServer = async ({ adapters, getGrant, preview, screenshot, logger,
|
|
|
72
72
|
const getSpace = () => spacePromise ??= loadSpace();
|
|
73
73
|
const server = new McpServer({
|
|
74
74
|
name: "plitzi-mcp",
|
|
75
|
-
version: "0.38.
|
|
75
|
+
version: "0.38.14"
|
|
76
76
|
}, { instructions: hasSpace ? serverInstructions : widgetsOnlyInstructions });
|
|
77
77
|
const { getChanges } = adapters;
|
|
78
78
|
registerResources(server, getSpace, MCP_ENV, log, hasSpace, getChanges ? (query) => getChanges(requireSpaceId(), MCP_ENV, query) : void 0);
|
|
@@ -82,6 +82,10 @@ export declare const applyShape: {
|
|
|
82
82
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
83
83
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
84
84
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
85
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
86
|
+
locked: "locked";
|
|
87
|
+
noBounce: "noBounce";
|
|
88
|
+
}>>>;
|
|
85
89
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
86
90
|
}, z.core.$strict>, z.ZodObject<{
|
|
87
91
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -78,6 +78,10 @@ export declare const documentOperation: z.ZodDiscriminatedUnion<readonly [z.ZodO
|
|
|
78
78
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
79
79
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
80
80
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
81
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
82
|
+
locked: "locked";
|
|
83
|
+
noBounce: "noBounce";
|
|
84
|
+
}>>>;
|
|
81
85
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
82
86
|
}, z.core.$strict>, z.ZodObject<{
|
|
83
87
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -1496,6 +1500,10 @@ export declare const operation: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1496
1500
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1497
1501
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1498
1502
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
1503
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
1504
|
+
locked: "locked";
|
|
1505
|
+
noBounce: "noBounce";
|
|
1506
|
+
}>>>;
|
|
1499
1507
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1500
1508
|
}, z.core.$strict>, z.ZodObject<{
|
|
1501
1509
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -3193,6 +3201,10 @@ export declare const operations: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
3193
3201
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
3194
3202
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
3195
3203
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
3204
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
3205
|
+
locked: "locked";
|
|
3206
|
+
noBounce: "noBounce";
|
|
3207
|
+
}>>>;
|
|
3196
3208
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
3197
3209
|
}, z.core.$strict>, z.ZodObject<{
|
|
3198
3210
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -4885,6 +4897,10 @@ export declare const documentOperations: z.ZodArray<z.ZodDiscriminatedUnion<read
|
|
|
4885
4897
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4886
4898
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4887
4899
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
4900
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
4901
|
+
locked: "locked";
|
|
4902
|
+
noBounce: "noBounce";
|
|
4903
|
+
}>>>;
|
|
4888
4904
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4889
4905
|
}, z.core.$strict>, z.ZodObject<{
|
|
4890
4906
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -81,6 +81,10 @@ export declare const elementOps: {
|
|
|
81
81
|
layout: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
|
|
82
82
|
layoutContainer: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
|
|
83
83
|
enabled: import('zod').ZodOptional<import('zod').ZodBoolean>;
|
|
84
|
+
documentScroll: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodEnum<{
|
|
85
|
+
locked: "locked";
|
|
86
|
+
noBounce: "noBounce";
|
|
87
|
+
}>>>;
|
|
84
88
|
flag: import('zod').ZodOptional<import('zod').ZodNullable<import('zod').ZodString>>;
|
|
85
89
|
}, import('zod/v4/core').$strip>;
|
|
86
90
|
deletePage: import('zod').ZodObject<{
|
|
@@ -11,6 +11,10 @@ export declare const upsertPageOp: z.ZodObject<{
|
|
|
11
11
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
12
12
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
13
13
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
15
|
+
locked: "locked";
|
|
16
|
+
noBounce: "noBounce";
|
|
17
|
+
}>>>;
|
|
14
18
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
15
19
|
}, z.core.$strip>;
|
|
16
20
|
export type UpsertPage = z.infer<typeof upsertPageOp>;
|
|
@@ -3,7 +3,7 @@ import { layoutsUri, pageUri, pagesUri } from "../../../../helpers/uris.js";
|
|
|
3
3
|
import { empty, fail } from "../../../../helpers/opResult.js";
|
|
4
4
|
import { elementFlag } from "../shared.js";
|
|
5
5
|
import { guardNewRef } from "../write.js";
|
|
6
|
-
import { flagGateOf } from "@plitzi/sdk-authoring";
|
|
6
|
+
import { DOCUMENT_SCROLLS, flagGateOf } from "@plitzi/sdk-authoring";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
//#region src/modules/mcp/tools/operations/schema/pages/upsertPage.ts
|
|
9
9
|
var upsertPageOp = z.object({
|
|
@@ -16,6 +16,7 @@ var upsertPageOp = z.object({
|
|
|
16
16
|
layout: z.string().nullable().optional().describe("Ref of the layout shell this page renders inside; \"\" or null detaches it. See plitzi://schema/{env}/layouts."),
|
|
17
17
|
layoutContainer: z.string().nullable().optional().describe("Ref of the container INSIDE that shell where this page body goes — the slot. Must belong to it."),
|
|
18
18
|
enabled: z.boolean().optional().describe("false disables the page in the published SDK runtime (not routable/accessible to end users); it stays editable here. Defaults to true"),
|
|
19
|
+
documentScroll: z.enum(DOCUMENT_SCROLLS).nullable().optional().describe("\"locked\": the document never moves, only overflow:auto panels scroll; \"noBounce\": no rubber band"),
|
|
19
20
|
flag: elementFlag.nullable().optional()
|
|
20
21
|
}).describe("Create a page, or update it when ref already exists (only the fields you pass change).");
|
|
21
22
|
var upsertPage = (space, env, op) => {
|
|
@@ -66,6 +67,8 @@ var upsertPage = (space, env, op) => {
|
|
|
66
67
|
...layoutValue !== void 0 ? { layout: layoutValue } : {},
|
|
67
68
|
...slotValue !== void 0 ? { layoutContainer: slotValue } : {}
|
|
68
69
|
};
|
|
70
|
+
if (op.documentScroll === null) Reflect.deleteProperty(existing.attributes, "documentScroll");
|
|
71
|
+
else if (op.documentScroll !== void 0) existing.attributes.documentScroll = op.documentScroll;
|
|
69
72
|
if (op.flag === null) Reflect.deleteProperty(existing.definition, "flag");
|
|
70
73
|
else if (op.flag !== void 0) existing.definition.flag = flagGateOf(op.flag, `Page "${op.ref}"`);
|
|
71
74
|
return {
|
|
@@ -90,7 +93,8 @@ var upsertPage = (space, env, op) => {
|
|
|
90
93
|
enabled: op.enabled ?? true,
|
|
91
94
|
folder: folderValue ?? "",
|
|
92
95
|
layout: layoutValue ?? "",
|
|
93
|
-
layoutContainer: slotValue ?? ""
|
|
96
|
+
layoutContainer: slotValue ?? "",
|
|
97
|
+
...op.documentScroll ? { documentScroll: op.documentScroll } : {}
|
|
94
98
|
},
|
|
95
99
|
definition: {
|
|
96
100
|
rootId: id,
|
|
@@ -3,7 +3,7 @@ import { descendantCount, elementById, getLayoutElements, getPageElements, nameO
|
|
|
3
3
|
import { bindingsToAI, flowsFromInteractions } from "../../../helpers/interactions.js";
|
|
4
4
|
import { definitionToAI, globalStylesForType, idStyleToAI } from "../style/translator.js";
|
|
5
5
|
import { descendants } from "@plitzi/sdk-schema/helpers/elementTree";
|
|
6
|
-
import { flagSpecOf, splitNotificationsCss } from "@plitzi/sdk-authoring";
|
|
6
|
+
import { flagSpecOf, isDocumentScroll, splitNotificationsCss } from "@plitzi/sdk-authoring";
|
|
7
7
|
//#region src/modules/mcp/tools/operations/schema/translator.ts
|
|
8
8
|
var splitClasses = (value) => value ? value.split(/\s+/).filter(Boolean) : [];
|
|
9
9
|
var propsOf = (el) => {
|
|
@@ -84,6 +84,7 @@ var pageSkeletonToAI = (schema, pageEl, style) => {
|
|
|
84
84
|
slug,
|
|
85
85
|
default: pageEl.attributes.default === true,
|
|
86
86
|
enabled: pageEl.attributes.enabled !== false,
|
|
87
|
+
...isDocumentScroll(pageEl.attributes.documentScroll) ? { documentScroll: pageEl.attributes.documentScroll } : {},
|
|
87
88
|
routeParams: slugRouteParams(slug),
|
|
88
89
|
tree: orderedChildren(schema, pageEl).map((child) => skeletonNode(schema, child, style))
|
|
89
90
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import { Operation } from './operations';
|
|
4
|
-
import { OfflineDataRaw } from '@plitzi/sdk-shared';
|
|
2
|
+
import { RenderWidgetInput } from './renderWidget';
|
|
5
3
|
export declare const renderShape: {
|
|
4
|
+
patch: z.ZodOptional<z.ZodBoolean>;
|
|
5
|
+
renderId: z.ZodOptional<z.ZodString>;
|
|
6
6
|
operations: z.ZodArray<z.ZodDiscriminatedUnion<readonly [z.ZodObject<{
|
|
7
7
|
type: z.ZodLiteral<"upsertElement">;
|
|
8
8
|
pageRef: z.ZodString;
|
|
@@ -80,6 +80,10 @@ export declare const renderShape: {
|
|
|
80
80
|
layout: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
81
81
|
layoutContainer: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
82
82
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
83
|
+
documentScroll: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
84
|
+
locked: "locked";
|
|
85
|
+
noBounce: "noBounce";
|
|
86
|
+
}>>>;
|
|
83
87
|
flag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
84
88
|
}, z.core.$strict>, z.ZodObject<{
|
|
85
89
|
type: z.ZodLiteral<"deletePage">;
|
|
@@ -1421,40 +1425,9 @@ export declare const renderShape: {
|
|
|
1421
1425
|
type: z.ZodLiteral<"deleteFont">;
|
|
1422
1426
|
family: z.ZodString;
|
|
1423
1427
|
}, z.core.$strict>], "type">>;
|
|
1424
|
-
patch: z.ZodOptional<z.ZodBoolean>;
|
|
1425
|
-
renderId: z.ZodOptional<z.ZodString>;
|
|
1426
1428
|
};
|
|
1427
|
-
export type RenderInput = {
|
|
1428
|
-
operations: Operation[];
|
|
1429
|
+
export type RenderInput = RenderWidgetInput & {
|
|
1429
1430
|
patch?: boolean;
|
|
1430
1431
|
renderId?: string;
|
|
1431
1432
|
};
|
|
1432
|
-
/** What the render needs from its host beyond the operations. Optional: a host that wires no resource endpoint
|
|
1433
|
-
* still renders a widget — one whose external URLs travel as authored and load only where the surface allows
|
|
1434
|
-
* their origin. */
|
|
1435
|
-
export type RenderOptions = {
|
|
1436
|
-
proxy?: ResourceProxy;
|
|
1437
|
-
};
|
|
1438
|
-
export type RenderResponse = {
|
|
1439
|
-
rendered: false;
|
|
1440
|
-
errors: {
|
|
1441
|
-
path: string;
|
|
1442
|
-
message: string;
|
|
1443
|
-
hint?: string;
|
|
1444
|
-
}[];
|
|
1445
|
-
warnings?: string[];
|
|
1446
|
-
} | {
|
|
1447
|
-
rendered: true;
|
|
1448
|
-
rootRef: string;
|
|
1449
|
-
elementCount: number;
|
|
1450
|
-
offlineData: OfflineDataRaw;
|
|
1451
|
-
/** The batch this render was built from, EXPANDED (repeats already unrolled). The view keeps it so a later
|
|
1452
|
-
* patch has something to merge into; it is never shown to the model. */
|
|
1453
|
-
operations: Operation[];
|
|
1454
|
-
/** One line per interaction flow the widget actually stored — what got wired to what. Absent when the
|
|
1455
|
-
* widget has no flows, which is most of them. */
|
|
1456
|
-
interactions?: string[];
|
|
1457
|
-
warnings?: string[];
|
|
1458
|
-
};
|
|
1459
|
-
export declare const render: (input: RenderInput, options?: RenderOptions) => RenderResponse;
|
|
1460
1433
|
export declare const renderTool: import('.').ToolDef;
|
|
@@ -1,68 +1,16 @@
|
|
|
1
1
|
import { iconFontCss } from "../apps/render/styles.js";
|
|
2
2
|
import { RENDER_APP_URI } from "../apps/render/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import { proxifyResources } from "../proxy/rewrite.js";
|
|
5
|
-
import { compactDocumentOperations, documentOperations } from "./operations/index.js";
|
|
6
|
-
import { draftBatch } from "./shared/draftBatch.js";
|
|
3
|
+
import { compactDocumentOperations } from "./operations/index.js";
|
|
7
4
|
import { defineTool } from "./shared/tool.js";
|
|
8
|
-
import {
|
|
5
|
+
import { render, renderWidgetShape } from "./renderWidget.js";
|
|
9
6
|
import { randomUUID } from "node:crypto";
|
|
10
7
|
import { z } from "zod";
|
|
11
|
-
import { generateCache } from "@plitzi/sdk-style/StyleHelper";
|
|
12
8
|
//#region src/modules/mcp/tools/render.ts
|
|
13
|
-
var HOST_PAGE_REF = "render";
|
|
14
|
-
var seedSpace = () => {
|
|
15
|
-
const space = emptySpace();
|
|
16
|
-
space.schema.definition.name = "Widget";
|
|
17
|
-
space.schema.flat[HOST_PAGE_REF] = {
|
|
18
|
-
id: HOST_PAGE_REF,
|
|
19
|
-
attributes: {
|
|
20
|
-
slug: "",
|
|
21
|
-
name: "Render",
|
|
22
|
-
default: true
|
|
23
|
-
},
|
|
24
|
-
definition: {
|
|
25
|
-
rootId: HOST_PAGE_REF,
|
|
26
|
-
label: "Page",
|
|
27
|
-
type: "page",
|
|
28
|
-
items: [],
|
|
29
|
-
styleSelectors: { base: "" }
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
space.schema.pages = [HOST_PAGE_REF];
|
|
33
|
-
return space;
|
|
34
|
-
};
|
|
35
|
-
var noWarnings = (warnings) => warnings.length > 0 ? warnings : void 0;
|
|
36
9
|
var renderShape = {
|
|
37
|
-
|
|
10
|
+
...renderWidgetShape,
|
|
38
11
|
patch: z.boolean().optional().describe("Set true to CHANGE a widget you already rendered instead of rebuilding it: send its `renderId` and ONLY the operations that differ (patchDefinition, patchElement, deleteElement, a new repeatElement…). The widget merges them into the batch it was built from and reports back what it applied. The refs it already has (card-1, blk-2-3) are the ones to address. If that widget cannot be recovered — a surface that renders none, a host that keeps no storage — the answer says so and you re-send the whole batch."),
|
|
39
12
|
renderId: z.string().optional().describe("Handle returned by a previous render. Required with patch:true; it names the widget being changed.")
|
|
40
13
|
};
|
|
41
|
-
var render = (input, options = {}) => {
|
|
42
|
-
const result = draftBatch(seedSpace(), "main", input.operations, "widget");
|
|
43
|
-
if (!result.ok) return {
|
|
44
|
-
rendered: false,
|
|
45
|
-
errors: result.errors,
|
|
46
|
-
warnings: noWarnings(result.warnings)
|
|
47
|
-
};
|
|
48
|
-
const { ops, draft: space } = result;
|
|
49
|
-
const behaviour = interactionReport(space);
|
|
50
|
-
const warnings = [...result.warnings, ...behaviour.warnings];
|
|
51
|
-
if (options.proxy) warnings.push(...proxifyResources(space, options.proxy));
|
|
52
|
-
space.style.cache = generateCache(space.style);
|
|
53
|
-
return {
|
|
54
|
-
rendered: true,
|
|
55
|
-
operations: ops,
|
|
56
|
-
...behaviour.flows ? { interactions: behaviour.flows } : {},
|
|
57
|
-
rootRef: HOST_PAGE_REF,
|
|
58
|
-
elementCount: Object.keys(space.schema.flat).length - 1,
|
|
59
|
-
offlineData: {
|
|
60
|
-
schema: space.schema,
|
|
61
|
-
style: space.style
|
|
62
|
-
},
|
|
63
|
-
warnings: noWarnings(warnings)
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
14
|
/** A patch is not rendered here — it CANNOT be: this server keeps nothing between calls (no session, no store,
|
|
67
15
|
* any replica answers any request), so the previous widget only exists on the host side. The delta therefore
|
|
68
16
|
* travels as a courier result: the model pays for the delta alone, the view recovers the batch that widget was
|
|
@@ -156,4 +104,4 @@ var renderTool = defineTool({
|
|
|
156
104
|
run: (input, ctx) => input.patch === true ? toPatchResult(input.operations, input.renderId) : toRenderResult(render(input, { proxy: ctx.proxy }), input.renderId ?? newRenderId())
|
|
157
105
|
});
|
|
158
106
|
//#endregion
|
|
159
|
-
export {
|
|
107
|
+
export { renderShape, renderTool };
|