Skip to content

Conversation

EstoesMoises
Copy link

Hello everyone! I'm super happy to be writing this PR as it's one of my first open-source contributions :) Plus, this is a feature that I really want to have as part of DecapCMS.

Meet the Notes Pane!


Summary

  • Introduced a Notes Pane in the Editor (only for the Editorial Workflow), allowing users to add, edit, resolve, and delete notes linked to entries.
  • Notes are managed per entry and support author info, timestamps, and resolution status.
  • Notes are synchronized with backend storage and displayed in the dedicated UI pane (the Notes Pane).
  • Added localization (English only) for all note-related actions and notifications.

Currently, the Notes feature has only been fully implemented on the GitHub backend and the Test Backend. I've also written some code for the Proxy Backend, but the feature will not work with local_backend as it requires the Editorial Workflow.

In essence, the goal of this feature works as a layer of abstraction for PR issue comments. With this we take more advantage of the information that is recorded in each PR issue. This aligns well with the philosophy behind the Editorial Workflow, particularly the (awesome) work Decap has done around PR and branch handling.

I made this change after following the discussion on [Issue 52](#52), and personally Decap is a tool that I really want to use for some Documentation projects, the only thing I felt was missing was a "comments" feature, which is collaboration aspect that other popular platforms like Google Docs provide. But well, Decap is cooler ¯_(ツ)_/¯ so it should have one as well.


Test Plan / Demonstration

Notes Pane UI within the Editor:

Screenshot 2025-07-28 214457

GitHub Comments:

Screenshot 2025-07-28 214711

Resolving Notes adds an HTML comment to the GitHub PR Issue Comment:

Screenshot 2025-07-28 214723 Screenshot 2025-07-28 214732

This passes all current tests, and I've modified some test files to account for the Notes actions. There’s still a pending change to add backend/implementation-specific testing.


Current state / room for improvement

There is definitely room for improvement in this MVP. A few features that come to mind, and that are definitely on my roadmap (and I’d be more than happy to collaborate with others to make them happen) are:

  • Polling new comments every few seconds/minutes
  • As discussed in [Issue 52](Add a "notes pane" to the edit screen #52), this pane could be extended to show recent user activity, for example, when an entry has been reviewed, published, or unpublished.
  • Implementation across all backends (currently only GitHub is supported as a production backend)
  • Better unit testing

One of the main reasons I wanted to publish this in its current state is to get some early feedback before diving into more work. I’m really looking forward to hearing the maintainers’ thoughts and collaborating to make the Notes Pane feature as solid as possible.


Checklist

Please add an x inside each checkbox:


A picture of a cute animal (not mandatory but encouraged)

My dog, Luna :)

image

@EstoesMoises
Copy link
Author

In case this also helps navigate changes. Here's a summary provided by CoderabbitAI on the files changed:

Cohort / File(s) Change Summary
GitHub Backend Notes API
packages/decap-cms-backend-github/src/API.ts, packages/decap-cms-backend-github/src/implementation.tsx, packages/decap-cms-backend-github/src/types/githubPullRequests.ts
Adds methods to manage notes as PR comments, parse/format note content, handle note CRUD, and fetch PR metadata. Exposes note management in the backend implementation and defines the GitHubIssueComment type.
Proxy and Test Backend Notes
packages/decap-cms-backend-proxy/src/implementation.ts, packages/decap-cms-backend-test/src/implementation.ts
Implements note CRUD and PR metadata methods in proxy and test backends, with in-memory or delegated storage, including error handling and mock data for tests.
Core Backend and Types
packages/decap-cms-core/src/backend.ts, packages/decap-cms-lib-util/src/implementation.ts, packages/decap-cms-lib-util/src/index.ts
Extends backend interface and lib-util types to support notes and PR metadata methods, including the Note type and related signatures.
Redux Actions and Reducers
packages/decap-cms-core/src/actions/entries.ts, packages/decap-cms-core/src/reducers/entryDraft.js, packages/decap-cms-core/src/types/redux.ts, packages/decap-cms-core/src/reducers/__tests__/entryDraft.spec.js
Adds note-related action types, action creators, and thunks for note lifecycle events. Updates reducer and types to manage notes in draft state, with corresponding test updates.
Editor UI Integration
packages/decap-cms-core/src/components/Editor/Editor.js, packages/decap-cms-core/src/components/Editor/EditorInterface.js
Integrates notes into the editor UI, including state management, loading, and persistence, with new handlers and props for note actions and conditional rendering of the notes pane.
Editor Notes Pane Components
packages/decap-cms-core/src/components/Editor/EditorNotesPane/AddNoteForm.js, packages/decap-cms-core/src/components/Editor/EditorNotesPane/EditorNotesPane.js, packages/decap-cms-core/src/components/Editor/EditorNotesPane/NoteItem.js, packages/decap-cms-core/src/components/Editor/EditorNotesPane/NotesList.js
Introduces new React components for displaying, adding, editing, resolving, and deleting notes in a dedicated notes pane, with styled UI and user interaction logic.
Localization
packages/decap-cms-locales/src/en/index.js
Adds English localization strings for all notes-related UI, actions, and notifications.
Core Actions Test
packages/decap-cms-core/src/actions/__tests__/editorialWorkflow.spec.js
Updates editorial workflow test to cover note loading and related action dispatches.

@martinjagodic martinjagodic requested a review from Copilot July 31, 2025 08:35
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a collaborative Notes Pane feature for the Editor screen, enabling users to add, edit, resolve, and delete notes linked to entries in the Editorial Workflow. The notes are backed by GitHub PR comments and synchronized with backend storage.

  • Added comprehensive Notes system with CRUD operations, resolution tracking, and UI components
  • Implemented backend support for GitHub and Test backends with PR comments integration
  • Added localization support for all note-related messages and actions

Reviewed Changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/decap-cms-locales/src/en/index.js Added English localization strings for Notes Pane UI and notifications
packages/decap-cms-lib-util/src/implementation.ts Added Note interface and optional note-related methods to Implementation
packages/decap-cms-core/src/types/redux.ts Added Note types and action payload definitions for Redux state
packages/decap-cms-core/src/reducers/entryDraft.js Updated reducer to handle notes state and actions
packages/decap-cms-core/src/components/Editor/EditorNotesPane/*.js Created Notes Pane UI components for displaying and managing notes
packages/decap-cms-core/src/components/Editor/EditorInterface.js Integrated Notes Pane into editor interface with toggle functionality
packages/decap-cms-core/src/components/Editor/Editor.js Added notes loading and change handling logic
packages/decap-cms-core/src/backend.ts Added backend wrapper methods for note operations
packages/decap-cms-core/src/actions/entries.ts Implemented Redux actions for notes CRUD operations
packages/decap-cms-backend-/src/implementation. Added notes implementation for GitHub, Test, and Proxy backends

state = {
showEventBlocker: false,
previewVisible: localStorage.getItem(PREVIEW_VISIBLE) !== 'false',
notesVisible: localStorage.getItem(NOTES_VISIBLE) === 'false',
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

The logic for initializing notesVisible is inverted. It should be === 'true' to show notes when the localStorage value is 'true', not when it's 'false'.

Suggested change
notesVisible: localStorage.getItem(NOTES_VISIBLE) === 'false',
notesVisible: localStorage.getItem(NOTES_VISIBLE) === 'true',

Copilot uses AI. Check for mistakes.

<AvatarImage
src={note.get('avatarUrl')}
alt={`${note.get('author')} avatar`}
onError={e => {
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

The onError handler assumes the next sibling exists but doesn't verify it. This could cause runtime errors if the DOM structure changes. Consider adding a null check: e.target.nextSibling?.style && (e.target.nextSibling.style.display = 'flex')

Copilot uses AI. Check for mistakes.

author: currentUser.login || currentUser.name,
avatarUrl: currentUser.avatar_url,
entrySlug: slug,
timestamp: noteData.timestamp || new Date().toISOString(),
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

The noteData.timestamp is being used as a fallback, but the Note interface shows timestamp as a required field. Either the interface should mark it as optional or this fallback should be removed to ensure consistency.

Copilot uses AI. Check for mistakes.

private formatNoteForPR(note: Note): string {
const status = note.resolved ? API.NOTE_STATUS_RESOLVED : API.NOTE_STATUS_OPEN;

return `<!-- DecapCMS Note - Status: ${status} -->
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

[nitpick] The template literal has trailing spaces after the HTML comment. This could cause inconsistent formatting. Consider removing trailing whitespace or using a more structured approach.

Suggested change
return `<!-- DecapCMS Note - Status: ${status} -->
return `<!-- DecapCMS Note - Status: ${status} -->

Copilot uses AI. Check for mistakes.

const loadedEntry = await tryLoadEntry(getState(), collection, slug);
dispatch(entryLoaded(collection, loadedEntry));
dispatch(createDraftFromEntry(loadedEntry));
await dispatch(loadNotes(collection, slug));
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

The loadNotes call is made without checking if the backend supports notes or if workflow is enabled. This could cause unnecessary API calls or errors for backends that don't implement notes.

Copilot uses AI. Check for mistakes.

Comment on lines 108 to 114
handleBlur = () => {
// Delay to allow button click to register
setTimeout(() => {
if (!this.state.content.trim()) {
this.setState({ isFocused: false });
}
}, 150);
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

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

Using setTimeout with magic number 150ms to handle blur/click timing is fragile and could cause race conditions. Consider using a more robust approach like checking if the click target is the submit button or using onMouseDown instead of onClick.

Suggested change
handleBlur = () => {
// Delay to allow button click to register
setTimeout(() => {
if (!this.state.content.trim()) {
this.setState({ isFocused: false });
}
}, 150);
handleBlur = e => {
const { relatedTarget } = e;
if (relatedTarget && relatedTarget.type === 'submit') {
return; // Do not update state if blur is caused by clicking the submit button
}
if (!this.state.content.trim()) {
this.setState({ isFocused: false });
}

Copilot uses AI. Check for mistakes.

@EstoesMoises
Copy link
Author

EstoesMoises commented Aug 1, 2025

Hey @martinjagodic, I'm planning to tackle some of the action items from Copilot and to review this and ensure it is production-ready for the Github Backend

Leaving the base for others to add compatibility with the other backends if they are interested.

The action items I'm thinking of before moving this from Draft PR to Ready are:

  • Tackling copilot recommendations
  • Add the new tests for my code
  • Add a way to GET the notes when other users add them, probably using GitHub Webhooks if possible, and if not, a button to manually reload the notes.

I believe that with that done, I would consider it ready to be implemented in Main after your review.

Let me know your thoughts on this and if you'd like to add anything else, or if you have any feedback.

Thanks very much!

@martinjagodic
Copy link
Member

@EstoesMoises sounds good! How do you debug this while developing, as it doesn't work with the local server?

@EstoesMoises
Copy link
Author

EstoesMoises commented Aug 1, 2025

@EstoesMoises sounds good! How do you debug this while developing, as it doesn't work with the local server?

@martinjagodic I've been swapping backends by changing the config.yml from the dev-test folder. Since I'm focusing on Github compatibility, I'm using the configuration for Github locally - https://github.com/decaporg/decap-cms/blob/main/dev-test/config.yml

For debugging, I created and connected my GitHub account and I use a GitHub repository I made for this purpose.

Please let me know if there is a different approach that you suggest.

Side note: So far, I've tested the Test Backend and GitHub Backend. When I started implementing this feature, I also considered creating functionality for the local backend by creating a .json for notes, but I reverted those changes to make this feature only available on Editorial Workflow.

@martinjagodic
Copy link
Member

I had a really quick check and these are my first impressions:

  • Notes should be opt-in. I suggest editor.notes: true/false (false is default)
  • Should work with i18n. The toggle button should be different, because now it's the same icon that toggles side-by-side locale editing.
  • All entries should have notes, not only unpublished ones.

@EstoesMoises
Copy link
Author

I had a really quick check and these are my first impressions:

  • Notes should be opt-in. I suggest editor.notes: true/false (false is default)
  • Should work with i18n. The toggle button should be different, because now it's the same icon that toggles side-by-side locale editing.
  • All entries should have notes, not only unpublished ones.

Perfect! - Thanks for the feedback Martin. I will work on this

@EstoesMoises
Copy link
Author

Just wanted to leave a quick note - due to some unexpected life events, it might take me up to two weeks to get back with an update. I’ll still be working on the changes as soon as I can and I'm looking forward and aiming to get this feature completed 👍

@EstoesMoises
Copy link
Author

Hello everyone, due to unexpected circumstances, it has been a while since my last update, but I'm still here and will get this updated very soon 👍 I'm just sending this message to confirm that this is not abandoned, and I'm very excited about getting this feature ready :P

@EstoesMoises EstoesMoises marked this pull request as ready for review September 25, 2025 20:34
@EstoesMoises EstoesMoises requested a review from a team as a code owner September 25, 2025 20:34
@EstoesMoises
Copy link
Author

EstoesMoises commented Sep 25, 2025

Hey @martinjagodic this PR is ready for your review.

  • I made notes optional as suggested. Via config editor.notes
  • I've added the required tests for the Github implementation.
  • Changed from using PR Comments to create Github issues (and issues comments), this way I could enable Notes to both Published and Unpublished entries.
  • Github issues are closed when entries are published and are reopened if that same entry is unpublished.

Let me know your thoughts on this ! Looking forward to your feedback

@martinjagodic
Copy link
Member

martinjagodic commented Oct 6, 2025

Great work! Sorry, it took me a while to review. Not all my comments have to be implemented initially. It's better to go out with a minimal feature but a working feature and iterate from that.

  • I can use Notes even without setting notes: true
  • Decap Notes should listen to changes on GitHub and display comments async after a user leaves a comment on GitHub
  • Notes create GH issues for all entries that could have a notes interaction. This floods the repo with new issues. Is it possible to create an issue only after the first comment is submitted?
  • I like your solution with issues, but it bothers me that now we have one issue and one PR for each entry. Is it not possible to track notes on closed PRs? This is just a question; no need to revert to PRs yet.
  • I would like to see a link to the linked GH issue/PR somewhere on the notes pane.
  • Idea: could the "add new note" field be our markdown widget? Github comments support markdown. (can be done in notes v2)
  • Instead of relative time, let's have absolute time. Reason: Decap is translated into over 30 languages. Having 3 min ago hardcoded is not the best for that.
  • I can't see the time because it's under the icon
Screenshot 2025-10-06 at 16 16 52

@EstoesMoises
Copy link
Author

EstoesMoises commented Oct 14, 2025

Thanks for the notes @martinjagodic ;) , I'll be diving more deeply over the weekend to get this another update based on your feedback which is very good.

In the meantime I wanted to share my thoughts when I made the change from PR Comments to Github Issues, in case this background also sparks more ideas ! And I'd also like to get your thoughts:

When I first implemented this using PR comments, I relied on the information that Decap used to get as part of the Editorial Workflow. Essentially, for each pull request, I would retrieve the comments associated with it, and if any matched the regex, those would be treated as Decap Notes. In this setup, the pull request acted as the stable identifier for the list of notes. And I loved that because I wasn't creating more noise in the repo, but relying on an existing tool.

But there were two issues with my initial implementation:

  1. It didn’t work with published entries because I wasn’t tracking any PR that the entry was not a part of, which meant, the entry needed to be on the Editorial Workflow
  2. Even if I tracked those, unpublishing a published entry would still create a new PR.

In the second scenario, the entry would already have notes associated with it. However, if it were unpublished and returned to the Editorial Workflow, a new pull request would be created, which would mean a new identifier for the notes list. As a result, the notes would appear empty because they would now be tied to the new pull request instead of the original one.

I believe both issues could technically be fixed. It would be possible to build a mechanism that identifies and carries over the comments from the original pull request to maintain persistence. However, instead of implementing that, I decided to use GitHub Issues to keep the discussion independent from the PR. I thought this would make the content easier to find within the repository itself, even when not using Decap. It also made sense to me because, even if multiple pull requests exist for a single entry, there would only be one GH Issue serving as the main source for discussion (Notes). Since GitHub Issues is designed for conversations, I didn't think this approach would be confusing (or too annoying).

Let me know your thoughts !

@martinjagodic
Copy link
Member

Those are good points. Maybe someone else shares a fresh opinion, but you convinced me.

Do you think having an option in config to choose between issues or PRs would be too complicated to implement?

maybe:

editor:
  notes: 
    enable: true
    source: issue # or pull_request

An additional option is to have both the above config and notes: true/false, and it would just take issues as default.

We could also have notes: issue/pull_request/false, but this is mixing enums and booleans, which is not ideal.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants