Phase 5 - BYOB Backend (TASK-007A/B): - LocalSQL Adapter with full CloudStore API compatibility - QueryBuilder translates Parse-style queries to SQL - SchemaManager with PostgreSQL/Supabase export - LocalBackendServer with REST endpoints - BackendManager with IPC handlers for Electron - In-memory fallback when better-sqlite3 unavailable Phase 3 - GitHub Panel (GIT-004): - Issues tab with list/detail views - Pull Requests tab with list/detail views - GitHub API client with OAuth support - Repository info hook integration Phase 3 - Editor UX Bugfixes (TASK-013): - Legacy runtime detection banners - Read-only enforcement for legacy projects - Code editor modal close improvements - Property panel stuck state fix - Blockly node deletion and UI polish Phase 11 - Cloud Functions Planning: - Architecture documentation for workflow automation - Execution history storage schema design - Canvas overlay concept for debugging Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
5.7 KiB
TASK-001D Phase 3 Complete: Read-Only Enforcement
Date: 2026-01-13
Status: ✅ Complete
What Was Fixed
Critical Bug: Read-Only Mode Was Not Actually Enforcing!
When clicking "Open Read-Only" on a legacy project, the code was calling the right function but never actually passing the readOnly flag through the routing system. The project would open normally and be fully editable.
The Complete Fix
1. Added readOnly to Routing Interface
File: packages/noodl-editor/src/editor/src/pages/AppRouter.ts
export interface AppRouteOptions {
to: string;
from?: string;
uri?: string;
project?: ProjectModel;
readOnly?: boolean; // NEW: Flag to open project in read-only mode
}
2. Added _isReadOnly Property to ProjectModel
File: packages/noodl-editor/src/editor/src/models/projectmodel.ts
export class ProjectModel extends Model {
public _retainedProjectDirectory?: string;
public _isReadOnly?: boolean; // NEW: Flag for read-only mode (legacy projects)
public settings?: ProjectSettings;
// ...
}
3. Router Passes readOnly Flag and Sets on ProjectModel
File: packages/noodl-editor/src/editor/src/router.tsx
if (args.project && ProjectModel.instance !== args.project) {
ProjectModel.instance = args.project;
// Set read-only mode if specified (for legacy projects)
if (args.readOnly !== undefined) {
args.project._isReadOnly = args.readOnly;
}
}
// Routes
if (args.to === 'editor') {
this.setState({
route: EditorPage,
routeArgs: { route, readOnly: args.readOnly } // Pass through
});
}
4. ProjectsPage Passes readOnly: true When Opening Legacy Projects
File: packages/noodl-editor/src/editor/src/pages/ProjectsPage/ProjectsPage.tsx
const handleOpenReadOnly = useCallback(
async (projectId: string) => {
// ... load project ...
tracker.track('Legacy Project Opened Read-Only', {
projectName: project.name
});
// Open the project in read-only mode
props.route.router.route({ to: 'editor', project: loaded, readOnly: true });
},
[props.route]
);
5. NodeGraphContext Detects and Applies Read-Only Mode
File: packages/noodl-editor/src/editor/src/contexts/NodeGraphContext/NodeGraphContext.tsx
// Detect and apply read-only mode from ProjectModel
useEffect(() => {
if (!nodeGraph) return;
const eventGroup = {};
// Apply read-only mode when project instance changes
const updateReadOnlyMode = () => {
const isReadOnly = ProjectModel.instance?._isReadOnly || false;
nodeGraph.setReadOnly(isReadOnly);
};
// Listen for project changes
EventDispatcher.instance.on('ProjectModel.instanceHasChanged', updateReadOnlyMode, eventGroup);
// Apply immediately if project is already loaded
updateReadOnlyMode();
return () => {
EventDispatcher.instance.off(eventGroup);
};
}, [nodeGraph]);
The Complete Flow
- User clicks "Open Read-Only" on legacy project card
- ProjectsPage.handleOpenReadOnly() loads project and calls:
props.route.router.route({ to: 'editor', project: loaded, readOnly: true }); - Router.route() receives
readOnly: trueand:- Sets
ProjectModel.instance._isReadOnly = true - Passes
readOnly: trueto EditorPage
- Sets
- EventDispatcher fires
'ProjectModel.instanceHasChanged'event - NodeGraphContext hears the event and:
- Checks
ProjectModel.instance._isReadOnly - Calls
nodeGraph.setReadOnly(true)
- Checks
- NodeGraphEditor.setReadOnly() (already implemented):
- Sets
this.readOnly = true - Calls
this.renderEditorBanner()to show warning banner - Banner appears with "Migrate Now" and "Learn More" buttons
- Sets
- Existing readOnly checks throughout NodeGraphEditor prevent:
- Adding/deleting nodes
- Creating/removing connections
- Editing properties
- Copy/paste/cut operations
- Undo/redo
Files Modified
packages/noodl-editor/src/editor/src/pages/AppRouter.ts- Added readOnly to interfacepackages/noodl-editor/src/editor/src/models/projectmodel.ts- Added _isReadOnly propertypackages/noodl-editor/src/editor/src/router.tsx- Pass and apply readOnly flagpackages/noodl-editor/src/editor/src/pages/ProjectsPage/ProjectsPage.tsx- Pass readOnly=truepackages/noodl-editor/src/editor/src/contexts/NodeGraphContext/NodeGraphContext.tsx- Detect and apply
Testing Required
Before marking completely done, test with a legacy React 17 project:
- ✅ Open legacy project → see alert
- ✅ Choose "Open Read-Only"
- ✅ Banner should appear at top of editor
- ✅ Editing should be blocked (cannot add nodes, make connections, etc.)
- ✅ Close project, return to launcher
- ✅ Legacy badge should still show on project card
- ✅ Restart editor
- ✅ Legacy badge should persist (runtime info cached)
Next Steps
Phase 4: Wire "Migrate Now" Button
Currently shows placeholder toast. Need to:
- Import and render MigrationWizard dialog
- Pass project path and name
- Handle completion/cancellation
- Refresh project list after migration
Phase 5: Runtime Info Persistence
The runtime detection works but results aren't saved to disk, so:
- Detection re-runs every time
- Badge disappears after closing project
- Need to persist runtime info in project metadata or local storage
Notes
- The existing
readOnlychecks in NodeGraphEditor already block most operations - The banner system from Phase 2 works perfectly
- The routing system cleanly passes the flag through all layers
- EventDispatcher pattern ensures NodeGraphContext stays in sync with ProjectModel
- No breaking changes -
readOnlyis optional everywhere