Skip to content

[Needs manual review, do not merge] Misc plugin bugs#145

Draft
roger-datocms wants to merge 9 commits intomasterfrom
fix/misc-bugs
Draft

[Needs manual review, do not merge] Misc plugin bugs#145
roger-datocms wants to merge 9 commits intomasterfrom
fix/misc-bugs

Conversation

@roger-datocms
Copy link
Copy Markdown
Contributor

Summary

Fixes the nine plugin bugs Roger filed in the Triage card table. One commit per plugin (asset-optimization bundles two related preview-mode fixes), each bumping its own package.json patch version and appending a changelog entry to the plugin's README.

Each fix was reproduced locally against claude-debugging, fixed at the root cause indicated by the cards' RCAs, and re-verified end-to-end through the DatoCMS admin UI before commit.

Plugin New version Commit
field-anchor-menu 1.0.4 80c101f
zoned-datetime-picker 1.0.3 8bfdc73
lorem-ipsum 0.2.6 3c55b3a
inverse-relationships 0.0.5 a8a455b
asset-optimization 0.7.9 e6b563f
todo-list 0.0.21 50c5d4e
slug-redirects 0.0.16 2947920
project-wide-stage-viewer 0.0.5 ad9dc54

Note: only patch bumps; nothing here is published from CI in this PR — versions are bumped in package.json so the next publish picks them up.


Per-plugin breakdown

field-anchor-menu — Fix scroll-to-field min-fields setting silently reverting to default

Bug: Setting "Minimum number of fields to show panel" to anything other than the default would appear to save, then revert to the default on the next config-screen load.

Root cause: The Final Form field stored the input as a string but the validator + plugin parameters expected a number. Number(undefined) === NaN short-circuited validation as "valid", and the persisted value got dropped silently when the plugin re-read its parameters.

Fix: Added a parseMinFieldsToShow coercion + a numeric validator, switched the input to type="number", and made the onSubmit coerce to a number before persisting. Anything <= 0 clamps to 1.

Verified: min=1 → all 6 fields shown; min=99 → panel hidden; min=0 → clamped to 1, persists across reloads.


zoned-datetime-picker — Add missing Spanish UI translations to zoned-datetime-picker

Bug: When the editor's UI locale is set to Spanish, the picker's labels stayed in English ("Suggested", "Date and time", "Time zone", etc.).

Root cause: i18n/uiLabels.ts had no es entry, so the resolver fell through to English.

Fix: Added the es translation map (Sugeridas, Tu navegador, Este proyecto, Fecha y hora, Zona horaria).

Verified: Switched user UI locale to Spanish → labels now read Fecha y hora, Zona horaria, etc.; the date picker renders mayo 2026 and Spanish weekday abbreviations (locale was already wired up correctly for react-datepicker).


lorem-ipsum — Fix lorem-ipsum blockquote rejected as invalid structured text

Bug: Generating dummy content into a Structured Text field failed with "Format not valid" and refused to save.

Root cause: The DAST Blockquote spec requires Blockquote.children to be Paragraph[], but the generator was emitting blockquote(text…) directly — text children, not a paragraph wrapper. The validator on the host side rejected the doc.

Fix: Wrap the blockquote body in a paragraph (t('blockquote', t('p', s(4)))), and tighten the t() helper to accept and flatten the deeply-nested arrays that sentences() returns. Added a toStructuredText(tree) overload, pulled in react-select as a direct dep, and corrected a few as const/icon types so the project type-checks again.

Verified: "Generate dummy text" now produces a structured-text doc with H1/H2/p/blockquote that renders without "Format not valid" and saves cleanly.


inverse-relationships — Make inverse-relationships configurable and zero-config-friendly

Bug: The sidebar panel needed a manually-pasted API token in instance parameters, was hard to configure per-field, and broke for editors who didn't have permission to mint tokens.

Root cause: Token + config were declared as instance parameters only; the plugin had no currentUserAccessToken permission, so it couldn't fall back to the editor's session token.

Fix: Promoted instance parameters to global parameters, made the explicit token field optional, and added the currentUserAccessToken permission to the plugin manifest. When no manual token is configured, the plugin uses the editor's own session token.

Verified (zero-config flow): Installed fresh against a model that has an inverse single_link relationship — the sidebar panel appears on records and lists the linking records without any configuration.


asset-optimization — Fix asset-optimization decimal MB threshold and preview View asset

Two preview-mode fixes bundled into one commit (both filed in the same Triage card).

Bug 1 — decimal MB thresholds break the asset listing. Setting "Large Asset Size Threshold" to anything fractional (e.g. 0.1 MB) returned INVALID_FILTER_FIELDS_PARAM ("Could not coerce value 0.10485760 to IntType") and the listing failed.

