Skip to content

fix: make public projects editable; gate only backend writes - #840

Merged
thiagoralves merged 1 commit into
developmentfrom
fix/public-projects-editable
Jun 4, 2026
Merged

fix: make public projects editable; gate only backend writes#840
thiagoralves merged 1 commit into
developmentfrom
fix/public-projects-editable

Conversation

@thiagoralves

@thiagoralves thiagoralves commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Shared-surface mirror of openplc-web PR #484 — keeps the byte-identical shared surface (src/frontend/, src/middleware/shared/) in sync.

What changed (shared surface)

  • Removes the read-only / fork-redirect feature: read-only-project modal, the workspace.isReadOnly UI-lockdown flag, the forkProject / listMyFolders port methods + ProjectFolder/ForkProject* types, and all Monaco/graphical/menu/tree gating.
  • Adds workspace.canEdit (persist-permission, defaults true) that gates only backend writes — save, commit, create/delete branch, stash, discard, restore, README save — each with a graceful warning toast (notifyNoWritePermission).

Editor impact

None functionally: the desktop editor has no remote permission concept, so canEdit stays undefined ⇒ treated as editable. This PR exists purely to keep the shared surface byte-identical with web.

Verification

  • tsc --noEmit clean on this repo.
  • scripts/compare-surfaces.py reports match: True against the web PR branch (0 diffs).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added write permission notifications: users without edit access now receive a quick warning notification when attempting restricted actions instead of a modal dialog.
  • Improvements

    • Simplified permission handling throughout the application using a clearer canEdit permission flag.
    • Removed the read-only project modal and associated fork workflow.
    • Refined editor read-only behavior for better user experience.

Shared-surface mirror of openplc-web PR #484 (byte-identical).

Removes the read-only / fork-redirect feature (read-only-project modal,
workspace.isReadOnly UI-lockdown flag, forkProject/listMyFolders port
methods + ProjectFolder/ForkProject* types, and all editor/menu/tree
gating). Replaces it with a narrowly-scoped workspace.canEdit flag that
gates only backend writes (save/commit/branch/stash/discard/restore/
README) with a graceful warning toast. In-memory editing, simulation,
and compilation are always allowed.

No behavioral change for the editor (desktop has no remote permission
concept, so canEdit stays undefined ⇒ treated as editable). This PR
exists to keep the byte-identical shared surface in sync.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aa04be3c-9ddf-4ddf-956f-1d7ea61a9f37

📥 Commits

Reviewing files that changed from the base of the PR and between 3c49dc6 and a78e288.

📒 Files selected for processing (27)
  • src/frontend/components/_features/[workspace]/branches/create-branch-modal.tsx
  • src/frontend/components/_features/[workspace]/branches/create-branch-popover.tsx
  • src/frontend/components/_features/[workspace]/branches/delete-branch-modal.tsx
  • src/frontend/components/_features/[workspace]/create-element/element-card/index.tsx
  • src/frontend/components/_features/[workspace]/editor/graphical/index.tsx
  • src/frontend/components/_features/[workspace]/editor/monaco/index.tsx
  • src/frontend/components/_features/[workspace]/source-control/changes-section.tsx
  • src/frontend/components/_features/[workspace]/source-control/commit-details.tsx
  • src/frontend/components/_features/[workspace]/source-control/stash-section.tsx
  • src/frontend/components/_molecules/menu-bar/menus/file.tsx
  • src/frontend/components/_molecules/project-tree/index.tsx
  • src/frontend/components/_organisms/modals/project-readme-modal.tsx
  • src/frontend/components/_organisms/modals/read-only-project-modal.tsx
  • src/frontend/components/_organisms/workspace-activity-bar/default.tsx
  • src/frontend/components/_templates/app-layout.tsx
  • src/frontend/screens/workspace-screen.tsx
  • src/frontend/services/save-actions.ts
  • src/frontend/store/__tests__/shared-slice.test.ts
  • src/frontend/store/__tests__/workspace-slice.test.ts
  • src/frontend/store/slices/modal/slice.ts
  • src/frontend/store/slices/modal/types.ts
  • src/frontend/store/slices/shared/slice.ts
  • src/frontend/store/slices/workspace/slice.ts
  • src/frontend/store/slices/workspace/types.ts
  • src/frontend/utils/__tests__/notify-no-write-permission.test.ts
  • src/frontend/utils/notify-no-write-permission.ts
  • src/middleware/shared/ports/project-port.ts
💤 Files with no reviewable changes (5)
  • src/frontend/store/slices/modal/slice.ts
  • src/frontend/store/slices/modal/types.ts
  • src/frontend/components/_templates/app-layout.tsx
  • src/frontend/components/_organisms/modals/read-only-project-modal.tsx
  • src/frontend/screens/workspace-screen.tsx

Walkthrough

This PR refactors the workspace permission model from a read-only flag with modal redirects to a canEdit state that gates only backend persistence operations. The ReadOnlyProjectModal component is deleted entirely. Components now show notifications instead of routing to modals when write permissions are denied. Local editing, simulation, and compilation remain unaffected by the permission flag.

Changes

Permission Model Refactoring

