Skip to content

Polish paragraph suggestions#1669

Merged
nygrenh merged 1 commit intomasterfrom
suggestions-part-2
Mar 6, 2026
Merged

Polish paragraph suggestions#1669
nygrenh merged 1 commit intomasterfrom
suggestions-part-2

Conversation

@nygrenh
Copy link
Member

@nygrenh nygrenh commented Mar 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added numbered suggestion labels in the AI suggestions dialog when multiple suggestions are available for a given writing action.
  • Refactor

    • Redesigned the AI suggestion dialog with an improved single-column layout, displaying each suggestion as an individually selectable button.
    • Removed content generation, learning support, and structure formatting suggestion categories from the AI suggestions menu.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

This PR introduces a strongly-typed ParagraphSuggestionAction enum to replace string-based action parameters across the CMS and headless-LMS services. It removes the "generate" action group, updates the suggestion dialog UI to single-column per-suggestion rendering, replaces hard-coded colors with theme-driven values, and removes runtime action validation in favor of compile-time type safety. Localization files are updated across multiple languages.

Changes

Cohort / File(s) Summary
Type System Definition
services/headless-lms/models/src/cms_ai.rs, services/headless-lms/models/src/lib.rs, shared-module/packages/common/src/bindings.ts
Introduces ParagraphSuggestionAction enum with 33 variants mapped to API strings via serde, including an as_str() method; exports type in shared bindings and Rust models module.
Type Guards & Validation
shared-module/packages/common/src/bindings.guard.ts
Adds isParagraphSuggestionAction() type guard; updates ParagraphSuggestionRequest guard to use new type instead of string validation.
API Integration
services/headless-lms/server/src/controllers/cms/ai_suggestions.rs, services/headless-lms/server/src/ts_binding_generator.rs
Changes ParagraphSuggestionRequest.action from string to ParagraphSuggestionAction; removes ALLOWED_PARAGRAPH_ACTIONS constant and runtime validation; adds type to TS binding generator.
AI Prompt Generation
services/headless-lms/chatbot/src/cms_ai_suggestion.rs
Adds action_instruction() helper mapping enum variants to human-readable instructions; injects action guidance into system and user prompts; updates CmsParagraphSuggestionInput.action type to ParagraphSuggestionAction.
CMS Frontend Types & Menu
services/cms/src/utils/Gutenberg/ai/abilities.ts, services/cms/src/utils/Gutenberg/ai/menu.ts
Updates buildParagraphSuggestionRequest() and createPlaceholderAbility() to use ParagraphSuggestionAction; removes "generate" from AiActionGroupId; introduces AiGroupLabelKey and AiActionLabelKey type unions.
Suggestion Dialog UI Component
services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
Refactors suggestion dialog from split-panel layout to single-column button list; adds per-suggestion rendering with DiffFormatter; replaces hard-coded colors with theme-driven values (BORDER_COLOR, ACCENT_COLOR, TEXT_COLOR); updates translation casts to use AiGroupLabelKey and AiActionLabelKey; increases vertical gap from 0.5rem to 0.75rem.
Localization Files
shared-module/packages/common/src/locales/{ar,en,fi,sv,uk}/cms.json
Adds "ai-dialog-label-suggestion-n" translation key across all five language files; removes 15 generation, learning, and structure-related action translation keys per language.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

feature, refactor

Poem

🐰 A rabbit hops through types so strong,
From strings to enums, the code moves along,
Suggestions now shimmer in single-column grace,
Theme-driven colors light up the space,
With actions typed safe, the future's in place!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Polish paragraph suggestions' is vague and generic, using the term 'Polish' without clearly indicating whether it means improving/refining the feature or adding Polish language support. Use a more specific title that clearly describes the main change, such as 'Refactor paragraph suggestions to use strongly-typed ParagraphSuggestionAction enum' or 'Improve paragraph suggestion UI with per-suggestion rendering'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch suggestions-part-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
services/headless-lms/models/src/cms_ai.rs (1)

76-116: Consider using strum to eliminate string duplication.

The as_str method duplicates the API strings already defined in #[serde(rename = "...")] attributes. This creates a maintenance burden where changes must be made in two places, risking drift.

