Skip to content

Release: Merge development into main#4724

Merged
zachwolfe merged 33 commits into
mainfrom
development
Jul 18, 2026
Merged

Release: Merge development into main#4724
zachwolfe merged 33 commits into
mainfrom
development

Conversation

@zachwolfe

Copy link
Copy Markdown
Collaborator

Automated release PR to merge development into main.

zachwolfe and others added 30 commits June 30, 2026 01:00
Plugins that consist of a .csproj with NuGet package references but no
source files are a valid pattern (e.g. wrapper plugins). The previous
check rejected these because it required at least one .cs file.
A failed load was blocking subsequent retry attempts when DLLs appeared
shortly after the directory was created (e.g. during plugin installation).
Previously, deleting a failed plugin's directory left a stale entry in
_failedPlugins because GetPluginIdByDirectory only checks _knownPlugins.
Now fires PluginUnloaded so the UI updates accordingly.
Without this, the plugin settings view never re-rendered after a failed
load because no event was fired to trigger UsePluginState().
Ensures PluginLoadFailed fires on all genuine load failures so the UI
updates. Removes redundant LogPluginLoadFailure, centralizes logging
in RecordFailure, and avoids firing the event when the write lock is held.
Previously, deleting a plugin directory would unload it but leave it in
_knownPlugins, causing it to appear as "Unloaded" with no way to reload.
… system

Fired when a plugin directory is deleted (whether it was loaded, unloaded,
or failed). Ensures the UI removes it from all lists without needing a restart.
….yaml

Same fix as directory deletion — unloading alone leaves the plugin in
_knownPlugins, showing it as "Unloaded" with no way to reload.
Previously, exceptions thrown during Configure (e.g. missing dependency
DLLs) would propagate unhandled without recording the failure or firing
PluginLoadFailed, leaving the plugin invisible in the UI.
…er startup

When enabled, UsePlugins skips the synchronous DiscoverAndLoad during
setup and instead loads all plugins in a background task after the
server is ready. This significantly improves startup time when source
plugins need to be built.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…utes

Plugins can now register endpoints under /ivy/plugins/{slug}/ via a
dedicated API instead of requiring raw UseWebApplication access. Endpoints
are backed by a dynamic data source so they're added/removed with the
plugin lifecycle. Also moves the plugin icon asset route to
/ivy/plugin-icons/ to avoid path collisions with plugin endpoints.
…erface

These APIs expose too much of the ASP.NET pipeline to plugins. Plugins
should use UseEndpoints for HTTP routes instead. The methods remain on
Server for internal first-party use (DesktopWindow, DocsServer).
- Fix whitespace in PluginContext.cs (missing indentation on field and method)
- Fix indentation on goto label in PluginLoader.cs
- Add 'when' exception filters to exclude OutOfMemoryException and
  StackOverflowException from plugin isolation catches, satisfying
  CodeQL cs/generic-catch-clause rule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Suppress CP0006 (new interface member on IIvyExtendedPluginContext.UseEndpoints)
and CP0002 (removed UseWebApplication/UseWebApplicationBuilder from PluginContextBase).

Context interfaces are safe to extend per project conventions — plugins
call into them, not implement them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
System.Linq is already included via ImplicitUsings, so the explicit
using directive triggers IDE0005 in CI's dotnet format check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PluginLoadFailed and PluginRemoved are new members on IPluginManager,
which is a host-provided interface not implemented by plugins, so this
is not a breaking change for deployed plugins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The keydown listener that prevents Backspace from triggering browser
back-navigation was being removed whenever all shortcuts unregistered,
creating a window where the protection was disabled during view transitions.

This change makes the listener permanent — it is installed at module load
and never removed, ensuring Backspace prevention is always active regardless
of registry state.

Changes:
- Remove removeListener() call from unregisterShortcut()
- Remove removeListener() function (no longer called)
- Update _resetForTesting() to handle listener directly
- Add test verifying listener persists after all shortcuts are unregistered

Plan: 00652

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Reverts 5d6b992. The Vite+ CLI (vp) is a standalone binary installed
by voidzero-dev/setup-vp in CI, not an npm package; npx resolves an
unrelated placeholder "vp" package that errors out, breaking the
backend-checks job on every PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…8579

fix: revert npx vp back to bare vp in MSBuild targets
PR #4717 made the Backspace keydown listener permanent, but that did not
address the real bug: a shortcut registered by an app in a hidden tab
(e.g. DraftApp's Backspace delete action) still fired while a different
app (RecommendationsApp) was visually open.

The shortcut registry's isActive() check keys off an aria-hidden="true"
ancestor to decide whether a shortcut's element is visible. The Content
variant (ContentVariant) already sets aria-hidden on inactive panels, but
the Tabs variant (TabsVariant, used by DefaultSidebarAppShell for app
tabs) rendered inactive panels with only invisible/opacity-0 and no
aria-hidden. So a hidden app's button had no aria-hidden ancestor,
isActive() returned true, and its shortcut handler fired.

Add role="tabpanel"/aria-labelledby/aria-hidden to the non-Radix
TabsVariant panel wrapper, mirroring ContentVariant.

Also update the stale useShortcut test assertion left over from #4717:
the listener is now intentionally permanent, so it stays installed after
all shortcuts unregister.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aceKeyRemovingListenerWhenAllShortcutsUnregister

[00652] Fix Backspace Key Removing Listener When All Shortcuts Unregister
…655)

Remove the percentage maxHeight=100% from the DataTable's height==='Full'
branch (added by PR #4485). Inside the scrolling app host, that clamp
resolved against an ambiguous parent height and collapsed the table to its
min-height, leaving a whitespace band below the rows (issue #1695). flexGrow
already fills the flex parent; flexShrink + minHeight preserve the responsive
lower bound #4485 intended.

Update DataTableWidget.test.ts to guard against reintroducing the clamp,
scoped to the Full branch so the explicit-height else branch is unaffected.
…ightDataTableNotFillingAvailableSpace

[00655] Fix Full-Height DataTable Not Filling Available Space
…ention

The File icon kind added complexity (route handler, validation, path traversal
protection) with zero adopters. Icons for available plugins will now flow through
a tendril.json file in the .nupkg, supporting only Named and Url kinds.
zachwolfe and others added 3 commits July 18, 2026 00:27
@zachwolfe
zachwolfe merged commit 7d931f4 into main Jul 18, 2026
19 checks passed
@artem-ivy-ai

Copy link
Copy Markdown
Collaborator

Staging removed

Staging environment has been deleted for this PR.

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