diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
index 2e1dcfd8..99738648 100644
--- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
+++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
@@ -128,7 +128,9 @@ else
-
+ @* No default target for the rule zones: while the policy definition is open, three
+ zones are in play, so each drop has to be aimed at the one it belongs to. *@
+
@T("After the AI has processed all documents, it needs your instructions on how the result should be formatted. Would you like a structured list with keywords or a continuous text? Should the output include emojis or be written in formal business language? You can specify all these preferences in the output rules. There, you can also predefine a desired structure—for example, by using Markdown formatting to define headings, paragraphs, or bullet points.")
@@ -136,7 +138,7 @@ else
-
+
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
{
@@ -153,7 +155,7 @@ else
-
+
@T("Policy Description")
@@ -166,7 +168,10 @@ else
@T("Documents for the analysis")
-
+ @* The whole assistant catches drops, but only while this panel is the open one. A
+ collapsed panel keeps its content in the DOM with a height of zero, so without this
+ the invisible zone would swallow the drops meant for the policy definition. *@
+
diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
index be9fc83a..e1067b43 100644
--- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
+++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor.cs
@@ -244,6 +244,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore
+ /// Whether the document selection panel is the open one.
+ ///
+ ///
+ /// Only one of the two panels is ever open, so this is normally the opposite of the field above
+ /// -- but not always: the user can collapse both. It is tracked rather than derived because it
+ /// decides whether the document zone is the default target of the whole assistant, and a
+ /// collapsed zone must not hold that role.
+ ///
+ private bool documentSelectionExpanded;
private string policyName = string.Empty;
private string policyDescription = string.Empty;
private string policyAnalysisRules = string.Empty;
@@ -333,7 +345,11 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore this.selectedPolicy = value);
state.Restore(POLICY_IS_PROTECTED_STATE_KEY, value => this.policyIsProtected = value);
state.Restore(POLICY_HIDE_POLICY_DEFINITION_STATE_KEY, value => this.policyHidePolicyDefinition = value);
- state.Restore(POLICY_DEFINITION_EXPANDED_STATE_KEY, value => this.policyDefinitionExpanded = value);
+ state.Restore(POLICY_DEFINITION_EXPANDED_STATE_KEY, value =>
+ {
+ this.policyDefinitionExpanded = value;
+ this.documentSelectionExpanded = !value;
+ });
state.Restore(POLICY_NAME_STATE_KEY, value => this.policyName = value);
state.Restore(POLICY_DESCRIPTION_STATE_KEY, value => this.policyDescription = value);
state.Restore(POLICY_ANALYSIS_RULES_STATE_KEY, value => this.policyAnalysisRules = value);
@@ -359,6 +375,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore this.OwnerChat is not null
@@ -124,7 +125,6 @@ public partial class AttachDocuments : MSGComponentBase
{
this.MediaTranscriptionService.StateChanged += this.OnMediaImportStateChanged;
this.ApplyFilters([], [ Event.HIGHLIGHT_DROP_ZONE, Event.PATHS_DROPPED ]);
- this.ClaimDefaultZoneRole();
await base.OnInitializedAsync();
}
@@ -132,6 +132,7 @@ public partial class AttachDocuments : MSGComponentBase
/// Rehydrates results after the component is assigned another chat or target.
protected override async Task OnParametersSetAsync()
{
+ this.UpdateDefaultZoneRole();
await base.OnParametersSetAsync();
await this.SyncCompletedMediaAttachmentsAsync();
}
@@ -262,11 +263,35 @@ public partial class AttachDocuments : MSGComponentBase
#endregion
///
- /// Asks the area for the role of its default target, if this component wants it.
+ /// Keeps the role of the default target in step with the CatchAllDocuments parameter.
+ ///
+ ///
+ /// The flag is a parameter, so it can change while this component lives. A zone inside a
+ /// collapsed panel is the case this exists for: MudBlazor leaves the content of a collapsed
+ /// panel in the DOM with a height of zero, so the zone stays alive and cannot be aimed at --
+ /// yet it would keep the role and swallow every drop meant for the visible part of the page.
+ ///
+ private void UpdateDefaultZoneRole()
+ {
+ if (this.CatchAllDocuments)
+ {
+ this.ClaimDefaultZoneRole();
+ return;
+ }
+
+ if (!this.isDefaultZone)
+ return;
+
+ this.Scope?.ReleaseDefaultZone(this);
+ this.isDefaultZone = false;
+ }
+
+ ///
+ /// Asks the area for the role of its default target.
///
private void ClaimDefaultZoneRole()
{
- if (!this.CatchAllDocuments)
+ if (this.isDefaultZone)
return;
if (this.Scope is null)
@@ -275,15 +300,27 @@ public partial class AttachDocuments : MSGComponentBase
// There is nothing to claim: the surrounding page, assistant, or dialog is not a drop
// area at all. The flag would then do nothing, and silently -- which is how a zone ends
// up promising a behaviour it cannot deliver. So say it out loud: either the area needs
- // a DropZoneScope, or the flag does not belong here.
+ // a DropZoneScope, or the flag does not belong here. Reported once only, because the
+ // claim is retried on every parameter change.
//
- this.Logger.LogWarning("The attachment zone '{Name}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.Name);
+ if (!this.hasReportedDefaultZoneProblem)
+ {
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogWarning("The attachment zone '{Name}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.Name);
+ }
+
return;
}
this.isDefaultZone = this.Scope.TryBecomeDefaultZone(this);
- if (!this.isDefaultZone)
- this.Logger.LogDebug("The attachment zone '{Name}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.Name);
+
+ // Losing the role to a neighbour is a decision, not a defect -- and it can be undone later,
+ // when that neighbour goes away. So this one only goes to the debug log, and only once:
+ if (this.isDefaultZone || this.hasReportedDefaultZoneProblem)
+ return;
+
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogDebug("The attachment zone '{Name}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.Name);
}
///
diff --git a/app/MindWork AI Studio/Components/PathDropZone.razor.cs b/app/MindWork AI Studio/Components/PathDropZone.razor.cs
index 6b0d6b2d..5f572d46 100644
--- a/app/MindWork AI Studio/Components/PathDropZone.razor.cs
+++ b/app/MindWork AI Studio/Components/PathDropZone.razor.cs
@@ -64,17 +64,22 @@ public partial class PathDropZone : MSGComponentBase
private string dragClass = DEFAULT_DRAG_CLASS;
private bool isDefaultZone;
private bool isHighlighted;
+ private bool hasReportedDefaultZoneProblem;
#region Overrides of MSGComponentBase
protected override async Task OnInitializedAsync()
{
this.ApplyFilters([], [ Event.HIGHLIGHT_DROP_ZONE, Event.PATHS_DROPPED ]);
- this.ClaimDefaultZoneRole();
-
await base.OnInitializedAsync();
}
+ protected override void OnParametersSet()
+ {
+ this.UpdateDefaultZoneRole();
+ base.OnParametersSet();
+ }
+
///
/// Hands the role of the default target back to the area.
///
@@ -116,11 +121,35 @@ public partial class PathDropZone : MSGComponentBase
#endregion
///
- /// Asks the area for the role of its default target, if this zone wants it.
+ /// Keeps the role of the default target in step with the CatchAllDocuments parameter.
+ ///
+ ///
+ /// The flag is a parameter, so it can change while this zone lives. A zone inside a collapsed
+ /// panel is the case this exists for: MudBlazor leaves the content of a collapsed panel in the
+ /// DOM with a height of zero, so the zone stays alive and cannot be aimed at -- yet it would
+ /// keep the role and swallow every drop meant for the part of the page one can actually see.
+ ///
+ private void UpdateDefaultZoneRole()
+ {
+ if (this.CatchAllDocuments)
+ {
+ this.ClaimDefaultZoneRole();
+ return;
+ }
+
+ if (!this.isDefaultZone)
+ return;
+
+ this.Scope?.ReleaseDefaultZone(this);
+ this.isDefaultZone = false;
+ }
+
+ ///
+ /// Asks the area for the role of its default target.
///
private void ClaimDefaultZoneRole()
{
- if (!this.CatchAllDocuments)
+ if (this.isDefaultZone)
return;
if (this.Scope is null)
@@ -131,13 +160,28 @@ public partial class PathDropZone : MSGComponentBase
// up promising a behaviour it cannot deliver. So say it out loud: either the area needs
// a DropZoneScope, or the flag does not belong here.
//
- this.Logger.LogWarning("The path drop zone '{ZoneId}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.dropZoneId);
+ //
+ // Reported once only: the claim is retried on every parameter change, and repeating
+ // the message on every render would bury the log.
+ //
+ if (!this.hasReportedDefaultZoneProblem)
+ {
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogWarning("The path drop zone '{ZoneId}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.dropZoneId);
+ }
+
return;
}
this.isDefaultZone = this.Scope.TryBecomeDefaultZone(this);
- if (!this.isDefaultZone)
- this.Logger.LogDebug("The path drop zone '{ZoneId}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.dropZoneId);
+
+ // Losing the role to a neighbour is a decision, not a defect -- and it can be undone later,
+ // when that neighbour goes away. So this one only goes to the debug log, and only once:
+ if (this.isDefaultZone || this.hasReportedDefaultZoneProblem)
+ return;
+
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogDebug("The path drop zone '{ZoneId}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.dropZoneId);
}
///
diff --git a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
index a64fc4c5..8ecf467e 100644
--- a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
+++ b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
@@ -93,6 +93,7 @@ public partial class ReadFileContent : MSGComponentBase
private string dragClass = DEFAULT_DRAG_CLASS;
private bool isDefaultZone;
private bool isHighlighted;
+ private bool hasReportedDefaultZoneProblem;
private bool isFileDialogOpen;
private bool hasLoadedFileContent;
private string loadedFileName = string.Empty;
@@ -120,6 +121,7 @@ public partial class ReadFileContent : MSGComponentBase
this.loadedFileName = string.Empty;
}
+ this.UpdateDefaultZoneRole();
base.OnParametersSet();
}
@@ -127,10 +129,7 @@ public partial class ReadFileContent : MSGComponentBase
{
this.MediaTranscriptionService.StateChanged += this.OnMediaImportStateChanged;
if (this.EnableDragDrop)
- {
this.ApplyFilters([], [ Event.HIGHLIGHT_DROP_ZONE, Event.PATHS_DROPPED ]);
- this.ClaimDefaultZoneRole();
- }
await base.OnInitializedAsync();
await this.SyncCompletedMediaTextAsync();
@@ -240,11 +239,35 @@ public partial class ReadFileContent : MSGComponentBase
#endregion
///
- /// Asks the area for the role of its default target, if this component wants it.
+ /// Keeps the role of the default target in step with the CatchAllDocuments parameter.
+ ///
+ ///
+ /// The flag is a parameter, so it can change while this component lives. A zone inside a
+ /// collapsed panel is the case this exists for: MudBlazor leaves the content of a collapsed
+ /// panel in the DOM with a height of zero, so the zone stays alive and cannot be aimed at --
+ /// yet it would keep the role and swallow every drop meant for the visible part of the page.
+ ///
+ private void UpdateDefaultZoneRole()
+ {
+ if (this.EnableDragDrop && this.CatchAllDocuments)
+ {
+ this.ClaimDefaultZoneRole();
+ return;
+ }
+
+ if (!this.isDefaultZone)
+ return;
+
+ this.Scope?.ReleaseDefaultZone(this);
+ this.isDefaultZone = false;
+ }
+
+ ///
+ /// Asks the area for the role of its default target.
///
private void ClaimDefaultZoneRole()
{
- if (!this.CatchAllDocuments)
+ if (this.isDefaultZone)
return;
if (this.Scope is null)
@@ -253,15 +276,27 @@ public partial class ReadFileContent : MSGComponentBase
// There is nothing to claim: the surrounding page, assistant, or dialog is not a drop
// area at all. The flag would then do nothing, and silently -- which is how a zone ends
// up promising a behaviour it cannot deliver. So say it out loud: either the area needs
- // a DropZoneScope, or the flag does not belong here.
+ // a DropZoneScope, or the flag does not belong here. Reported once only, because the
+ // claim is retried on every parameter change.
//
- this.Logger.LogWarning("The file zone '{ZoneId}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.dropZoneId);
+ if (!this.hasReportedDefaultZoneProblem)
+ {
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogWarning("The file zone '{ZoneId}' wants to be the default target of its area, but it does not live in a drop zone scope. Dropping next to this zone will do nothing.", this.dropZoneId);
+ }
+
return;
}
this.isDefaultZone = this.Scope.TryBecomeDefaultZone(this);
- if (!this.isDefaultZone)
- this.Logger.LogDebug("The file zone '{ZoneId}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.dropZoneId);
+
+ // Losing the role to a neighbour is a decision, not a defect -- and it can be undone later,
+ // when that neighbour goes away. So this one only goes to the debug log, and only once:
+ if (this.isDefaultZone || this.hasReportedDefaultZoneProblem)
+ return;
+
+ this.hasReportedDefaultZoneProblem = true;
+ this.Logger.LogDebug("The file zone '{ZoneId}' asked to be the default target of its area, which another zone already is. It now takes only the drops aimed at itself.", this.dropZoneId);
}
///