Skip to content

[6.x] Move the control panel JavaScript to modules and off jQuery and Garnish - #20378

Merged
brandonkelly merged 115 commits into
6.xfrom
chore/cp-js-modules
Nov 5, 2026
Merged

brandonkelly merged 115 commits into
6.xfrom
chore/cp-js-modules

Conversation

@brianjhanson

@brianjhanson brianjhanson commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

This PR combines three earlier pull requests (#20276, #20320, #20359) into one branch, rebased onto 6.x. It's the same work, but reviewers only have to look at one thing, and nothing has to be kept in sync between branches.

What it does, in three steps

  1. Move old control panel code to modern modules. The old jQuery-based JavaScript for the control panel (about 24 files, 20,000+ lines) is gone. Each file was either rewritten as a TypeScript module, copied unchanged into a compatibility layer (the "adapter") so plugins keep working, or deleted. The old Craft.* functions now exist only in that adapter, for backwards compatibility. Core code now calls simpler functions instead, like showError(message) or t('Save') in place of Craft.cp.displayError(message) or Craft.t('app', 'Save').

  2. Stop loading jQuery and Garnish in core. The control panel no longer loads these two older libraries on its own. Core now uses plain browser features and custom elements (for things like element select inputs, menus and tabs). The adapter still loads jQuery and Garnish, so plugins that depend on them continue to work. Plugins that extend the old element select classes keep working too.

  3. Share control panel modules with plugins. Plugins can now import vue, @craftcms/cp and @craftcms/garnish from the control panel itself, instead of bundling their own copies or relying on old global objects. @craftcms/cp gains helpers for translation, HTTP requests, URLs, config, slideouts, element editors, the image editor, uploads and more.

Bugs fixed along the way

  • Screen reader announcements were silent on some pages.
  • Announcements weren't heard inside slideouts.
  • A toggle showed its targets when switched off (it read the switch's state one step late).
  • Date and time form fields lost their value, dropped the input name, and added an extra time input.

Testing

  • Type checks pass, and the frontend tests pass (2,473 passed, 1 skipped).
  • The control panel tests pass (614). The full core suite had 15 failures, but all of them also fail on plain 6.x, so they're unrelated to this change.
  • 10 of 10 core control panel pages loaded in a real browser with no errors.
  • Craft Commerce 6.x, installed as an outside plugin, rendered 15 of its 16 screens. The one failure is a bug in Commerce itself that happens without this branch too.
  • New regression tests cover the asset loading changes, the new menu element and the new shared module.

Related notes

  • This replaces the three original PRs.
  • Two CKEditor issues (#643 and #641) will use the new exports.
  • One unrelated change (HtmlSanitizerManager::config()) is included as a cherry-pick but is tracked separately in [6.x] Add HtmlSanitizerManager::config() #20328.

brianjhanson and others added 30 commits November 4, 2026 17:14
CraftCopyAttribute, CraftGlobalSidebar, and CraftTooltip were never
imported into the legacy cp bundle; @craftcms/ui and <cp-global-sidebar>
replaced them. Craft.ImageUpload's only consumer was the rebrand script,
whose asset bundle no longer registered it, so both go along with the
generalsettings webpack package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each module's index.ts now only defines its element and exports. Globals
core still calls move to globals.ts, which index.ts imports. Globals only
plugins call move to compat.ts, which only the yii2-adapter's cp-compat.ts
imports. Moving a global to the adapter is now a one-line move between
the two files.

Adapter-only from this commit: Craft.AssetImageEditor,
Craft.SortableCheckboxSelect, Craft.GroupedEntryTypeManager,
Craft.RecoveryCodesSetup and Craft.TagSelectInput, which core no longer
calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Uploads, highlighter, AuthChallengeForm, createElementSelectorModal and
the element selector controller registry are now exports of the
@craftcms/cp import-map module rather than Craft.* globals, so control
panel plugin code imports them. The Yii2 adapter keeps
Craft.createElementSelectorModal and Craft.highlighter for Craft 5
plugins. Pages outside the control panel still get Craft.Uploads from the
standalone uploads entry, since they have no import map.

SlideoutPanel no longer assumes window.Craft exists; it was relying on a
module that happened to create it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Craft.commandPalette is new in 6.0, so it moves to a CommandPalette export
of the @craftcms/cp import-map module instead of becoming a global the
adapter has to carry. Craft.charts dates from Craft 5 and core's legacy
New Users widget still calls it, so it gets the globals.ts split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class now lives in resources/js/common/utils/promise-queue.ts on
@craftcms/garnish Base, without jQuery. The legacy bundle imports it and
still assigns Craft.Queue and Craft.queue where Queue.js did, since
Craft.cp uses the queue while that bundle evaluates.

A failed job no longer leaves an unhandled rejection behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Craft.AssetsFieldSettings and Craft.ElementFieldSettings only serve the
legacy relation field settings templates, which only the adapter's
LegacyRelationFieldSettings renders; core builds those settings with
settingsUi(). They now ship unchanged in the cpcompat bundle, loaded
CP-wide by CpCompatAsset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trim/startsWith/remove* family, escapeRegex, escapeChars,
upper/lowercaseFirst, uuid, randomString, formatInputId,
namespaceInputName and namespaceId now live in
resources/js/common/craft/. Craft.js assigns them, so Craft.trim() and
the rest keep working for plugins, and the modern modules that reached
through the global import them instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
findDeltaData, findModifiedDeltaNames, groupParams and the private
_groupParamsByDeltaNames now live in resources/js/common/craft/delta.ts,
with a jQuery-compatible param() serializer in params.ts that's tested
against $.param itself. Craft.js assigns them for plugins, and the CP
modal, CP screen slideout and inline editing import them directly.

The inline editing test now runs the real delta logic instead of a stub
that returned the expected result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getUrl, getCpUrl, getSiteUrl and getActionUrl now live in
resources/js/common/craft/url.ts, read their base URLs from the CP config
at call time, and encode params with the jQuery-compatible param(). The
script-name and path-param branches are gone: Craft 6 never sends
pathParam or scriptName, so they couldn't change the result. Output was
checked against the legacy implementation.

Craft.js assigns them for plugins, and the modern modules that called
Craft.getCpUrl()/getActionUrl() import them. Their tests now set the base
URLs rather than stubbing the helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Asset fields drew their upload progress with the legacy Craft.ProgressBar,
whose stylesheet Craft 6 never loads, so the bar rendered unstyled and
the shade over the field never appeared. They now use the
<craft-progress-bar> element, which exposes a labelled progressbar to
assistive tech, with a small stylesheet for the overlay. Progress is
still announced every few seconds while an upload runs.

Craft.ProgressBar stays in the legacy bundle for the legacy asset index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
element-selector-modals/body returned the legacy ElementIndexHtml markup
alongside the index props, but every modal now renders the Vue index
from props, so the markup was rendered on each open and thrown away.
The response is now just props, and the request params and status
narrowing that only fed the markup are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The elementIndex() Twig function and ElementIndexHtml rendered the
legacy jQuery element index, and nothing in core used them. They now
live in the adapter, next to craft\helpers\Cp::elementIndexHtml() and
the nested element manager that call them.

_layouts/elementindex only drew the legacy index when the shell wasn't
collecting the screen, which nothing triggers any more, so the layout
now always declares the Vue index. Its empty initJs block stays for
child templates that extend it. craft.cp.screenCollecting() lost its
only caller and is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Craft.BaseElementIndex, its views, the per-type indexes, the structure
view and ProgressBar now ship in the adapter's cpcompat bundle as one
element-index entry, along with Craft.createElementIndex() and
Craft.registerElementIndexClass(). Core renders every element index with
the Vue index, so none of it was used without the adapter. The legacy cp
bundle drops from 406 KB to 265 KB.

The globals only those classes used (ElementTableSorter, IntervalManager,
PromptHandler and openCustomizeSourcesModal) move to compat.ts, and the
Vue index's Customize Sources action imports the modal directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the legacy dashboard, which only the adapter loads, and plugins
use the legacy admin table, so it now ships in the cpcompat bundle.
The editable table imports DataTableSorter instead of reaching for
Craft.DataTableSorter, leaving that global to the adapter as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Craft.t() assigned 'app' to its category argument instead of passing the
caller's category through, so plugin translations were never found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 151 Craft.t('app', …) calls in resources/js and the adapter's
nested element manager now use @craftcms/ui's t(). Each control panel
entry first seeds its translations from window.Craft, because custom
elements already on the page upgrade, and translate their labels, while
their modules are still loading, before the CP bootstrap runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core modules called Craft.sendActionRequest(), which only exists because
the legacy bundle defines it. They now use actionClient, which sends the
same registered-asset headers and resolves action paths, root-relative
URLs and absolute URLs the same way.

The CP modal and CP screen slideout still accept plugin-supplied axios
options, so their screen loads go through sendLegacyRequest() with
actionClient, and their form submissions post to the CP URL as before.
Craft.sendActionRequest() stays for plugins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core modules now import appendHeadHtml() and appendBodyHtml() from
@craftcms/ui instead of calling the Craft.* versions, so head HTML they
load lands above the control panel stylesheet like the Vue screens'
does. The CP modal, which didn't wait between the two, now appends the
body HTML once the head's scripts have loaded.

Craft.appendHeadHtml() and appendBodyHtml() keep their queue and still
append to the end of <head>, but use the same implementation. Their
duplicate check reads the live DOM instead of a list captured on first
use, so they no longer re-run a script something else added since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
addActionsToChip, setElementAttributes, escapeHtml and
hasMousePointerEvents now live in resources/js/common/craft/, without
jQuery except for the disclosure menu fallback that old chip markup
still needs. Core modules import them, and Craft.js assigns them so the
Craft.* names keep working for plugins.

setElementAttributes keeps the legacy behavior plugins may depend on: it
accepts a selector or a collection, applies to every element in it, and
adds px to bare numbers in a style object as jQuery's css() did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core modules now read text direction, the page trigger, element type
names and handle casing from ConfigService, and import cookie, focus,
broadcast and elevated-session helpers directly. The Craft.* members
stay in place for plugins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@craftcms/ui now exports a single create(tag, props, ...children) that
types props from HTMLElementTagNameMap, so every component gets checked
properties without a factory of its own. The per-component factories
only translated the legacy Craft.ui config keys, so they move next to the
Craft.ui patch in resources/js/modules/ui/legacy, which UI.js imports.

createCopyTextPrompt opens a dialog rather than building a control, so it
stays in @craftcms/ui as showCopyTextPrompt(). Core's button and copy
prompt callers now use create() and showCopyTextPrompt().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The field error helpers (addErrorsToField, clearErrorsFromField, the error
summary helpers and findTabAnchorForField) now live in
common/craft/field-errors.ts without jQuery, and Craft.ui's methods call
them. Core fetches icon SVGs through iconSvg(), which requests each name
once, and builds the matrix default-columns select with create().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core called Craft.cp.displayNotice(), displaySuccess(), displayError() and
displayNotification() in 111 places. They now import showNotice(),
showSuccess() and showError() from the messages module, which every
message already went through for deduping. showMessage() returns the
notification so callers can close it, and the functions are exported
from @craftcms/cp for plugins. Craft.cp's methods are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's seven Craft.cp.runQueue() calls now call
QueueService.getInstance().runQueue(), which Craft.cp.runQueue() already
delegated to. Also drops test stubs for Craft.cp.display*() that nothing
calls any more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's Craft.cp.elementThumbLoader.load() calls use a shared loader from
the element-thumb-loader module instead. Craft.cp.elementThumbLoader is
unchanged for plugins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
modules/elements/clipboard owns copied elements: localStorage, the
cross-tab broadcast, listeners and paste. Core and the @craftcms/cp
exports call it directly; Craft.cp's clipboard methods delegate to it.
The "copied" card is still the legacy ElementCopyNotification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
common/announcer finds the live region when a message is announced: the
top modal layer's, else the page's. This replaces CP.js's layer
tracking. useAnnouncer() and Craft.cp.announce() both go through it, so
Vue code inside a modal is heard and Craft.cp.announce() works on
Inertia pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lements

Core's 18 Craft.initUiElements() calls now go through initLegacyUi(), which
does nothing when the legacy bundle isn't loaded. A MutationObserver binds a
FieldToggle to each .fieldtoggle as it's added, so inserted markup no longer
needs the sweep for toggles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
<craft-switch> updates aria-checked after its change event, so FieldToggle
read the old state and showed a switch's targets when it was turned off.
Now that every .fieldtoggle is bound, this affected switches on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brianjhanson and others added 7 commits November 4, 2026 22:06
Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>

# Conflicts:
#	resources/js/modules/auth/components/login/login-challenge.browser.test.ts
The deploy jobs restore build-assets' cache instead of installing, but
`vp run` still checks the lockfile against the registry. Without the token
that check fails for the Font Awesome packages.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The recovery-code action now returns an array while the Password screen still expects a URL string, causing an array-to-string error and leaving the download-confirmation contract inconsistent.

2 open issues
What changed in this PR

Consolidates the 6.x control-panel migration to TypeScript modules and custom elements, moves jQuery/Garnish compatibility into the Yii2 adapter, and exposes shared modules for plugins.

Changes:

  • Separates core module exports from legacy compatibility globals and dependencies.
  • Migrates templates, controls, notifications, and bootstrap hooks to shared helpers and native events.
  • Updates plugin-facing exports, build configuration, assets, documentation, and regression tests.

This pull request requires changes.

The unresolved findings are:

  • Critical · 1 vote: The recovery-code action descriptor breaks the Password screen’s existing view-model/client contract and needs coordinated download-confirmation handling.
  • Moderate · 2 votes: Image-preview resizing still references the removed focal-point global, leaving the marker incorrectly positioned.
  • Moderate · 1 vote: Authentication-method removal bypasses the CSRF-aware action client and can fail with HTTP 419.
  • Moderate · 1 vote: Flashed broadcasts can fire before the asynchronously initialized relay exists, leaving other tabs stale.
  • Nit · 2 votes: The published Factory documentation still imports removed builders and needs migration guidance.
  • Nit · 1 vote: The copy-text prompt documentation still uses the removed factory export and old function name.

Project tests, builds, and interactive accessibility checks were not independently run during review.

File Description
yii2-adapter/​tests-laravel/​Field/​LegacyRelationFieldInputTest.php Expects custom-element relation inputs.
yii2-adapter/​src/​Mixins/​NestedElementManagerMixin.php Uses the adapter index renderer.
yii2-adapter/​src/​Cp/​Html/​LegacyMenuHtml.php Preserves legacy menu rendering.
yii2-adapter/​resources/​templates/​_includes/​forms/​volume.twig Resolves volume URLs server-side.
yii2-adapter/​resources/​templates/​_includes/​forms/​customSelect.twig Guards legacy menu initialization.
yii2-adapter/​resources/​templates/​_components/​fieldtypes/​Assets/​input.twig Delegates initialization to custom elements.
yii2-adapter/​resources/​js/​slide-picker-compat.browser.test.ts Loads relocated compatibility UI.
yii2-adapter/​resources/​js/​matrix/​matrix-input.ts Uses the relocated button builder.
yii2-adapter/​resources/​js/​matrix/​matrix-entry.ts Imports the CP URL helper.
yii2-adapter/​resources/​js/​element-select-input/​index.ts Registers legacy input classes.
yii2-adapter/​resources/​js/​cp-compat.ts Collects adapter compatibility registrations.
yii2-adapter/​legacy/​web/​twig/​Extension.php Exposes adapter element-index rendering.
yii2-adapter/​legacy/​web/​assets/​focalpoint/​FocalPointAsset.php Makes the obsolete bundle inert.
yii2-adapter/​legacy/​web/​assets/​cp/​CpAsset.php Loads adapter Craft JavaScript.
yii2-adapter/​legacy/​base/​WidgetTrait.php References the relocated widget image.
vite.config.js Adds the shared Garnish entry.
tests/​Unit/​Twig/​Templates/​CheckboxSelectTemplateTest.php Updates checkbox-select markup expectations.
tests/​Unit/​Cp/​Components/​CheckboxSelectTest.php Drops the legacy host-class assertion.
tests/​Unit/​Cp/​Components/​ActionMenuTest.php Checks elevated-session action metadata.
tests/​Feature/​Image/​RasterTest.php Uses a dedicated font fixture.
tests/​Feature/​Http/​Responses/​CpScreenSlideoutTest.php Updates the initial asset expectation.
tests/​Feature/​Http/​Controllers/​PluginStore/​InstallControllerTest.php Expects declarative updater initialization.
tests/​Feature/​Http/​Controllers/​Elements/​ElementSelectorModalControllerTest.php Expects props without legacy HTML.
tests/​Feature/​Cp/​PreviewHtmlTest.php Checks declarative preview controls.
src/​View/​LegacyAssets/​UpdaterAsset.php Stops loading legacy updater JavaScript.
src/​View/​LegacyAssets/​MoneyAsset.php Declares its Garnish dependency.
src/​View/​LegacyAssets/​InputmaskAsset.php Declares its jQuery dependency.
src/​View/​LegacyAssets/​GeneralSettingsAsset.php Removes obsolete commented registrations.
src/​View/​LegacyAssets/​FocalPointAsset.php Deletes the obsolete asset bundle.
src/​View/​LegacyAssets/​CpAsset.php Removes core legacy dependencies.
src/​Twig/​Variables/​Cp.php Removes the fragment-collection accessor.
src/​Http/​ViewModels/​ContentIndexViewModel.php Removes an outdated documentation reference.
src/​Http/​Mixins/​SessionMixin.php Dispatches native broadcast events.
src/​Http/​Controllers/​PluginsController.php Removes legacy plugin asset registration.
src/​Http/​Controllers/​Elements/​EditElementController.php Removes the obsolete Inertia editor branch.
src/​Http/​Controllers/​BaseUpdaterController.php Supplies declarative updater state.
src/​FieldLayout/​LayoutElements/​Assets/​AltField.php Removes the legacy textarea class.
src/​Field/​Number.php Filters numeric input without jQuery.
src/​Element/​ElementIndexState.php Documents adapter ownership of legacy indexes.
src/​Cp/​Html/​PreviewHtml.php Emits declarative preview controls.
src/​Cp/​Cp.php Publishes the shared Garnish module.
src/​Cp/​Components/​CheckboxSelect.php Removes the legacy host class.
src/​Auth/​Methods/​RecoveryCodes.php Defines an elevated POST download descriptor.
scripts/​ensure-built.mjs Updates the legacy-build sentinel.
resources/​translations/​en/​app.php Adds the upload-progress label.
resources/​templates/​users/​_auth-methods.twig Uses modern action menus.
resources/​templates/​assets/​_previews/​image.twig Declares focal-point initialization attributes.
resources/​templates/​_special/​updater.twig Embeds updater configuration.
resources/​templates/​_special/​setup-2fa.twig Replaces jQuery DOM operations.
resources/​templates/​_layouts/​cp.twig Guards adapter-only update checks.
resources/​templates/​_layouts/​components/​crumbs.twig Passes the menu invoker explicitly.
resources/​templates/​_layouts/​components/​alerts.twig Guards legacy alert loading.
resources/​templates/​_layouts/​base.twig Resolves relocated icon assets.
resources/​templates/​_components/​fieldtypes/​PlainText/​input.twig Removes the legacy text class.
resources/​templates/​_components/​auth/​methods/​TOTP/​setup.twig Dispatches native message events.
resources/​js/​uploads.ts Documents standalone upload usage.
resources/​js/​pages/​users/​Passkeys.vue Uses module error reporting.
resources/​js/​pages/​assets/​Index.vue Uses the configured pagination helper.
resources/​js/​pages/​assets/​AssetUploadButton.test.ts Mocks the module queue dependency.
resources/​js/​modules/​uploader/​uploader.ts Uses module error reporting.
resources/​js/​modules/​uploader/​uploader.test.ts Removes legacy message stubs.
resources/​js/​modules/​uploader/​upload-queue.ts Reports errors through the message module.
resources/​js/​modules/​uploader/​upload-notification.ts Uses native notification controls.
resources/​js/​modules/​uploader/​picker.test.ts Mocks module error reporting.
resources/​js/​modules/​uploader/​index.ts Separates uploader global registration.
resources/​js/​modules/​uploader/​globals.ts Registers uploader compatibility globals.
resources/​js/​modules/​uploader/​asset-upload-queue.ts Calls queue and message modules.
resources/​js/​modules/​ui/​useUiForm.ts Uses module error reporting.
resources/​js/​modules/​ui/​useInertiaTable.ts Reads the configured pagination key.
resources/​js/​modules/​ui/​UiModal.vue Uses module notice reporting.
resources/​js/​modules/​ui/​UiModal.browser.test.ts Removes legacy message stubs.
resources/​js/​modules/​ui/​README.md Documents adapter compatibility loading.
resources/​js/​modules/​ui/​nested-elements/​useNestedElementsQuery.ts Uses the pagination helper.
resources/​js/​modules/​ui/​nested-elements/​useNestedElementsQuery.test.ts Removes legacy pagination configuration.
resources/​js/​modules/​ui/​nested-elements/​NestedElements.test.ts Removes the legacy notice stub.
resources/​js/​modules/​ui/​nested-elements/​nested-elements.ts Uses configured element names.
resources/​js/​modules/​ui/​nested-elements/​nested-elements-control-host.ts Serializes forms without jQuery.
resources/​js/​modules/​ui/​nested-elements/​nested-elements-control-host.test.ts Simplifies compatibility stubs.
resources/​js/​modules/​ui/​legacy/​switch.ts Corrects the component type import.
resources/​js/​modules/​ui/​legacy/​slide-picker.ts Corrects the component type import.
resources/​js/​modules/​ui/​legacy/​input-password.ts Uses relocated builder imports.
resources/​js/​modules/​ui/​legacy/​input-color.ts Uses relocated builder imports.
resources/​js/​modules/​ui/​legacy/​index.ts Exports relocated legacy builders.
resources/​js/​modules/​ui/​layout-component-settings-ui-host.ts Calls the shared action client.
resources/​js/​modules/​ui/​ElementSitesControl.vue Uses module error reporting.
resources/​js/​modules/​ui/​element-select/​usePasteableElements.ts Uses clipboard and naming modules.
resources/​js/​modules/​ui/​dashboard-widget-settings-ui-host.ts Calls the shared action client.
resources/​js/​modules/​ui/​compat.test.ts Tests the explicit compatibility entry.
resources/​js/​modules/​tabs/​index.ts Removes implicit global registration.
resources/​js/​modules/​tabs/​compat.ts Registers adapter tab compatibility.
resources/​js/​modules/​sortable-checkbox-select/​compat.ts Registers legacy sorting compatibility.
resources/​js/​modules/​slideout/​globals.ts Registers the compatified slideout constructor.
resources/​js/​modules/​slideout/​compat.ts Registers adapter slideout classes.
resources/​js/​modules/​queue/​index.ts Separates queue global registration.
resources/​js/​modules/​queue/​globals.ts Registers the queue compatibility global.
resources/​js/​modules/​prompt-handler/​index.ts Removes implicit global registration.
resources/​js/​modules/​prompt-handler/​compat.ts Registers adapter prompt compatibility.
resources/​js/​modules/​preview-file-modal/​index.ts Separates preview global registration.
resources/​js/​modules/​preview-file-modal/​globals.ts Registers preview compatibility.
resources/​js/​modules/​notifications/​store.ts Uses module error reporting.
resources/​js/​modules/​notifications/​store.test.ts Checks module error reporting.
resources/​js/​modules/​navigation/​components/​cp-queue-indicator.ts Imports the CP URL helper.
resources/​js/​modules/​navigation/​components/​cp-global-sidebar.ts Imports cookie helpers.
resources/​js/​modules/​messages/​useMessages.ts Reuses shared message functions.
resources/​js/​modules/​link-field/​link-field.ts Tracks controllers without jQuery.
resources/​js/​modules/​link-field/​index.ts Separates link-field global registration.
resources/​js/​modules/​link-field/​globals.ts Registers link-field compatibility globals.
resources/​js/​modules/​interval-manager/​interval-manager.ts Clarifies compatibility ownership in comments.
resources/​js/​modules/​interval-manager/​index.ts Removes implicit global registration.
resources/​js/​modules/​interval-manager/​compat.ts Registers adapter interval compatibility.
resources/​js/​modules/​input-generators/​index.ts Separates generator global registration.
resources/​js/​modules/​input-generators/​handle-generator.ts Reads the shared configuration service.
resources/​js/​modules/​input-generators/​globals.ts Registers generator compatibility globals.
resources/​js/​modules/​image-editor/​index.ts Separates image-editor global registration.
resources/​js/​modules/​image-editor/​globals.ts Registers the image-editor opener.
resources/​js/​modules/​image-editor/​compat.ts Registers the legacy editor name.
resources/​js/​modules/​highlight/​compat.ts Keeps adapter registration without an export.
resources/​js/​modules/​grouped-entry-type-manager/​README.md Updates documented legacy dependencies.
resources/​js/​modules/​grouped-entry-type-manager/​compat.ts Registers the legacy manager API.
resources/​js/​modules/​grid/​index.ts Separates grid global registration.
resources/​js/​modules/​grid/​grid.test.ts Checks native item management.
resources/​js/​modules/​grid/​globals.ts Registers grid compatibility.
resources/​js/​modules/​form-observer/​index.ts Separates observer global registration.
resources/​js/​modules/​form-observer/​globals.ts Registers observer compatibility.
resources/​js/​modules/​focal-point/​focal.scss Supplies focal-point marker styles.
resources/​js/​modules/​field-toggle/​index.ts Separates toggle global registration.
resources/​js/​modules/​field-toggle/​globals.ts Registers toggle compatibility.
resources/​js/​modules/​field-toggle/​field-toggle.test.ts Checks immediate switch-state reading.
resources/​js/​modules/​field-layout-designer/​README.md Updates documented legacy dependencies.
resources/​js/​modules/​field-layout-designer/​LayoutComponentSettings.vue Calls the shared action client.
resources/​js/​modules/​field-layout-designer/​index.ts Separates designer global registration.
resources/​js/​modules/​field-layout-designer/​globals.ts Registers designer compatibility classes.
resources/​js/​modules/​field-layout-designer/​field-layout-designer.ce.ts Calls the scoped legacy initializer.
resources/​js/​modules/​field-layout-designer/​drags.test.ts Removes the jQuery test dependency.
resources/​js/​modules/​entry-mover/​index.ts Separates mover global registration.
resources/​js/​modules/​entry-mover/​globals.ts Registers entry-mover compatibility.
resources/​js/​modules/​elements/​live-preview/​useLivePreview.ts Removes obsolete global typings.
resources/​js/​modules/​elements/​live-preview/​PreviewLayout.vue Imports iframe-resizer directly.
resources/​js/​modules/​elements/​index/​save-inline-elements.ts Uses module error reporting.
resources/​js/​modules/​elements/​index/​element-actions.ts Uses module message reporting.
resources/​js/​modules/​elements/​index/​copy-elements.ts Calls the shared clipboard module.
resources/​js/​modules/​elements/​index/​composables/​useElementIndexVisits.ts Uses configured pagination.
resources/​js/​modules/​elements/​index/​composables/​useElementIndexSort.test.ts Removes legacy pagination configuration.
resources/​js/​modules/​elements/​index/​composables/​useCustomizeSources.ts Imports the modal opener directly.
resources/​js/​modules/​elements/​index/​composables/​useCustomizeSources.test.ts Mocks the imported modal opener.
resources/​js/​modules/​elements/​index/​components/​ElementIndexPage.filters.test.ts Initializes pagination configuration explicitly.
resources/​js/​modules/​elements/​index/​components/​ElementIndex.test.ts Removes legacy pagination configuration.
resources/​js/​modules/​elements/​index/​components/​ElementBulkActionsBar.vue Uses module error reporting.
resources/​js/​modules/​elements/​html-nested-owner.ts Isolates optional legacy editor lookup.
resources/​js/​modules/​elements/​element-action-events.ts Dispatches server-rendered element actions.
resources/​js/​modules/​elements/​composables/​useBulkActionSelection.ts Updates clipboard documentation.
resources/​js/​modules/​element-thumb-loader/​index.ts Re-exports relocated thumbnail helpers.
resources/​js/​modules/​element-thumb-loader/​element-thumb-loader.test.ts Imports relocated compatibility CP code.
resources/​js/​modules/​element-table-sorter/​compat.ts Keeps adapter registration without an export.
resources/​js/​modules/​element-selector-modal/​volume-folder-selector-modal.test.ts Uses props-only response fixtures.
resources/​js/​modules/​element-selector-modal/​useModalElementIndex.test.ts Removes legacy pagination configuration.
resources/​js/​modules/​element-selector-modal/​useElementSelectorController.test.ts Uses a props-only response fixture.
resources/​js/​modules/​element-selector-modal/​index.test.ts Checks adapter-only factory registration.
resources/​js/​modules/​element-selector-modal/​globals.ts Registers folder-selector compatibility.
resources/​js/​modules/​element-selector-modal/​ElementSelectorModal.test.ts Uses props-only response fixtures.
resources/​js/​modules/​element-selector-modal/​compat.ts Registers the adapter modal factory.
resources/​js/​modules/​element-select-input/​README.md Documents core/adapter input separation.
resources/​js/​modules/​element-select-input/​index.ts Keeps core custom-element registration.
resources/​js/​modules/​element-deletion-manager/​index.ts Separates deletion global registration.
resources/​js/​modules/​element-deletion-manager/​globals.ts Registers deletion compatibility.
resources/​js/​modules/​element-action-trigger/​compat.ts Keeps adapter registration without an export.
resources/​js/​modules/​editable-table/​index.ts Removes implicit compatibility registration.
resources/​js/​modules/​editable-table/​compat.ts Registers compatified adapter table classes.
resources/​js/​modules/​data-table-sorter/​index.ts Removes the obsolete global entry.
resources/​js/​modules/​data-table-sorter/​data-table-sorter.ts Clarifies module consumers in comments.
resources/​js/​modules/​data-table-sorter/​compat.ts Registers adapter sorter compatibility.
resources/​js/​modules/​dashboard/​useDashboard.ts Refreshes the grid through native elements.
resources/​js/​modules/​dashboard/​NewUsers.vue Passes a native chart container.
resources/​js/​modules/​customize-sources/​index.ts Removes implicit global registration.
resources/​js/​modules/​customize-sources/​compat.ts Registers the adapter modal opener.
resources/​js/​modules/​cp-modal/​index.ts Separates modal global registration.
resources/​js/​modules/​cp-modal/​globals.ts Registers modal compatibility.
resources/​js/​modules/​component-select/​README.md Updates documented legacy dependencies.
resources/​js/​modules/​command-palette/​run.ts Uses module message reporting.
resources/​js/​modules/​command-palette/​registry.ts Updates the API example.
resources/​js/​modules/​command-palette/​index.ts Removes the command-palette global API.
resources/​js/​modules/​command-palette/​components/​UiFormView.test.ts Removes legacy message stubs.
resources/​js/​modules/​command-palette/​components/​CommandPalette.vue Uses module message reporting.
resources/​js/​modules/​chart/​index.ts Separates chart global registration.
resources/​js/​modules/​chart/​globals.ts Registers chart compatibility APIs.
resources/​js/​modules/​auth/​elevated-session/​index.test.ts Checks the action elevation event bridge.
resources/​js/​modules/​auth/​components/​auth-challenge-form.ts Documents shared-module subclassing.
resources/​js/​modules/​auth-method-setup/​index.ts Separates setup global registration.
resources/​js/​modules/​auth-method-setup/​globals.ts Registers authentication setup globals.
resources/​js/​modules/​auth-method-setup/​compat.ts Registers recovery-code compatibility.
resources/​js/​modules/​assets/​replaceAssetFile.ts Uses broadcast and message modules.
resources/​js/​modules/​assets/​composables/​useAssetIndexItemBehavior.ts Uses configured pagination.
resources/​js/​modules/​assets/​composables/​useAssetIndexItemBehavior.test.ts Removes legacy pagination configuration.
resources/​js/​modules/​assets/​composables/​useAssetFolderActions.test.ts Removes legacy message stubs.
resources/​js/​modules/​asset-select-input/​index.ts Removes core input global registration.
resources/​js/​modules/​asset-select-input/​asset-select-input.css Styles the upload progress overlay.
resources/​js/​modules/​asset-mover/​index.ts Separates mover global registration.
resources/​js/​modules/​asset-mover/​globals.ts Registers asset-mover compatibility.
resources/​js/​modules/​ai/​skills/​AiSkills.vue Uses module error reporting.
resources/​js/​modules/​ai/​assistant/​AssistantConversationMenu.vue Uses module error reporting.
resources/​js/​modules/​admin-table/​legacy-admin-table.test.ts Removes the unused request stub.
resources/​js/​garnish.ts Publishes shared Garnish exports.
resources/​js/​common/​web-components/​ControllerElement.ts Preserves controllers during DOM moves.
resources/​js/​common/​utils/​dom.ts Adds native content-box measurement.
resources/​js/​common/​types/​iframe-resizer.d.ts Declares the imported resizer API.
resources/​js/​common/​table/​useServerSort.ts Uses configured pagination.
resources/​js/​common/​table/​useServerPagination.ts Uses configured pagination.
resources/​js/​common/​table/​server-query.test.ts Removes legacy pagination configuration.
resources/​js/​common/​table/​craftTable.test.ts Removes legacy pagination configuration.
resources/​js/​common/​slideouts/​SlideoutPanel.vue Guards optional Craft configuration.
resources/​js/​common/​slideouts/​index.ts Leaves editor globals to the adapter.
resources/​js/​common/​craft/​pointer.ts Adds native pointer detection.
resources/​js/​common/​craft/​namespace.ts Adds input namespacing helpers.
resources/​js/​common/​craft/​legacy-ui.ts Isolates optional legacy initialization.
resources/​js/​common/​craft/​icon-svg.ts Fetches and caches icon SVGs.
resources/​js/​common/​craft/​element-types.ts Reads configured element names.
resources/​js/​common/​craft/​direction.ts Adds text-direction helpers.
resources/​js/​common/​composables/​useAsyncIcon.ts Uses the module icon loader.
resources/​js/​common/​components/​HtmlFragmentRenderer.vue Uses the scoped legacy initializer.
resources/​js/​bootstrap/​seed-translations.ts Seeds translations before component upgrades.
resources/​js/​bootstrap/​seed-translations.test.ts Checks early translation availability.
resources/​js/​bootstrap/​cp.ts Initializes migrated core services.
resources/​css/​fld.css Updates stylesheet provenance comments.
packages/​craftcms-ui/​vitest.config.ts Removes the factory test project.
packages/​craftcms-ui/​src/​utilities/​copy-text-prompt.ts Renames the prompt-opening utility.
packages/​craftcms-ui/​src/​index.ts Exports the copy-text prompt utility.
packages/​craftcms-ui/​src/​factory/​index.ts Removes the public factory entry.
packages/​craftcms-ui/​src/​core/​element-selector/​volume-folder-selector-controller.test.ts Uses a props-only response fixture.
packages/​craftcms-ui/​src/​core/​element-selector/​types.ts Removes the legacy response HTML contract.
packages/​craftcms-ui/​src/​core/​element-selector/​element-selector-controller.test.ts Uses a props-only response fixture.
packages/​craftcms-ui/​src/​core/​element-selector/​asset-selector-controller.test.ts Uses a props-only response fixture.
packages/​craftcms-ui/​src/​components/​thumbnail-loader/​README.md Removes an obsolete consumer reference.
packages/​craftcms-ui/​src/​components/​switch/​switch.ts Updates toggle-ownership comments.
packages/​craftcms-ui/​src/​components/​element-selector-modal/​element-selector-modal.test.ts Uses a props-only modal fixture.
packages/​craftcms-ui/​src/​components/​element-selector-modal/​element-selector-modal.browser.test.ts Uses a props-only browser fixture.
packages/​craftcms-ui/​scripts/​build.js Stops building factory entries.
packages/​craftcms-ui/​package.json Removes factory package exports.
packages/​craftcms-legacy/​package.json Removes the font-conversion dependency.
packages/​craftcms-legacy/​generalsettings/​webpack.config.js Deletes the obsolete rebranding build.
packages/​craftcms-legacy/​generalsettings/​src/​rebrand.scss Deletes obsolete rebranding styles.
packages/​craftcms-legacy/​generalsettings/​src/​rebrand.js Deletes obsolete rebranding behavior.
packages/​craftcms-legacy/​focalpoint/​webpack.config.js Deletes the standalone focal-point build.
packages/​craftcms-legacy/​dashboard/​src/​Dashboard.js Uses native grid properties.
packages/​craftcms-legacy/​cpcompat/​src/​element-index/​ThumbsElementIndexView.js Preserves the legacy thumbnail index view.
packages/​craftcms-legacy/​cpcompat/​src/​element-index/​index.js Assembles adapter index compatibility.
packages/​craftcms-legacy/​cpcompat/​src/​element-field-settings.js Documents adapter ownership in comments.
packages/​craftcms-legacy/​cpcompat/​src/​craft/​Queue.js Initializes legacy queue compatibility.
packages/​craftcms-legacy/​cpcompat/​src/​craft/​index.js Assembles the relocated Craft compatibility entry.
packages/​craftcms-legacy/​cpcompat/​src/​assets-field-settings.js Documents adapter ownership in comments.
packages/​craftcms-legacy/​cpcompat/​src/​admin-table.js Documents adapter ownership in comments.
packages/​craftcms-legacy/​cp/​webpack.config.js Deletes the old CP bundle build.
packages/​craftcms-legacy/​cp/​Ttf2Woff2Plugin.js Deletes the obsolete font converter.
packages/​craftcms-legacy/​cp/​src/​images/​view-modes/​cards.svg Removes the old bundle illustration.
packages/​craftcms-legacy/​cp/​src/​images/​preview/​icon-phone.svg Removes the old preview icon.
packages/​craftcms-legacy/​cp/​src/​images/​preview/​chrome-tablet-top.svg Removes old tablet artwork.
packages/​craftcms-legacy/​cp/​src/​images/​preview/​chrome-tablet-bottom.svg Removes old tablet artwork.
packages/​craftcms-legacy/​cp/​src/​images/​preview/​chrome-phone-top.svg Removes old phone artwork.
packages/​craftcms-legacy/​cp/​src/​images/​preview/​chrome-phone-bottom.svg Removes old phone artwork.
packages/​craftcms-legacy/​cp/​src/​images/​image-editor/​filters.svg Removes the old editor icon.
packages/​craftcms-legacy/​cp/​src/​images/​image-editor/​crop.svg Removes the old editor icon.
packages/​craftcms-legacy/​cp/​src/​images/​dev-mode.svg Removes old development-mode artwork.
packages/​craftcms-legacy/​cp/​src/​css/​craft.scss Deletes the old CP stylesheet entry.
packages/​craftcms-legacy/​cp/​src/​css/​_shame.scss Deletes obsolete browser workarounds.
packages/​craftcms-legacy/​cp/​src/​css/​_entry-type-select.scss Deletes old entry-type styles.
packages/​craftcms-legacy/​cp/​src/​css/​_debug_toolbar.scss Deletes old debug-toolbar styles.
packages/​craftcms-legacy/​cp/​src/​css/​_craft-spinner.scss Deletes old spinner styles.
packages/​craftcms-legacy/​cp/​src/​Craft.js Deletes the monolithic CP entry.
package.json Adds the module iframe-resizer dependency.
docs/​messages.md Documents the modern notification renderer.
.github/​workflows/​sync-cms-assets.yml Checks replacement build artifacts.
.github/​instructions/​a11y.instructions.md Updates screen-reader-only class guidance.

Review effort: Balanced

Comment thread src/Auth/Methods/RecoveryCodes.php Resolved
brandonkelly and others added 2 commits November 5, 2026 10:13
Recovery codes' Download action is now a descriptor array, which the
Password view model cast to a string, so the page failed with an array to
string conversion for anyone with recovery codes. Take the URL, download type
and elevation flag from the descriptor, keeping the Vue screen's contract.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>
The preview template no longer sets window.imageFocalPoint, so the preview
modal's resize and teardown hooks did nothing. Call the focal point module's
current editor instead.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The published Factory documentation still references removed @craftcms/ui/factory import paths, causing its examples to fail module resolution.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

The Factory and Utilities docs still showed the per-component factories and
button builders that create() replaced, so their imports no longer resolved.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Removing jQuery leaves the field layout designer waiting indefinitely for $ on core-only installations, preventing its tab controls, field library, and drag handlers from initializing.

No open issues 🎉

Review effort: Balanced

It waited for Craft.Grid and a global $ before initializing, though it now
builds its grids with the native Grid module. Without the Yii2 adapter
neither global appears, so its tabs, drag handlers and field library never
initialized. Wait only for the field search input.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1a41f-35f1-75f6-a070-f628cc4e7276
Co-authored-by: Amp <amp@ampcode.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The cross-stack bootstrap and adapter migration requires browser validation of initial loads, legacy plugin compatibility, and accessibility behavior that could not be exercised in this review environment.

No open issues 🎉

Review effort: Balanced

@brandonkelly
brandonkelly merged commit 8b5bdb3 into 6.x Nov 5, 2026
21 checks passed
@brandonkelly
brandonkelly deleted the chore/cp-js-modules branch November 5, 2026 11:14
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.

4 participants