Layer / File(s) Summary
Workspace permission state and notification utility
src/frontend/store/slices/workspace/types.ts, src/frontend/store/slices/workspace/slice.ts, src/frontend/utils/notify-no-write-permission.ts, src/frontend/utils/__tests__/notify-no-write-permission.test.ts
New workspace.canEdit: boolean state field and setCanEdit(value) action replace isReadOnly and setReadOnly. New notifyNoWritePermission(action) utility displays non-blocking warning toasts for permission denials, with unit test coverage.
Backend permission data flow
src/frontend/store/slices/shared/slice.ts, src/middleware/shared/ports/project-port.ts
handleOpenProjectResponse now calls setCanEdit(data.canEdit !== false) instead of setReadOnly. JSDoc clarifies canEdit gates only backend write operations (save/commit/branch/stash/discard) while leaving local editing/simulation/compilation enabled. Fork/folder-related types and methods removed from ProjectPort.
Service-layer permission gating
src/frontend/services/save-actions.ts
executeSaveProject and executeSaveFile check !state.workspace.canEdit and call notifyNoWritePermission before returning { success: false }, replacing prior read-only modal routing.
Source control handlers
src/frontend/components/_features/[workspace]/source-control/changes-section.tsx, src/frontend/components/_features/[workspace]/source-control/commit-details.tsx, src/frontend/components/_features/[workspace]/source-control/stash-section.tsx
Commit, discard, stash, and restore handlers now check canEdit and early-return with notifyNoWritePermission instead of branching to read-only modal. Button UI logic updated to depend on operation state rather than isReadOnly.
Branch operations
src/frontend/components/_features/[workspace]/branches/create-branch-modal.tsx, src/frontend/components/_features/[workspace]/branches/create-branch-popover.tsx, src/frontend/components/_features/[workspace]/branches/delete-branch-modal.tsx
Modal and popover components now derive canEdit from store, call notifyNoWritePermission when user cannot edit, and remove read-only modal redirect logic from submission and popover open handlers.
Editor and menu simplification
src/frontend/components/_features/[workspace]/editor/monaco/index.tsx, src/frontend/components/_features/[workspace]/editor/graphical/index.tsx, src/frontend/components/_molecules/menu-bar/menus/file.tsx, src/frontend/screens/workspace-screen.tsx
Monaco editor read-only now driven solely by debugger visibility (not isReadOnly). Graphical editor overlay message updated from "Read-only" to "Read-only: viewing historical commit". File menu save handlers no longer branch to read-only modal. WorkspaceScreen stops passing readOnly prop to editors.
Modal system and navigation cleanup
src/frontend/store/slices/modal/types.ts, src/frontend/store/slices/modal/slice.ts, src/frontend/components/_templates/app-layout.tsx, src/frontend/components/_molecules/project-tree/index.tsx, src/frontend/components/_features/[workspace]/create-element/element-card/index.tsx
ModalTypes union removes read-only-project and adds public-catalog-browser and confirm-install-libraries. ReadOnlyProjectModal import and rendering removed from AppLayout. Project tree leaf and element card popovers simplified: menu actions no longer route through read-only modal guard.
Additional components and tests
src/frontend/components/_organisms/modals/project-readme-modal.tsx, src/frontend/components/_organisms/workspace-activity-bar/default.tsx, src/frontend/store/__tests__/shared-slice.test.ts, src/frontend/store/__tests__/workspace-slice.test.ts
README modal uses projectCanEdit from store. Activity bar build pre-save logic checks canEdit instead of !isReadOnly. Store tests updated to verify workspace.canEdit behavior instead of isReadOnly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Autonomy-Logic/openplc-editor#799: Prior PR implements the older isReadOnlyread-only-project modal approach that this PR replaces with canEdit + notification-based gating.
  • Autonomy-Logic/openplc-editor#828: Introduces ProjectReadmeModal component; this PR updates its editability logic to use the new workspace.canEdit permission model.
  • Autonomy-Logic/openplc-editor#803: This PR adds modal type definitions (public-catalog-browser, confirm-install-libraries) that #803 builds out with actual modal component implementations.

Suggested labels

enhancement

Suggested reviewers

  • JoaoGSP
  • Gustavohsdp

Poem

🐰 Permissions refined, modals retired with care,
Read-only once routed through modals in air,
Now canEdit gates what gets saved to the store,
While local edits and building endure,
A cleaner permission model at your door!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: enabling editing on public projects while restricting backend write operations, which is the core objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/public-projects-editable

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@thiagoralves
thiagoralves merged commit baff693 into development Jun 4, 2026
12 checks passed
@thiagoralves
thiagoralves deleted the fix/public-projects-editable branch June 4, 2026 02:10
thiagoralves added a commit that referenced this pull request Jun 4, 2026
Two follow-ups from the development merge (#840 — "make public
projects editable; gate only backend writes"):

1. Resolve the build-save conflict in `workspace-activity-bar`.
   Dev added a `canEdit` capability so viewers of a public project
   (no write permission) can compile locally without their save
   getting 401'd by the backend.  Our branch had already removed
   the lossy `editingState` gate so the build always saves.  Merged
   pattern keeps the always-save behavior but gates it on
   `canEdit` instead of `!isReadOnly` — the previous `isReadOnly`
   flag was removed in dev with the now-deleted `ReadOnlyProjectModal`.
   Same gate applied to the debug-start path (mirrors the build).

2. Add `notify-no-write-permission.test.ts` to jest's
   `testPathIgnorePatterns`.  Dev authored it as a Vitest-native
   test (top-level `vi.mock` with relative path, expecting the
   runner to hoist).  Vitest does hoist `vi.mock` natively; Jest's
   `babel-plugin-jest-hoist` only recognises literal `jest.mock`
   and the test fails to load on editor.  Mirror of the same
   asymmetric-runner pattern web already uses for 6 jest-only
   tests in `vitest.config.ts` (`python-lsp`, library-build-
   orchestrator, pipeline, generate-confs, etc.).

All 4822 editor tests pass, arch + format + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant