Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,7 @@ UpgradeLog*.htm
__pycache__

# OpenCode
opencode.json
opencode.json

# Generated by the UI-definition tests for inspection; not an artefact of the build.
poc-output/
25 changes: 24 additions & 1 deletion Shoko.Abstractions/Actions/ExecutableActionInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Shoko.Abstractions.UI;

namespace Shoko.Abstractions.Actions;

Expand Down Expand Up @@ -40,6 +41,27 @@ namespace Shoko.Abstractions.Actions;
/// <param name="PluginId">
/// The ID of the plugin that owns the action.
/// </param>
/// <param name="Parameters">
/// A render-ready description of the action's invocation parameters, or
/// <see langword="null"/> when the action declares none.
/// </param>
/// <remarks>
/// <para>
/// An action's parameters are simply its own settable, serialized
/// properties — the caller's payload is populated straight onto the action
/// instance — so they are described by the very same
/// <see cref="UiDefinition"/> a configuration is described by. A client
/// renders an invocation form from it exactly as it renders a
/// configuration editor.
/// </para>
/// <para>
/// The action's own metadata surface (<see cref="Name"/>,
/// <see cref="Description"/>, <see cref="Category"/>,
/// <see cref="Permission"/>, <see cref="RequiresConfirmation"/>,
/// <see cref="Scope"/> and the scoped context) is deliberately not part of
/// it; those are described by this record instead.
/// </para>
/// </remarks>
public sealed record ExecutableActionInfo(
Guid Id,
string Name,
Expand All @@ -50,5 +72,6 @@ public sealed record ExecutableActionInfo(
ActionPermission Permission,
bool RequiresConfirmation,
string? ConfirmationMessage,
Guid PluginId
Guid PluginId,
UiDefinition? Parameters = null
);
2 changes: 1 addition & 1 deletion Shoko.Abstractions/Config/ConfigurationActionMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config;

Expand Down
2 changes: 1 addition & 1 deletion Shoko.Abstractions/Config/ConfigurationActionResult.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Collections.Generic;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config;

Expand Down
27 changes: 27 additions & 0 deletions Shoko.Abstractions/Config/ConfigurationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NJsonSchema;
using Shoko.Abstractions.Config.Services;
using Shoko.Abstractions.Plugin.Models;
using Shoko.Abstractions.UI;

namespace Shoko.Abstractions.Config;

Expand Down Expand Up @@ -114,6 +115,32 @@ public IReadOnlySet<string> LoadedEnvironmentVariables
/// </summary>
public required JsonSchema Schema { get; init; }

private UiDefinition? _uiDefinition = null;

/// <summary>
/// A render-ready description of how to lay out an editor for the
/// configuration, in the same shape an executable action's parameters are
/// described by.
/// </summary>
/// <remarks>
/// <para>
/// Unlike <see cref="Schema"/>, the returned document is meant to be
/// sufficient on its own: every element carries a concrete element kind,
/// its label, its default and the constraints needed for a cheap
/// client-side pre-check. The schema remains the authority for server-side
/// validation.
/// </para>
/// <para>
/// Built on first read and held from then on. A configuration's definition
/// can run to a hundred kilobytes or more, so nothing pays for one until it
/// asks for it, and nothing pays twice. Two threads racing the first read
/// both build one and the later assignment wins, which costs a discarded
/// tree and never a wrong answer.
/// </para>
/// </remarks>
public UiDefinition UiDefinition
=> _uiDefinition ??= _configurationService.GenerateUiDefinition(Type);

/// <summary>
/// Information about the plugin that the configuration belongs to.
/// </summary>
Expand Down
47 changes: 0 additions & 47 deletions Shoko.Abstractions/Config/Enums/DisplayButtonPosition.cs

This file was deleted.

36 changes: 36 additions & 0 deletions Shoko.Abstractions/Config/Services/IConfigurationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Shoko.Abstractions.Config.Events;
using Shoko.Abstractions.Config.Exceptions;
using Shoko.Abstractions.Plugin;
using Shoko.Abstractions.UI;
using Shoko.Abstractions.User;

namespace Shoko.Abstractions.Config.Services;
Expand Down Expand Up @@ -519,6 +520,41 @@ public interface IConfigurationService
/// </returns>
JsonSchema GenerateSchema(Type type);

/// <summary>
/// Generates a render-ready UI definition for the specified type using
/// the custom schema generator.
/// </summary>
/// <remarks>
/// <para>
/// Unlike <see cref="GenerateSchema(Type)" />, the returned document is
/// meant to be sufficient on its own: every element carries a concrete
/// element kind, its label, its default and the constraints needed for a
/// cheap client-side pre-check. The schema remains the authority for
/// server-side validation.
/// </para>
/// <para>
/// The type does not have to be a registered configuration, or an
/// <see cref="IConfiguration" /> at all — a plugin can describe any
/// shape it wants a form for. <see cref="UiDefinition.ID" /> is derived
/// the same way <see cref="GenerateSchema(Type)" /> derives
/// <c>Schema.Id</c>, so a type belonging to a loaded plugin gets a
/// stable id and anything else gets <see cref="Guid.Empty" />.
/// </para>
/// <para>
/// Nothing is cached here; each call walks the type afresh. A
/// configuration's own definition is cached by
/// <see cref="ConfigurationInfo.UiDefinition" />, which is what most
/// callers should read instead.
/// </para>
/// </remarks>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The UI definition.
/// </returns>
UiDefinition GenerateUiDefinition(Type type);

/// <summary>
/// Serializes the specified configuration to JSON.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Controls the displayed badge of a property/field in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Used to mark a property/field as a code editor in the specified language in
Expand All @@ -20,11 +20,6 @@ public class CodeEditorAttribute : Attribute
/// </summary>
public bool AutoFormatOnLoad { get; set; }

/// <summary>
/// The height of the text-area in the UI.
/// </summary>
public DisplayElementSize Height { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="CodeEditorAttribute"/> class with the specified <see cref="CodeEditorLanguage"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Defines a custom action for a section in the UI.
Expand Down Expand Up @@ -121,5 +121,11 @@ public object? DisableWhenSetTo
/// <summary>
/// When set, will disable the action if no changes are made to the configuration.
/// </summary>
/// <remarks>
/// Configuration-only: it compares the edited document against the saved
/// one, and an executable action's parameter form has nothing saved to
/// compare against. It stays on this otherwise shared attribute rather than
/// splitting the attribute in two over a single property.
/// </remarks>
public bool DisableIfNoChanges { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Define extra details for a list in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Define extra details for a record in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Define extra details around a section in the UI.
Expand All @@ -24,6 +24,12 @@ public class SectionAttribute(DisplaySectionType sectionType = DisplaySectionTyp
/// <summary>
/// Show the save action for the class/group in the UI.
/// </summary>
/// <remarks>
/// Configuration-only, and ignored on an executable action's parameter
/// form: an invocation has nothing to save, so the client renders an invoke
/// button instead. It stays on this otherwise shared attribute rather than
/// splitting the attribute in two over a single property.
/// </remarks>
public bool ShowSaveAction { get; set; } = false;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

using System;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Define the name of a section in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Define extra details for a select in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Used to mark a property/field as a text-area in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Shoko.Abstractions.Config.Enums;
using Shoko.Abstractions.UI.Enums;

namespace Shoko.Abstractions.Config.Attributes;
namespace Shoko.Abstractions.UI.Attributes;

/// <summary>
/// Controls the visibility of a property/field in the UI.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Shoko.Abstractions.Config.Components;
namespace Shoko.Abstractions.UI.Components;

/// <summary>
/// A select component for the UI.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Shoko.Abstractions.Config.Components;
namespace Shoko.Abstractions.UI.Components;

/// <summary>
/// A select group for the UI.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Shoko.Abstractions.Config.Components;
namespace Shoko.Abstractions.UI.Components;

/// <summary>
/// A select option for the UI.
Expand Down
10 changes: 10 additions & 0 deletions Shoko.Abstractions/UI/Elements/UiBooleanElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Shoko.Abstractions.UI.Elements;

/// <summary>
/// A boolean toggle.
/// </summary>
public sealed class UiBooleanElement : UiElement
{
/// <inheritdoc />
public override UiElementKind Kind => UiElementKind.Boolean;
}
Loading