Root cause: The plugin multiplies MB → bytes (* 1024 * 1024), but passed the resulting float straight to the CMA size filter, which is an IntType.

Fix: Floor the byte threshold (Math.floor(largeAssetThresholdBytes)) before the request goes out.

Bug 2 — "View Asset" in the Optimized panel after a preview run opens the original. During dry-run the asset isn't actually replaced, so jumping into the existing asset URL just shows the unmodified original — making editors think the optimization didn't apply.

Root cause: AssetList always rendered the original asset URL regardless of preview vs. real run.

Fix: The dry-run pipeline now keeps the Imgix URL it generated (processAsset and assetToOptimizedAsset return optimizedUrl). AssetList accepts an isPreview prop; for optimized rows in preview mode the button label switches to "View optimized preview" and links to the Imgix URL. The "Media Area" button is also hidden in preview mode for optimized rows — there's nothing in the media library to jump to yet.

Verified:

  • Threshold set to 0.11 MB → preview pass succeeded with no INVALID_FILTER_FIELDS_PARAM.
  • Optimized panel button after a preview reads "View optimized preview" and opens an Imgix URL like ?auto=compress&q=85&fm=avif&cs=origin.
  • Media Area button hidden on optimized rows in preview mode; still present on skipped/failed rows and on real (non-preview) runs.

todo-list — Fix todo-list bundle referencing assets via absolute paths

Bug: Plugin failed to load when installed via plugins-cdn — the bundled index.html requested /assets/... from the CDN root, which 404'd.

Root cause: Vite's default base: '/' writes asset URLs with leading slashes. plugins-cdn serves each plugin from a versioned subpath, so absolute paths look outside the plugin's own bundle and miss.

Fix: Set base: './' in vite.config.ts so the built HTML uses relative paths.

Verified: dist/index.html now references ./assets/… (relative). End-to-end CDN loading depends on a separate host-side issue — see "Out of scope" below.


slug-redirects — Fix slug-redirects bundle referencing assets via absolute paths

Same root cause and fix as todo-list: base: './' in vite.config.ts. Also cleaned up a few pre-existing TS errors (Record<string, unknown> casts and an id-less guard) that were blocking npm run build.

Verified: dist/index.html now references ./assets/…. Same out-of-scope CDN caveat as todo-list.


project-wide-stage-viewer — Fix every project-wide-stage-viewer sidebar item highlighting at once

Bug: Clicking one stage entry in the sidebar highlighted every stage entry simultaneously instead of just the active one.

Root cause: All sidebar items were registered with the same pageId, so the host UI's "is current page?" check matched all of them at once.

Fix: Added src/utils/pageId.ts exporting a menuItemPageId(item) helper that returns pwsv-{workflowId}-{stageId}. main.tsx now uses that helper for both pointsTo.pageId and the renderPage match, so each sidebar entry has a unique pageId and the host highlights only the active one.

Verified: Configured 4 stages (Work in progress, Ready for review, Needs changes, Done). Clicking each one in turn → only the clicked entry highlights, and the highlight follows correctly when switching. After refresh the URL uses the new format …/pages/pwsv-{wfId}-{stageId} and the page header shows the right workflow + stage.


Out of scope (host-side issues to triage separately)

For todo-list and slug-redirects, the plugin-side fix above (relative asset paths) is necessary but not sufficient on its own to make the plugins load via the CDN today. Per Roger's RCAs, the plugins-cdn loader is also constructing URLs as plugins-cdn.datocms.com/<pkg>/<version>/... (path-style) when jsDelivr expects …/<pkg>@<version>/... (npm-style). End-to-end CDN verification will need a separate fix on the loader side — that code is not in this repo.

Test plan

  • CI lint + build for every changed plugin
  • Manually re-verify each plugin in claude-debugging once installed from the published versions

🤖 Generated with Claude Code

roger-datocms and others added 9 commits May 7, 2026 17:10
The plugin's `minFieldsToShow` config used a TextField that submitted the
value as a string, which then failed `typeof === 'number'` in
`isValidGlobalParams` and made `normalizeGlobalParams` reset to the
default of 5 on the next read. Coerce the value to an integer at submit
time, validate that it is at least 1, and render as a numeric input so
the browser also clamps below-1 input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The plugin's README advertised Spanish (es) as a supported language but
`UILABELS_BY_COUNTRY` in `src/i18n/uiLabels.ts` only contained 7 of the 8
languages, so Spanish editors saw the silent English fallback. Add the
missing `es` entry with translations for `suggested`, `browser`, `site`,
`dateTime`, and `timeZone`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The blockquote that the dummy-text generator produced contained inline
text children, but DAST requires `Blockquote.children` to be
`Paragraph[]`. The editor flashed "Format not valid" and the API rejected
the save. Wrap the blockquote body in a paragraph (`t('blockquote',
t('p', s(4)))`) so the value validates and saves.

