Skip to content

Merge main into stable#3589

Closed
superdoc-bot[bot] wants to merge 22 commits into
stablefrom
merge/main-into-stable-2026-05-30
Closed

Merge main into stable#3589
superdoc-bot[bot] wants to merge 22 commits into
stablefrom
merge/main-into-stable-2026-05-30

Conversation

@superdoc-bot
Copy link
Copy Markdown
Contributor

@superdoc-bot superdoc-bot Bot commented May 30, 2026

Summary

  • creates merge/main-into-stable-2026-05-30 from stable
  • merges main into the candidate branch
  • opens the promotion PR to stable

Auto-created by promote-stable workflow.

andrii-harbour and others added 22 commits May 27, 2026 23:10
- Added validation for the MCP_PRESET environment variable in `server.ts` to ensure only supported presets are accepted at startup, preventing silent misconfigurations.
- Introduced a new preset registry in `presets.ts`, allowing for the management of LLM tool presets, with the initial implementation supporting only the 'legacy' preset.
- Updated the Node SDK to expose preset-related functions (`getPreset`, `listPresets`, `DEFAULT_PRESET`) for easier access to preset information.
- Created tests for preset validation and registry functionality, ensuring that unknown presets trigger appropriate errors and that the legacy preset behaves as expected.
- Added corresponding Python SDK support for the preset registry, mirroring the Node implementation for consistency across languages.
A searchable "Smart tags" palette in the contract-templates sidebar. Clicking a
tag inserts it as an inline content control at the caret, and the inserted field
paints with the SAME token look (--tag-* / .smart-tag) as the palette chip - so
the sidebar tag and the in-editor field read as one object. This is the core
custom-SDT story: turn off built-in chrome, style the painted wrapper, author
fields from your own UI.

Insert path (verified): ui.selection.capture() -> bridge the TextTarget to a
collapsed SelectionTarget -> editor.doc.create.contentControl({ at, content,
tag }) -> ui.contentControls.focus(). Adds a behavior test proving collapsed-
caret insertion works (no API gap) and a demo acceptance test for chip -> field.
- Smart tags get a deliberate amber identity (one --tag-* token set drives both
  the palette chip and the painted in-editor field, so they look identical).
- Two-way loop: clicking a smart-field token in the document highlights its
  sidebar chip (content-control:click); cleared on blur (active-change).
- README reframed around the custom content-control UI story (chrome:'none' +
  host-owned field look + smart-tags authoring), with the new flow documented.
- Adds a demo test for the click-token -> highlight-chip sync.
…field chip

- Remove the floating field chip (sd-field-chip): redundant now fields are
  styled inline, and it clashed with the amber palette. Drops field-chip.ts and
  the chip-anchor test (the chip was the only getRect/viewport.observe consumer).
- Inline and block fields now share one amber token language: inline as a token
  pill, block clauses as a quiet left-rail card (a region, not a token).
- Kill the jitter: under chrome:'none' SuperDoc resets the SDT border/fill on
  hover (:hover / .sdt-group-hover) and select (.ProseMirror-selectednode) so
  consumers own the look; without re-asserting, the box shifted ~2px and lost
  the amber. We re-assert both states for inline and block to hold the exact box
  and keep a controlled amber fill. The !important is ours, to win over the
  reset without coupling to SuperDoc's selector specificity -- a custom-UI
  styling rough edge (no first-class per-control hook yet) worth a follow-up.
- Size the sidebar chips to match the in-editor pills.
- Add regression tests asserting the inline pill and block clause boxes stay
  constant across hover/select (no jitter).
…SDTs

Reframe the contract-templates demo as a building-block library on a locked
template surface, driven entirely through the public superdoc/ui + editor.doc.*
API with chrome:'none'. This shows the legal-tech workflow: assemble a contract
from governed, reusable Word content controls whose variables stay consistent.

- Enable the formatting toolbar and center the editor. Fold Clauses into a
  Template tab; the sidebar is now Template (build) + Values (fill).
- Template tab is a catalog: smart-field chips and clause cards (each with
  category / jurisdiction / version and a "used N times" count, plus a
  library-only Indemnification clause). Drag or click to insert; a field goes
  inline at the caret, a clause snaps to a block boundary. Inserts resolve the
  drop point with ui.viewport.positionAt.