Consider using strum_macros::AsRefStr or strum_macros::Display with matching rename attributes, or implementing as_str via serialization:

♻️ Alternative approach using serde serialization
impl ParagraphSuggestionAction {
    /// Returns the API string value for this action.
    pub fn as_str(&self) -> &'static str {
        // Use a static map to avoid allocation on each call
        match self {
            // Keep the current implementation, but consider generating this
            // from a build script or macro to ensure sync with serde renames
            ...
        }
    }
}

Or with strum:

use strum_macros::AsRefStr;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, AsRefStr)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub enum ParagraphSuggestionAction {
    #[serde(rename = "moocfi/ai/generate-draft-from-notes")]
    #[strum(serialize = "moocfi/ai/generate-draft-from-notes")]
    GenerateDraftFromNotes,
    // ...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/headless-lms/models/src/cms_ai.rs` around lines 76 - 116, The as_str
method duplicates serde rename strings for ParagraphSuggestionAction leading to
drift; replace the manual match with a derived string-representation using strum
(or wire serde value) instead. Add strum_macros::AsRefStr (or Display) to the
enum derive and annotate each variant with #[strum(serialize = "...")] matching
the existing #[serde(rename = "...")] values, then change as_str to take &self
and return self.as_ref() (or call the strum-provided method); alternatively
derive both serde and strum from the same attributes so the API string is
defined once for ParagraphSuggestionAction and remove the long manual match in
impl as_str.
services/headless-lms/server/src/ts_binding_generator.rs (1)

58-58: Move cms_ai::ParagraphSuggestionAction to maintain module grouping.

The new export is placed between chapters::ChapterWithStatus and chapters::DatabaseChapter, breaking the module-based grouping pattern. It should be placed after all chapters::* exports and before chatbot_configurations::* exports to maintain alphabetical module ordering.

♻️ Suggested placement
         chapters::ChapterWithStatus,
+        cms_ai::ParagraphSuggestionAction,
-        cms_ai::ParagraphSuggestionAction,
         chapters::DatabaseChapter,

Should become:

         chapters::CourseUserInfo,
         chapters::ChapterLockPreview,
         chapters::UnreturnedExercise,
+        cms_ai::ParagraphSuggestionAction,

         chatbot_configurations::ChatbotConfiguration,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/headless-lms/server/src/ts_binding_generator.rs` at line 58, The
export cms_ai::ParagraphSuggestionAction is currently inserted between
chapters::ChapterWithStatus and chapters::DatabaseChapter, breaking the module
grouping; move the cms_ai::ParagraphSuggestionAction line so it sits after all
chapters::* exports (i.e., after chapters::DatabaseChapter and any other
chapters::... entries) and before the chatbot_configurations::* exports to
restore alphabetical/module grouping.
services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx (2)

386-390: Type mismatch: ai-dialog-label-suggestion-n is not in AiActionLabelKey.

The translation key "ai-dialog-label-suggestion-n" exists in cms.json but is not included in the AiActionLabelKey type union (see menu.ts lines 40-61). While TypeScript won't catch this at compile time since you're not casting this particular call, consider either:

  1. Creating a separate type for dialog-related translation keys
  2. Extending AiActionLabelKey to include dialog keys

This would improve type consistency with the pattern used elsewhere in this file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx` around
lines 386 - 390, The translation key "ai-dialog-label-suggestion-n" is used in
withParagraphAiToolbarAction.tsx but isn't included in the AiActionLabelKey
union; update types so the call is covered: either add the dialog key(s) (e.g.,
"ai-dialog-label-suggestion-n") to the AiActionLabelKey union in menu.ts or
create a new dialog-specific union and use that type for dialog translation
calls in withParagraphAiToolbarAction.tsx; ensure the translation call and any
related functions reference the updated type (AiActionLabelKey or the new
DialogLabelKey) to keep typings consistent.

325-326: Consider removing unused parameters or documenting why they're retained.

The parameters originalHtml and allowedHtmlTagNames are renamed with underscore prefixes indicating they're unused. If they're not needed in the current implementation, consider removing them from the props interface entirely rather than keeping dead parameters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx` around
lines 325 - 326, The props for withParagraphAiToolbarAction (in
withParagraphAiToolbarAction.tsx) currently include originalHtml and
allowedHtmlTagNames but they are renamed to _originalHtml and
_allowedHtmlTagNames indicating they are unused; remove these dead props from
the relevant props/interface definition and any places that pass them through
(or if they must be kept for API compatibility, add a comment documenting why
they are retained) — update the Props interface/type and the
withParagraphAiToolbarAction parameter list (and any callers) to drop
originalHtml and allowedHtmlTagNames, or add a clear TODO/comment above the
props explaining why they are intentionally unused.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx`:
- Around line 386-390: The translation key "ai-dialog-label-suggestion-n" is
used in withParagraphAiToolbarAction.tsx but isn't included in the
AiActionLabelKey union; update types so the call is covered: either add the
dialog key(s) (e.g., "ai-dialog-label-suggestion-n") to the AiActionLabelKey
union in menu.ts or create a new dialog-specific union and use that type for
dialog translation calls in withParagraphAiToolbarAction.tsx; ensure the
translation call and any related functions reference the updated type
(AiActionLabelKey or the new DialogLabelKey) to keep typings consistent.
- Around line 325-326: The props for withParagraphAiToolbarAction (in
withParagraphAiToolbarAction.tsx) currently include originalHtml and
allowedHtmlTagNames but they are renamed to _originalHtml and
_allowedHtmlTagNames indicating they are unused; remove these dead props from
the relevant props/interface definition and any places that pass them through
(or if they must be kept for API compatibility, add a comment documenting why
they are retained) — update the Props interface/type and the
withParagraphAiToolbarAction parameter list (and any callers) to drop
originalHtml and allowedHtmlTagNames, or add a clear TODO/comment above the
props explaining why they are intentionally unused.

In `@services/headless-lms/models/src/cms_ai.rs`:
- Around line 76-116: The as_str method duplicates serde rename strings for
ParagraphSuggestionAction leading to drift; replace the manual match with a
derived string-representation using strum (or wire serde value) instead. Add
strum_macros::AsRefStr (or Display) to the enum derive and annotate each variant
with #[strum(serialize = "...")] matching the existing #[serde(rename = "...")]
values, then change as_str to take &self and return self.as_ref() (or call the
strum-provided method); alternatively derive both serde and strum from the same
attributes so the API string is defined once for ParagraphSuggestionAction and
remove the long manual match in impl as_str.

In `@services/headless-lms/server/src/ts_binding_generator.rs`:
- Line 58: The export cms_ai::ParagraphSuggestionAction is currently inserted
between chapters::ChapterWithStatus and chapters::DatabaseChapter, breaking the
module grouping; move the cms_ai::ParagraphSuggestionAction line so it sits
after all chapters::* exports (i.e., after chapters::DatabaseChapter and any
other chapters::... entries) and before the chatbot_configurations::* exports to
restore alphabetical/module grouping.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 04086ff6-e5e8-49e9-9006-278b311d34e3

📥 Commits

Reviewing files that changed from the base of the PR and between 46763bf and eeb5ba8.

📒 Files selected for processing (15)
  • services/cms/src/utils/Gutenberg/ai/abilities.ts
  • services/cms/src/utils/Gutenberg/ai/menu.ts
  • services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
  • services/headless-lms/chatbot/src/cms_ai_suggestion.rs
  • services/headless-lms/models/src/cms_ai.rs
  • services/headless-lms/models/src/lib.rs
  • services/headless-lms/server/src/controllers/cms/ai_suggestions.rs
  • services/headless-lms/server/src/ts_binding_generator.rs
  • shared-module/packages/common/src/bindings.guard.ts
  • shared-module/packages/common/src/bindings.ts
  • shared-module/packages/common/src/locales/ar/cms.json
  • shared-module/packages/common/src/locales/en/cms.json
  • shared-module/packages/common/src/locales/fi/cms.json
  • shared-module/packages/common/src/locales/sv/cms.json
  • shared-module/packages/common/src/locales/uk/cms.json

@nygrenh nygrenh merged commit 62b5b2a into master Mar 6, 2026
16 checks passed
@nygrenh nygrenh deleted the suggestions-part-2 branch March 6, 2026 15:04
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.

1 participant