diff --git a/Directory.Packages.props b/Directory.Packages.props
index 78f1efe86..c56454e95 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -244,6 +244,8 @@
+
+
@@ -267,6 +269,7 @@
+
diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
index 122c96e61..7cd80886c 100644
--- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx
+++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
@@ -555,19 +555,25 @@
+
+
+
+
+
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
index ae24f9e1d..419496663 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.Configure.cs
@@ -1,5 +1,7 @@
using DotNetCore.CAP;
using Elsa.Agents;
+using Elsa.AI.Persistence.EFCore.Features;
+using Elsa.AI.Persistence.EFCore.Sqlite.Extensions;
using Elsa.Extensions;
using Elsa.Features.Services;
using Elsa.Persistence.EFCore.Extensions;
@@ -158,6 +160,7 @@ public partial class WorkflowServiceModule
{
options.IsBlazorWebApp = true;
});
+ services.AddAIPersistenceStores(options => options.UseSqlite());
PreConfigure(elsa =>
{
elsa.UseWorkflowManagement(management =>
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
index ad2d1d861..922e5d874 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/WorkflowServiceModule.cs
@@ -13,6 +13,7 @@ using LINGYUN.Abp.ElsaNext.Labels;
using LINGYUN.Abp.ElsaNext.Notifications;
using LINGYUN.Abp.ElsaNext.Secrets.Blazor;
using LINGYUN.Abp.ElsaNext.Server;
+using LINGYUN.Abp.ElsaNext.Studio.AI.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Dashboard.Blazor;
using LINGYUN.Abp.ElsaNext.Studio.Diagnostics.OpenTelemetry.Blazor;
@@ -74,6 +75,7 @@ namespace LINGYUN.Abp.MicroService.WorkflowService;
typeof(AbpElsaNextStudioBlazorModule),
typeof(AbpElsaNextAgentsBlazorModule),
typeof(AbpElsaNextSecretsBlazorModule),
+ typeof(AbpElsaNextStudioAIBlazorModule),
typeof(AbpElsaNextStudioDashboardBlazorModule),
typeof(AbpElsaNextStudioDiagnosticsOpenTelemetryBlazorModule),
typeof(AbpElsaNextStudioDiagnosticsStructuredLogsBlazorModule),
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.Development.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.Development.json
index 4c1ded63c..a3bb2eec7 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.Development.json
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.WorkflowService/appsettings.Development.json
@@ -33,6 +33,25 @@
}
},
"Elsa": {
+ "AI": {
+ "Host": {
+ "DefaultProviderName": "copilot",
+ "Providers": [
+ {
+ "Name": "copilot",
+ "Provider": "copilot",
+ "Model": "GPT-5 mini",
+ "Enabled": true
+ }
+ ]
+ },
+ "Copilot": {
+ "EnableStreaming": true,
+ "Model": "GPT-5 mini",
+ "WorkingDirectory": "copilot",
+ "BaseDirectory": "./workspace"
+ }
+ },
"Http": {
"BaseUrl": "http://localhost:30050"
},
@@ -60,7 +79,10 @@
"RemoteServices": {
"BlobManagement": {
"BaseUrl": "http://localhost:30025",
- "IdentityClient": "InternalServiceClient",
+ "UseCurrentAccessToken": false
+ },
+ "Platform": {
+ "BaseUrl": "http://localhost:30025",
"UseCurrentAccessToken": false
}
},
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN.Abp.ElsaNext.AI.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN.Abp.ElsaNext.AI.csproj
new file mode 100644
index 000000000..9d149eb73
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN.Abp.ElsaNext.AI.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ net10.0
+ LINGYUN.Abp.ElsaNext.AI
+ LINGYUN.Abp.ElsaNext.AI
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/AbpElsaNextAIModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/AbpElsaNextAIModule.cs
new file mode 100644
index 000000000..0570b6f44
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/AbpElsaNextAIModule.cs
@@ -0,0 +1,23 @@
+using Elsa.Features.Services;
+using LINGYUN.Abp.ElsaNext.AI.Extensions;
+using Microsoft.Extensions.Configuration;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ElsaNext.AI;
+
+[DependsOn(typeof(AbpElsaNextModule))]
+public class AbpElsaNextAIModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Configuration.GetSection("Elsa:AI");
+ PreConfigure(elsa =>
+ {
+ elsa.UseAbpAI(ai =>
+ {
+ ai.ConfigureOptions = options => configuration.GetSection("Host").Bind(options);
+ ai.ConfigureCopilotOptions = options => configuration.GetSection("Copilot").Bind(options);
+ });
+ });
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Extensions/ModuleExtensions.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Extensions/ModuleExtensions.cs
new file mode 100644
index 000000000..6f651b2a9
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Extensions/ModuleExtensions.cs
@@ -0,0 +1,14 @@
+using Elsa.Extensions;
+using Elsa.Features.Services;
+using LINGYUN.Abp.ElsaNext.AI.Features;
+using System;
+
+namespace LINGYUN.Abp.ElsaNext.AI.Extensions;
+
+public static class ModuleExtensions
+{
+ public static IModule UseAbpAI(this IModule module, Action? configure = null)
+ {
+ return module.Use(configure);
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Features/AbpAIFeature.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Features/AbpAIFeature.cs
new file mode 100644
index 000000000..689dbda46
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.AI/LINGYUN/Abp/ElsaNext/AI/Features/AbpAIFeature.cs
@@ -0,0 +1,44 @@
+using Elsa.AI.Copilot.Options;
+using Elsa.AI.Host.Features;
+using Elsa.AI.Host.Options;
+using Elsa.AI.Host.Services;
+using Elsa.Extensions;
+using Elsa.Features.Abstractions;
+using Elsa.Features.Services;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+
+namespace LINGYUN.Abp.ElsaNext.AI.Features;
+
+public class AbpAIFeature(IModule module) : FeatureBase(module)
+{
+ public Action? ConfigureOptions { get; set; }
+ public Action? ConfigureCopilotOptions { get; set; }
+ public Action? ConfigureToolEnablement { get; set; }
+
+ public override void Configure()
+ {
+ Module.AddFastEndpointsAssembly();
+ }
+
+ public override void Apply()
+ {
+ Services.AddAIHostServices(ConfigureOptions);
+ Services.AddCopilotAIProvider(ConfigureCopilotOptions);
+ if (ConfigureToolEnablement != null)
+ {
+ Services.AddSingleton(ConfigureToolEnablement);
+ }
+ Module.AddFastEndpointsFromModule();
+ }
+
+ public AbpAIFeature EnableWorkflowProposalTools()
+ {
+ ConfigureToolEnablement += enablement =>
+ {
+ enablement.Enable("workflows.proposeCreate");
+ enablement.Enable("workflows.proposeUpdate");
+ };
+ return this;
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/AbpElsaNextStudioAIBlazorModule.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/AbpElsaNextStudioAIBlazorModule.cs
new file mode 100644
index 000000000..0d14d6fde
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/AbpElsaNextStudioAIBlazorModule.cs
@@ -0,0 +1,49 @@
+using Elsa.Studio.AI.Extensions;
+using LINGYUN.Abp.ElsaNext.AI;
+using LINGYUN.Abp.ElsaNext.Localization;
+using LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.Navigation;
+using LINGYUN.Abp.ElsaNext.Studio.Blazor;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Routing;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.UI.Navigation;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.AI.Blazor;
+
+[DependsOn(
+ typeof(AbpElsaNextAIModule),
+ typeof(AbpElsaNextStudioBlazorModule))]
+public class AbpElsaNextStudioAIBlazorModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var configuration = context.Configuration.GetSection("Elsa:Studio");
+
+ Configure(options =>
+ {
+ options.AdditionalAssemblies.Add(typeof(AbpElsaNextStudioAIBlazorModule).Assembly);
+ });
+
+ Configure(options =>
+ {
+ options.MenuContributors.Add(new ElsaStudioAIMenuContributor());
+ });
+
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded("LINGYUN.Abp.ElsaNext.Studio.AI.Blazor");
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/Localization/Resources/AIBlazor");
+ });
+ var elsaNextStudioBlazoeOptions = context.Services.ExecutePreConfiguredActions();
+
+ context.Services.AddWeaverModule(elsaNextStudioBlazoeOptions.BackendApiConfig);
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xml b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xml
new file mode 100644
index 000000000..5d6962159
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xsd b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.csproj b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.csproj
new file mode 100644
index 000000000..a9b00d7d4
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.csproj
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ net10.0
+ enable
+ enable
+ LINGYUN.Abp.ElsaNext.Studio.AI.Blazor
+ LINGYUN.Abp.ElsaNext.Studio.AI.Blazor
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/en.json b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/en.json
new file mode 100644
index 000000000..a6c727672
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/en.json
@@ -0,0 +1,7 @@
+{
+ "culture": "en",
+ "texts": {
+ "Menu:AI": "AI",
+ "AI": "AI"
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/zh-Hans.json b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/zh-Hans.json
new file mode 100644
index 000000000..5b82fa625
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Localization/Resources/AIBlazor/zh-Hans.json
@@ -0,0 +1,7 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Menu:AI": "AI",
+ "AI": "AI"
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Navigation/ElsaStudioAIMenuContributor.cs b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Navigation/ElsaStudioAIMenuContributor.cs
new file mode 100644
index 000000000..31f6010ac
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Navigation/ElsaStudioAIMenuContributor.cs
@@ -0,0 +1,27 @@
+using LINGYUN.Abp.ElsaNext.Localization;
+using LINGYUN.Abp.ElsaNext.Studio.Blazor.Navigation;
+using Volo.Abp.UI.Navigation;
+
+namespace LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.Navigation;
+
+public class ElsaStudioAIMenuContributor : IMenuContributor
+{
+ public Task ConfigureMenuAsync(MenuConfigurationContext context)
+ {
+ if (context.Menu.Name == StandardMenus.Main)
+ {
+ var menu = context.Menu;
+ var l = context.GetLocalizer();
+
+ var group = menu.GetMenuItemOrNull(ElsaStudioMenus.GroupName);
+ group?.AddItem(new ApplicationMenuItem(
+ ElsaStudioMenus.GroupName + ".AI",
+ l["Menu:AI"],
+ url: "/ai/weaver",
+ icon: "fa fa-openai",
+ order: 6));
+ }
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Pages/Weaver.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Pages/Weaver.razor
new file mode 100644
index 000000000..62ef6a33e
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/Pages/Weaver.razor
@@ -0,0 +1,11 @@
+@page "/ai/weaver"
+@attribute [Authorize]
+@inherits StudioComponentBase
+@using Elsa.Studio.AI.UI.Pages
+
+
+ @* *@
+
+
+
+
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/_Imports.razor b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/_Imports.razor
new file mode 100644
index 000000000..6a4064616
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/_Imports.razor
@@ -0,0 +1,5 @@
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Components.Web
+@using Elsa.Studio.Components
+@using Elsa.Studio.Localization
+@using MudBlazor
diff --git a/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/wwwroot/weaver.css b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/wwwroot/weaver.css
new file mode 100644
index 000000000..61ffa893e
--- /dev/null
+++ b/aspnet-core/modules/elsa/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/wwwroot/weaver.css
@@ -0,0 +1,196 @@
+.weaver-page {
+ height: calc(100vh - 72px);
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.weaver-heading,
+.weaver-thread-header,
+.weaver-panel-title,
+.weaver-composer-actions,
+.weaver-tool,
+.weaver-activity {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+}
+
+.weaver-workspace {
+ min-height: 0;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 360px;
+ gap: 12px;
+ flex: 1;
+}
+
+.weaver-main,
+.weaver-side,
+.weaver-thread {
+ min-height: 0;
+}
+
+.weaver-thread,
+.weaver-panel {
+ border: 1px solid var(--mud-palette-lines-default);
+ border-radius: 8px;
+ background: var(--mud-palette-surface);
+}
+
+.weaver-thread {
+ height: 100%;
+ display: grid;
+ grid-template-rows: auto minmax(0, 1fr) auto auto;
+ gap: 12px;
+ padding: 12px;
+}
+
+.weaver-message-list,
+.weaver-activity-list,
+.weaver-tool-list {
+ min-height: 0;
+ overflow: auto;
+}
+
+.weaver-message-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.weaver-message {
+ max-width: 78%;
+ border: 1px solid var(--mud-palette-lines-default);
+ border-radius: 8px;
+ padding: 10px 12px;
+ background: var(--mud-palette-background);
+}
+
+ .weaver-message.is-user {
+ align-self: flex-end;
+ background: var(--mud-palette-primary-hover);
+ }
+
+ .weaver-message.is-assistant {
+ align-self: flex-start;
+ }
+
+.weaver-message-meta {
+ font-size: 0.72rem;
+ color: var(--mud-palette-text-secondary);
+ margin-bottom: 4px;
+}
+
+.weaver-message-text {
+ white-space: pre-wrap;
+}
+
+.weaver-empty {
+ margin: auto;
+ text-align: center;
+}
+
+.weaver-composer {
+ border-top: 1px solid var(--mud-palette-lines-default);
+ padding-top: 10px;
+ display: grid;
+ gap: 8px;
+}
+
+.weaver-attachments {
+ min-height: 28px;
+ flex-wrap: wrap;
+}
+
+.weaver-agent-select {
+ width: 160px;
+}
+
+.weaver-context-kind {
+ width: 190px;
+}
+
+.weaver-reference-input {
+ width: 220px;
+}
+
+.weaver-side {
+ display: grid;
+ grid-template-rows: auto minmax(160px, 1fr) minmax(120px, 1fr) auto;
+ gap: 12px;
+}
+
+.weaver-panel {
+ padding: 12px;
+ overflow: auto;
+}
+
+.weaver-capability-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 8px;
+ margin-top: 10px;
+}
+
+.weaver-capability-pill {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ border: 1px solid var(--mud-palette-lines-default);
+ border-radius: 6px;
+ padding: 6px 8px;
+ font-size: 0.82rem;
+}
+
+ .weaver-capability-pill.is-enabled {
+ color: var(--mud-palette-success);
+ }
+
+ .weaver-capability-pill.is-disabled {
+ color: var(--mud-palette-text-secondary);
+ }
+
+.weaver-activity-list,
+.weaver-tool-list {
+ display: grid;
+ gap: 8px;
+ margin-top: 10px;
+}
+
+.weaver-activity,
+.weaver-tool,
+.weaver-proposal {
+ border: 1px solid var(--mud-palette-lines-default);
+ border-radius: 8px;
+ padding: 8px;
+}
+
+@media (max-width: 960px) {
+ .weaver-page {
+ height: auto;
+ }
+
+ .weaver-workspace {
+ grid-template-columns: 1fr;
+ }
+
+ .weaver-side {
+ grid-template-rows: none;
+ }
+
+ .weaver-message {
+ max-width: 100%;
+ }
+
+ .weaver-composer-actions {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .weaver-agent-select,
+ .weaver-context-kind,
+ .weaver-reference-input {
+ width: 100%;
+ }
+}