Also clean up pre-existing TS errors that blocked the build: broaden
`t()` to accept and flatten the nested arrays `sentences()` produces,
add an extra `toStructuredText` overload, pull `react-select` in as a
direct dependency, and fix a couple of `as const`/icon type mismatches
in `main.tsx`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The inverse-relationships sidebar panel was never appearing because the
required `itemTypeApiKey`, `fieldApiKey`, `orderBy`, and `limit`
parameters were declared as `instance` parameters (per-field) but the
runtime read them from `ctx.plugin.attributes.parameters` (global), and
the plugin no longer registers any field extension to attach instance
params to. Promote them to `global` parameters so the built-in plugin
settings UI exposes them.

Drop the `required: true` from `datoCmsApiToken` and clarify in its hint
that it's optional (the SidebarPanel already falls back to
`ctx.currentUserAccessToken`). Declare
`"permissions": ["currentUserAccessToken"]` in the manifest so that
fallback actually works — without the permission the SDK leaves
`ctx.currentUserAccessToken` undefined and the auto-token flow silently
fails. Also drop the broken `/admin/access_tokens` link from the hint.

Add `base: './'` to `vite.config.ts` so the bundled `index.html`
references its assets relatively, matching the other plugins in the
repo and avoiding 404s when served under the per-plugin subdirectory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bundled preview-mode fixes:

* The MB→bytes conversion (`largeAssetThreshold * 1024 * 1024`) produced
  a float for any non-integer MB value (e.g. 0.1 MB → 104857.6 bytes),
  which the CMA `size` filter rejected with `INVALID_FILTER_FIELDS_PARAM`
  ("Could not coerce value 0.10485760 to IntType"). Floor the threshold
  before passing it into the filter.

* The "View Asset" button on each row of the Optimized Assets panel
  always opened the unmodified original. After a preview run the asset
  hasn't been rewritten yet, so editors thought the optimization didn't
  apply. Plumb the dry-run Imgix URL through to `OptimizedAsset`, pass
  `isPreview` down to `AssetList`, and switch the button to "View
  optimized preview" + open that URL when the panel is showing dry-run
  results. Hide the "Media Area" button in that case too — it would
  just take editors to the still-unmodified asset and reinforce the
  same misconception.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without `base: './'` in `vite.config.ts`, Vite emits the bundled
`index.html` with `<script src=\"/assets/index-*.js\">`. When the plugin
is served from a per-package subdirectory on plugins-cdn the absolute
path resolves to the CDN root and 404s, leaving the iframe stuck on
"Loading the plugin…". Set `base: './'` so the references become
relative (`./assets/…`) and resolve under the plugin's own directory,
matching the other plugins in the repo.

This fix is necessary but not sufficient on its own: the host plugin
loader is also constructing the CDN URL with `/<version>/` instead of
the `@<version>/` form jsDelivr expects, which 404s the index.html
itself. That part needs to be addressed in the host code, not here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same loading issue as the todo-list plugin: without `base: './'` Vite
emits absolute `/assets/…` references in the bundled `index.html`, which
404 when the plugin is served from a per-package subdirectory on
plugins-cdn. Set `base: './'` so references become relative.

Also tighten a few `as object` casts in `onBeforeItemUpsert` and
`onBeforeItemsPublish` (use `Record<string, unknown>` instead) and guard
against the `id`-less create case in the upsert hook so the project
type-checks again — these errors had been masking the build.

The host plugin loader's `/version` vs `@version` URL bug still needs to
be fixed separately for the plugin to actually load on plugins-cdn.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Configured stage menu items used pageIds like `wf:X__st:Y`. The embedded
colons collided with the host's URL/route matching, causing every item
the plugin contributed to highlight simultaneously whichever stage was
active. Replace the format with a URL-safe `pwsv-{workflowId}-{stageId}`
derived from the menu item's structural fields, in a small `pageId`
helper used by both `contentAreaSidebarItems` and `renderPage`. This
keeps existing user configs working — `item.id` is no longer read back,
so stored items with the legacy format still resolve via their
`workflowId` + `stageId`.

Verified live: with four stage items configured, only the active stage
is highlighted in the sidebar and clicking another stage moves the
highlight cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@roger-datocms roger-datocms changed the title Fix 9 plugin bugs reported in Triage [Needs manual review, do not merge] Misc plugin bugs May 8, 2026
@roger-datocms roger-datocms marked this pull request as draft May 8, 2026 04:10
@roger-datocms roger-datocms self-assigned this May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant