Repository navigation
Conversation
Documentation build overview
32 files changed ·
|
davisagli
left a comment
There was a problem hiding this comment.
Thoughts:
- We could potentially also use this info to auto-generate reference docs. So I would put it in a more generic key like
docsinstead ofllm. - I wonder if it would be more useful for an agent to produce Markdown output instead of structured json.
- Do we need to have a way to translate this info?
- Maybe we should have a function that returns this info instead of just an object in the config, so that it can take into account dynamic configuration.
| description: | ||
| 'Displays an internal or external image with optional title, description, alt text, alignment, size, and link. Use for standalone images within page content.', | ||
| usage_notes: | ||
| "For INTERNAL Plone images: set 'url' to the Image content object path (e.g. /my-folder/my-image) and set 'image_field' to 'image'. For EXTERNAL images: set 'url' to the full image URL and omit 'image_field'. Do NOT use @@images URLs for internal images. Do not set 'align' or 'size' when nesting inside anpther block (e.g. gridBlock) since the parent block controls layout.", |
There was a problem hiding this comment.
It is ok to use a full URL for internal images also. Either way, it gets converted into a UID-based path when it is stored.
| "For INTERNAL Plone images: set 'url' to the Image content object path (e.g. /my-folder/my-image) and set 'image_field' to 'image'. For EXTERNAL images: set 'url' to the full image URL and omit 'image_field'. Do NOT use @@images URLs for internal images. Do not set 'align' or 'size' when nesting inside anpther block (e.g. gridBlock) since the parent block controls layout.", | |
| "For INTERNAL Plone images: set 'url' to the Image content object path (e.g. /my-folder/my-image) and set 'image_field' to 'image'. For EXTERNAL images: set 'url' to the full image URL and omit 'image_field'. Do NOT use @@images URLs for internal images. Do not set 'align' or 'size' when nesting inside another block (e.g. gridBlock) since the parent block controls layout.", |
| querystring: | ||
| "Required. Object with 'query' (required array of filter criteria), optional 'sort_on', 'sort_order', 'sort_order_boolean', 'b_size' (string, items per page), 'limit' (string, max total results). Multiple criteria are combined with AND logic. Each criterion requires 'i' (index name), 'o' (FULL operation string — see below), 'v' (value). " + | ||
| 'Indexes: portal_type, path, Title, Description, SearchableText, Creator, getId, review_state, Subject, created, modified. ' + | ||
| 'Operations (always use the FULL string): ' + |
There was a problem hiding this comment.
The available indexes and operations are dynamic based on the configuration in the site's registry. This could refer to the API for getting them instead of including a hardcoded list.
| mostUsed: false, | ||
| blockHasOwnFocusManagement: true, | ||
| sidebarTab: 0, | ||
| llm: { |
There was a problem hiding this comment.
None of this is really specifically useful to LLMs. It's documentation that is also useful to humans. I'd suggest calling this key docs rather than llms (name it what it is, not based on one specific thing that will consume it).
| "sort_on values: 'sortable_title', 'effective', 'created', 'modified', 'getObjPositionInParent', 'start', 'end', 'review_state'. " + | ||
| "sort_order: 'ascending' (default) or 'descending'.", | ||
| variation: | ||
| "Required. 'default' (simple list), 'summary' (list with images), 'imageGallery' (image carousel), 'eventCalendar' (calendar view), 'listDate' (list with date).", |
There was a problem hiding this comment.
The available variations can be customized in Volto config.
|
@davisagli @danalvrz I took a look at the current state, answering to your main questions:
+1 (done)
+1000 I think we reached a point that LLMs are more powerful with short markdown explanations. Also, it's instant docs for humans too. My take on this is that it feels wrong to have the structured docs in JSON... I see no reason for it, although some that I can't see now related to the MCP (which should not be the case, as I'd decouple the use case from the endpoint).
Which would be the point? Machines talk any language, unless we'd like to generate docs out of this endpoint, which is unlikely... Anyways, you could define a .po with generic keys
I am missing the point of this, if can we elaborate the use case it would be great. Also, we need docs for this endpoint, since it's a whole new category (frontend endpoint), I guess the question is where in the docs structure. |
|
@davisagli @danalvrz I'd love to see this happening before the conf. Any feedback from my comment? |
|
Self-review of the current state, including @davisagli's comments. I think the idea is right: putting each block's contract next to its Problems in the code
The docs content
Proposed direction
Porting to Plone AuroraIt fits well and would be easier there:
Summary: worth merging once the auth question and the per-block error handling are fixed, with |
The block configuration already ships in the public client bundle, so the endpoint no longer requires authentication and is cacheable. The SSR change that let an Authorization header override the auth_token cookie is removed.
A block whose schema or schemaEnhancer throws now comes back with schema: null and an error, and the other blocks are still returned, instead of the whole request failing.
The top-level schema now gets the same enhancer chain as the block settings
form (default variation, then the block's own schemaEnhancer). Each variation
is returned as { id, title, isDefault, schema } with its own enhanced schema,
instead of the raw variation config with its components and functions.
Schemas can hold widget components, React elements, functions and cyclic references. toJSONSafe drops those before the response is sent, so the output is predictable instead of relying on JSON.stringify side effects.
docs is now a Markdown string, or a function of the block config that returns one; its first paragraph is the block's summary. Variations can have their own docs. The core block docs move to a docs.js file next to each block and are written for human readers too: no hardcoded query indexes or operations, and the grid docs come from its configuration.
The slate block gets Markdown docs explaining value and plaintext, the element types, inline formatting as wrapping elements, and links, with examples whose plaintext is checked against the editor's serializer.
?format=md, or an Accept header that asks for text/markdown and not JSON, returns one Markdown document: a section per block with its docs, its variations and its schema. A variation's schema is only included when it differs from the block's. The detached text block gets short docs of its own instead of repeating the text block's.
With a content path in front, the endpoint only lists the blocks the block chooser offers when editing that content: it fetches the content (with the navroot expander) and the current user using the caller's auth_token cookie, and evaluates each block's restricted the way BlockChooser does, now through a shared isBlockRestricted helper. Backend 401, 403 and 404 are passed through, and context responses are private. The root list flags restricted blocks (true, or 'context' when it depends on the content). Regenerated the type declarations, which also adds the ones for the block docs files.
Adds a page on writing a block's docs and on the endpoint: the list and detail routes, the Markdown format, the context path, errors and caching. The block settings reference mentions the new docs option.
Block docs are now Markdown files next to each block, named *.block.md,
whose frontmatter names the block (and optionally a variation). They are
no longer part of the block configuration.
@plone/registry gets createBlocksDocsLoader, which finds those files in
core and in every add-on at build time and writes a module that imports
them with ?raw. Volto's build aliases it as load-blocks-docs and adds an
asset/source rule for *.block.md?raw. Only the @blocks-schema middleware
imports it, so the docs stay out of the client bundle. Later add-ons
replace the docs of earlier ones.
{{key}} placeholders are filled from the block configuration, which the
grid docs use for its allowed blocks and maximum count. The block's docs
config key remains as an optional function to tailor the result.
…sschemaendpoint * origin/blocksschemaendpoint: (32 commits) Fix webpack duplication and react-aria catalog minimums, add minimumReleaseAge (#8464) Restore pnpm store cache fallback and fix volto-razzle CI job (#8463) Update packages in pnpm-lock.yaml (#8461) Fix invisible keyboard focus on single react-select widgets (#8457) Use pnpm/action-setup and setup-node pnpm cache in CI (#8459) Don't show the parent's tags and related items below the add form. (#8453) Fix adding a field with the default type in the schema editor (#8454) Pick the SSR locale only from supportedLanguages (#8448) (#8449) Fixed link redirects from `thevalleyofcode.com` to `flaviocopes.com` (#8451) Fix release-it from babel-preset-razzle Release 19.5.0 Release @plone/volto-slate 19.1.0 Release @plone/razzle 1.0.2 Release @plone/razzle-dev-utils 1.0.1 Release @plone/scripts 4.0.2 Release @plone/babel-preset-razzle 1.0.2 Release @plone/registry 3.0.3 Release @plone/components 4.2.2 Release @plone/types 2.1.0 Fix diff of text block word by word (#8429) ...
blocks/documentation.md explains why blocks carry their own docs and how to write them: *.block.md files, conventions, variations, placeholders and the optional docs function. reference/blocks-schema-endpoint.md is the reference of the endpoint. Each links to the other.
|
@davisagli @danalvrz I think this is complete now, and covers all our concerns. I'd go for it, make the Aurora counterpart from this specs and way to proceed. |
davisagli
left a comment
There was a problem hiding this comment.
@sneridagh I like this a lot!
The only thing I am not sure about is making /@blocks-schema public. Yeah, the blocks config was already part of the frontend bundle, but the block docs weren't. I can imagine that in some cases they might include private instructions for editors, or even contact information.
| Request it from the frontend URL, for example `http://localhost:3000/@blocks-schema`. | ||
| ``` | ||
|
|
||
| The endpoint is public, because everything it returns is already part of the frontend code that every visitor downloads. |
There was a problem hiding this comment.
I don't think this is true. The block docs are not part of the frontend bundle, and they could contain private editing instructions or even contact information.
There was a problem hiding this comment.
@davisagli after the last interations, I agree that this statement is no longer true (because we improved it). So, yes, could be that returning the endpoint now it's better that it requires auth (any auth).
Merge first:
Adds a
/@blocks-schemaendpoint to the Volto server. It describes the blocks registered on the site: their documentation, their variations, and the schema of their data. It's meant for agents (for example through the Plone MCP server) and for generating reference docs.Only the frontend knows which blocks a site has, since core, add-ons and the project configuration all register and change them, so the endpoint lives in the Volto server, not in plone.restapi.
What it does
GET /@blocks-schema: ID, title and summary of every block.?full=1returns the full details for all of them.GET /@blocks-schema/<id>: one block'sdocs,schemaandvariations.?format=md, or anAcceptheader that asks fortext/markdownand not for JSON, returns one Markdown document with a section per block. JSON stays the default.GET /<path>/@blocks-schemalists only the blocks the block chooser offers when editing that item. It evaluates each block'srestrictedwith the same context as the editor (content with thenavrootexpander, content type, current user), using the caller'sauth_tokencookie.Details
public, max-age=300and vary onAcceptandAccept-Language. Per-content responses are private.applySchemaEnhancer, so the default variation's enhancer and then the block's own one are applied. Each variation is returned as{ id, title, isDefault, schema, docs? }, with its own schema.schema: nulland anerror.restricted: the root list flags blocks asrestricted: true, or'context'when it depends on the content. The check is now a sharedisBlockRestrictedhelper thatBlockChooseralso uses.Block docs
*.block.mdfiles next to each block. Their frontmatter names the block (block: teaser), and can add a variation (variation: imageGallery). The first paragraph is the block's summary. The docs aren't part of the block configuration, and authors don't register anything.@plone/registrygetscreateBlocksDocsLoader. It finds the*.block.mdfiles in Volto'ssrcand in every add-on, and writes a module that imports each one with?raw. Volto's build aliases that module asload-blocks-docsand adds anasset/sourcerule for*.block.md?raw. Only the@blocks-schemamiddleware imports it, so the docs never reach the client bundle. When two files document the same block, the add-on loaded later wins, so a project can replace core docs.{{key}}is filled from the block's top-level configuration. The grid docs use{{maxLength}}and{{allowedBlocks}}.docsin the block configuration (@plone/types) is now only an optional function,({ blockConfig, intl, markdown }) => string, for special cases.docs.block.md. The text block's (volto-slate) documents thevalueformat, with elements, inline formatting as wrapping elements, links, andplaintext. A test checks that its examples'plaintextmatches the editor's serializer.@type. Vitest generates the same loader, and CI already builds@plone/registrybefore the unit tests.Review comments
Answers to @davisagli's review:
llm: it'sdocs.docsfunction can tailor the text. Variations are returned from the configuration, not written into the docs.Related
blockSchemato the core blocks that didn't have one yet (image, maps, toc, html, leadimage). Until it's merged, those blocks returnschema: nullhere.docs/source/blocks/blocks-schema.md.HEAD /@blocks-schemacurrently returns Volto's HTML page instead of the endpoint's response, because something earlier in Volto's server handles HEAD requests first. It isn't caused by this PR.Not covered
allowedBlocksof container blocks such as the grid./@blocks-schemaalways returns the unfiltered list, so there's currently no way to ask what's offered at the site root.