- Every control is contentLocked, so it can't be edited by typing. Fields show
  their name token (e.g. DISCLOSING_PARTY) as a placeholder. Values are filled
  only through the Values form, which broadcasts to every occurrence - including
  ones nested in a locked clause (the write briefly unlocks clauses, since a
  clause's content lock otherwise silently vetoes nested writes).
- Clauses are assembled from structured parts (prose + {field} slots): inserting
  one wraps each slot as a nested, locked inline smart field, so an inserted
  Permitted Use carries real Receiving party / Purpose fields like the seeded one.
- Remove the clause version review/replace lifecycle (out of scope here; it's a
  separate clause-lifecycle demo). Drop the floating field chip earlier in the arc.
- Rewrite the README and file header to the library model; add tests for locking,
  nested-clause broadcast, clause insert, and inserted-clause field nesting.
Three review findings from PR 3541:

1. Restore structured ToolCatalog.tools type. The refactor narrowed the
   public catalog row to `unknown[]`, breaking TS consumers that read
   tools[i].toolName etc. Move ToolCatalogEntry + ToolCatalogOperation
   into presets.ts as public types and tighten the catalog signature.

2. Fail fast on malformed provider bundles. Node and Python preset
   loaders previously coerced a missing or non-array `tools` field to
   `[]`, hiding broken codegen output behind a silently empty tool
   surface. Restore the pre-presets TOOLS_ASSET_INVALID throw at the
   preset boundary.

3. Cross-lang parity for empty-string presets. Python choose_tools
   treated `{'preset': ''}` as legacy via `or DEFAULT_PRESET`; Node and
   MCP both raise PRESET_NOT_FOUND. Use an explicit None check so
   Python matches.

Tests added covering structural catalog access, empty-string preset
fail-fast, and cross-lang parity for the empty-string case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make the clause library a single-use inclusion checklist instead of a duplicate
stamp tool. A clause is either "In contract" or available to "Add clause": a
clause already placed can't be inserted again - clicking its card reveals the
existing section, while an available card adds it (click or drag) and then flips
to "In contract". Drops the "used N times" surface. This teaches the right model:
fields are reusable variables, clauses are governed sections included once.

- Add a library-only "Return of Materials" clause carrying a nested Receiving
  party slot, so insert-with-nested-fields stays demonstrable now that the seeded
  Permitted Use is "In contract" and no longer insertable.
- Recolor fields and clauses to the SuperDoc brand blue (--sd-color-blue-500/600,
  per brand.md) instead of amber. They render as tinted/outlined pills, so they
  stay distinct from the solid-blue primary buttons.
- Update tests (single-use status badges, add-once-no-duplicate, nested-field on
  add), the README, and code comments to the single-use + blue model.
…art-tags

feat(demo): smart-tags palette for custom SDT fields (SD-3320)
@superdoc-bot superdoc-bot Bot requested a review from a team as a code owner May 30, 2026 06:10
@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

Agent docs audit

Found deterministic findings on 1 changed agent-doc item(s).

packages/superdoc/AGENTS.md (296 lines)

  • over nested-warn (296 > 200)
  • 3 broken path ref(s)

Broken path refs:

  • superdoc/style.css
  • superdoc/style.layered.css
  • superdoc/ui/react

Deterministic L1 only: no AI, no Bash, no secrets. Semantic L2/L3 audit runs weekly on main. Policy: agent-docs-policy.md.

@github-actions
Copy link
Copy Markdown
Contributor

I attempted to verify against ECMA-376 via the ecma-spec MCP tools, but the tool calls were not permitted in this session (every call returned a permissions error). I've reviewed against the ECMA-376 WordprocessingML schema from knowledge instead, and the file is small and self-contained enough to assess confidently.

Status: PASS

This handler (trackedChangeIdMapper.js) is a read-only pass that walks parsed OOXML and builds a w:id → internal UUID map for tracked changes. It doesn't emit any XML, so there are no fabricated attributes or baked-in defaults to worry about — the relevant question is just whether the elements/attributes it reads are real.

Everything it touches is legitimate WordprocessingML:

  • Elementsw:ins, w:del (the run/block-level revision elements), plus the "transparent" range markers w:commentRangeStart/End, w:bookmarkStart/End, w:proofErr, w:permStart/End, w:moveFromRangeStart/End, w:moveToRangeStart/End. All are real ECMA-376 elements, and treating the range/annotation markers as content-free (so they don't break replacement adjacency) is consistent with their spec semantics. See https://ooxml.dev/spec?q=ins and https://ooxml.dev/spec?q=del.
  • Attributesw:author, w:date, w:id, all defined on CT_TrackChange (the base type for w:ins/w:del). https://ooxml.dev/spec?q=CT_TrackChange

A couple of notes (none are violations):

  • The code reads w:author/w:date defensively (?? ''). In the schema w:author is required and w:date is optional; defaulting to empty on a reader is fine and arguably safer than assuming presence.
  • The replacement-pairing logic (matching opposite-type ins/del with same author+date, and the new "child replacement inside another author's deletion" case) is a SuperDoc interpretation of how Word serializes replacements — the spec models each w:ins/w:del as an independent revision, which is exactly what the 'independent' mode and the §17.13.5 doc comment describe. So the heuristic layers on top of the spec model rather than contradicting it.

No non-existent elements/attributes, no missing-required issues in what's read, no incorrect defaults written.

One caveat for transparency: because the MCP spec tools were blocked, I could not machine-verify the exact section number cited in the JSDoc (§17.13.5) or the precise use/default flags on CT_TrackChange. If you want that confirmed, granting the ecma-spec tool permissions and re-running would close that gap — but nothing in the diff appears spec-incorrect.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42c6d5ff9e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +113 to +116
if (
options.textModel === 'visible' &&
node.marks?.some((mark: ProseMirrorMark) => mark.type.name === TrackDeleteMarkName)
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip deleted inline leaves in visible text

When textModel: 'visible' is used, this filter only suppresses text nodes with trackDelete marks. Tracked deletions can also mark inline leaf nodes (for example images), and those still flow through the later node.isLeaf branch (tabs are emitted even earlier), so doc.getText() can include placeholders/tabs for content that should be hidden from the visible text model, shifting returned text and offsets whenever a deletion covers non-text inline content.

Useful? React with 👍 / 👎.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants