Skip to content

Add action row support#1484

Open
solareon wants to merge 9 commits into
Tyrrrz:primefrom
solareon:add-buttons
Open

Add action row support#1484
solareon wants to merge 9 commits into
Tyrrrz:primefrom
solareon:add-buttons

Conversation

@solareon

Copy link
Copy Markdown

this adds support for exporting the action rows sometimes found in embeds. Only was able to test with single button action rows. I used copilot to draft up a proof of concept and tweaked to get it working 100% and fixed the colors to match discord styling

No issues closed by this. This adds support for action rows in exports for html and json

@solareon

solareon commented Feb 25, 2026

Copy link
Copy Markdown
Author

Preview in HTML format
image

Comment thread DiscordChatExporter.Cli.Tests/Specs/ComponentParsingSpecs.cs Outdated
Comment thread DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs Outdated
Comment thread DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs
@Tyrrrz Tyrrrz requested a review from Copilot February 26, 2026 13:29
@Tyrrrz Tyrrrz changed the title feat: add action row support Add action row support Feb 26, 2026

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.

Pull request overview

This pull request adds support for exporting Discord action rows with button components to both HTML and JSON export formats. The implementation follows Discord's component structure and styling conventions, allowing exported chat logs to display interactive buttons (though they're rendered as static or disabled elements in the export).

Changes:

  • New data models for button components and action rows with JSON parsing support
  • HTML rendering with Discord-style button CSS including 6 button styles (Primary, Secondary, Success, Danger, Link, Premium)
  • JSON export serialization for components structure
  • Basic test coverage for component parsing and JSON array existence

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
DiscordChatExporter.Core/Discord/Data/Components/ButtonStyle.cs Defines enum for 6 Discord button styles matching API specification
DiscordChatExporter.Core/Discord/Data/Components/ButtonComponent.cs Button component model with parsing logic for style, label, emoji, URL, and other properties
DiscordChatExporter.Core/Discord/Data/Components/ActionRowComponent.cs Action row container for button components with filtering for button type
DiscordChatExporter.Core/Discord/Data/Message.cs Integrates components into Message model and IsEmpty logic
DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml Adds CSS styles for button components and action rows
DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml Renders buttons as anchor tags (for URL buttons) or disabled button elements
DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs Serializes components to JSON format with button and action row structures
DiscordChatExporter.Cli.Tests/Specs/ComponentParsingSpecs.cs Tests parsing of link button from message payload
DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs Verifies components array exists in JSON export

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml Outdated
Comment thread DiscordChatExporter.Cli.Tests/Specs/ComponentParsingSpecs.cs Outdated
Comment thread DiscordChatExporter.Core/Discord/Data/Components/ActionRowComponent.cs Outdated
Comment thread DiscordChatExporter.Cli.Tests/Specs/ComponentParsingSpecs.cs Outdated
Comment thread DiscordChatExporter.Cli.Tests/Specs/JsonContentSpecs.cs Outdated
Comment thread DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml Outdated
Comment thread DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml Outdated
@solareon solareon requested a review from Tyrrrz February 27, 2026 13:33
Comment thread DiscordChatExporter.Core/Discord/Data/Message.cs Outdated
Comment thread DiscordChatExporter.Core/Discord/Data/Message.cs Outdated
Comment thread DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs Outdated
solareon and others added 5 commits March 2, 2026 14:46
this adds support for exporting the action rows sometimes found in embeds. Only was able to test with single button action rows. I used copilot to draft up a proof of concept and tweaked to get it working 100% and fixed the colors to match discord styling

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@solareon solareon force-pushed the add-buttons branch 2 times, most recently from cbeec1a to 1a671b3 Compare March 2, 2026 13:59
Comment thread DiscordChatExporter.Core/Discord/Data/Components/MessageComponent.cs Outdated

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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread DiscordChatExporter.Core/Exporting/MessageGroupTemplate.cshtml
Comment thread DiscordChatExporter.Core/Exporting/PreambleTemplate.cshtml Outdated
Comment thread DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs Outdated
@Tyrrrz

Tyrrrz commented Apr 7, 2026

Copy link
Copy Markdown
Owner

@solareon thank you. Please see updated review comments above.

solareon added 2 commits April 8, 2026 09:32
This should improve buffer performance for the JsonWriter. I guess that the others in this file are probably redundant as well but out of scope for this PR.
This respects the disabled state of buttons and fixes a few styling and performance tweaks to the loop.
@solareon solareon requested a review from Tyrrrz April 8, 2026 07:47
bool IsDisabled
)
{
public bool IsUrlButton => !string.IsNullOrWhiteSpace(Url);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Suggested change
public bool IsUrlButton => !string.IsNullOrWhiteSpace(Url);
public bool IsLink => !string.IsNullOrWhiteSpace(Url);

Comment on lines +32 to +33
if (!Enum.IsDefined(typeof(MessageComponentType), type))
return null;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Are you sure this check is necessary? If that value is not defined in the enum, your current logic would flow through to ParseDefault anyway. Is ParseDefault not meant to act as a fallback?

json.GetPropertyOrNull("style")
?.GetInt32OrNull()
?.Pipe(s =>
Enum.IsDefined(typeof(ButtonStyle), s) ? (ButtonStyle)s : (ButtonStyle?)null

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think we can just do Enum.ParseOrNull<ButtonStyle>(s) here (you may need to merge latest prime to get access to this method via PowerKit.Extensions). We will handle unknown styles during rendering (which your code already does by falling back to secondary style).

namespace DiscordChatExporter.Core.Discord.Data.Components;

// https://docs.discord.com/developers/components/reference#component-object
public partial record MessageComponent(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The design I was thinking about was more like this:

  • MessageComponent: abstract class, essentially a marker (basically a union in other languages). Has a Parse(JsonElement) method that checks the rawType and calls actual components' own Parse(JsonElement) methods.
  • ButtonMessageComponent: inherits from MessageComponent, has its own Parse(JsonElement), and the properties that are inherent to a button.
  • ActionRowComponent: inherits from MessageComponent, has its own Parse(JsonElement) (that recursively calls into MessageComponent.Parse to parse children), and the properties that are inherent to an action row (i.e. Children).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The rendering code would then pattern match via:

component switch
{
		ButtonMessageComponent button => ...
		ActionRowComponent actionRow => ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants