diff --git a/.clineignore b/.clineignore new file mode 100644 index 0000000..1a58f5a --- /dev/null +++ b/.clineignore @@ -0,0 +1,153 @@ +# ============================================================================= +# OpenNoodl .clineignore +# ============================================================================= +# This file tells Cline which folders/files to ignore when indexing the codebase. +# Place this file at the root of the OpenNoodl repository. +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Specific heavy file locations that Cline doesn't need +# ----------------------------------------------------------------------------- + +packages/noodl-editor/src/assets/ +packages/noodl-core-ui/src/assets/ +packages/noodl-editor/build/icons/ +packages/noodl-editor/src/editor/parse-dashboard-public/ +packages/noodl-editor/src/assets/ +packages/noodl-editor/tests/testfs/ +packages/noodl-editor/tests/recordings/ + +# ----------------------------------------------------------------------------- +# Dependencies (MASSIVE - always ignore) +# ----------------------------------------------------------------------------- +node_modules/ +**/node_modules/ + +# ----------------------------------------------------------------------------- +# Build & Distribution Output +# ----------------------------------------------------------------------------- +build/ +dist/ +**/build/ +**/dist/ +publish/ +**/publish/ +bundles/ +**/bundles/ + +# ----------------------------------------------------------------------------- +# External Dependencies (Parse Dashboard, etc.) +# ----------------------------------------------------------------------------- +deps/ + +# ----------------------------------------------------------------------------- +# Git +# ----------------------------------------------------------------------------- +.git/ + +# ----------------------------------------------------------------------------- +# Compiled/Bundled JavaScript (not source code) +# ----------------------------------------------------------------------------- +*.bundle.js +*.min.js +*.min.css +**/*.bundle.js +**/*.min.js + +# Specific bundled/minified files +packages/noodl-viewer-react/static/ +packages/noodl-editor/src/editor/parse-dashboard-public/ + +# ----------------------------------------------------------------------------- +# Static Assets (images, fonts, etc.) +# ----------------------------------------------------------------------------- +**/assets/fonts/ +**/assets/images/ +**/public/fonts/ +**/public/images/ +**/*.png +**/*.jpg +**/*.jpeg +**/*.gif +**/*.ico +**/*.woff +**/*.woff2 +**/*.ttf +**/*.eot +**/*.svg +!packages/noodl-core-ui/src/**/*.svg + +# ----------------------------------------------------------------------------- +# Test Artifacts +# ----------------------------------------------------------------------------- +coverage/ +**/coverage/ +**/__snapshots__/ +*.snap + +# ----------------------------------------------------------------------------- +# IDE & Editor Configs (not needed for code understanding) +# ----------------------------------------------------------------------------- +.idea/ +.vscode/ +*.code-workspace + +# ----------------------------------------------------------------------------- +# OS Generated Files +# ----------------------------------------------------------------------------- +.DS_Store +Thumbs.db +*.log + +# ----------------------------------------------------------------------------- +# Temporary & Cache Files +# ----------------------------------------------------------------------------- +.cache/ +**/.cache/ +*.tmp +*.temp +.eslintcache +.prettiercache + +# ----------------------------------------------------------------------------- +# Environment & Secrets +# ----------------------------------------------------------------------------- +.env +.env.* +*.pem +*.key + +# ----------------------------------------------------------------------------- +# Electron Build Artifacts +# ----------------------------------------------------------------------------- +packages/noodl-editor/release/ +packages/noodl-editor/out/ + +# ----------------------------------------------------------------------------- +# Storybook Build Output +# ----------------------------------------------------------------------------- +storybook-static/ +**/storybook-static/ + +# ----------------------------------------------------------------------------- +# Generated Type Declarations (if separate from source) +# ----------------------------------------------------------------------------- +**/*.d.ts.map + +# ----------------------------------------------------------------------------- +# Lock Files (package structure is in package.json) +# ----------------------------------------------------------------------------- +package-lock.json +yarn.lock +pnpm-lock.yaml + +# ----------------------------------------------------------------------------- +# Miscellaneous Large/Unneeded Files +# ----------------------------------------------------------------------------- +*.dmg +*.exe +*.AppImage +*.deb +*.rpm +*.zip +*.tar.gz diff --git a/dev-docs/.clinerules b/dev-docs/.clinerules new file mode 100644 index 0000000..8709930 --- /dev/null +++ b/dev-docs/.clinerules @@ -0,0 +1,601 @@ +# Cline Development Guidelines for OpenNoodl + +## Overview + +This document provides guidelines for AI-assisted development on the OpenNoodl codebase using Cline in VSCode. Follow these guidelines to ensure consistent, well-documented, and testable contributions. + +--- + +## 1. Before Starting Any Task + +### 1.1 Understand the Context + +```bash +# Always check which branch you're on +git branch + +# Check for uncommitted changes +git status + +# Review recent commits +git log --oneline -10 +``` + +### 1.2 Read Relevant Documentation + +Before modifying any file, understand its purpose: + +1. Check for README files in the package +2. Read JSDoc comments on functions +3. Look for related test files +4. Search for usage patterns: `grep -r "functionName" packages/` + +### 1.3 Identify Dependencies + +```bash +# Check what imports a file +grep -r "from.*filename" packages/ + +# Check what the file imports +head -50 path/to/file.ts | grep "import" +``` + +--- + +## 2. Code Style Requirements + +### 2.1 TypeScript Standards + +```typescript +// ✅ GOOD: Explicit types +interface NodeProps { + id: string; + type: NodeType; + connections: Connection[]; +} + +function processNode(node: NodeProps): ProcessedNode { + // ... +} + +// ❌ BAD: Implicit any +function processNode(node) { + // ... +} + +// ❌ BAD: Using TSFixme +function processNode(node: TSFixme): TSFixme { + // ... +} +``` + +### 2.2 React Component Standards + +```tsx +// ✅ GOOD: Functional component with types +interface ButtonProps { + label: string; + onClick: () => void; + disabled?: boolean; +} + +export function Button({ label, onClick, disabled = false }: ButtonProps) { + return ( + + ); +} + +// ❌ BAD: Class component (unless necessary for lifecycle) +class Button extends React.Component { + // ... +} +``` + +### 2.3 Import Organization + +```typescript +// 1. External packages (alphabetical) +import classNames from 'classnames'; +import React, { useCallback, useState } from 'react'; + +// 2. Internal packages (alphabetical by alias) +import { IconName } from '@noodl-core-ui/components/common/Icon'; +import { NodeGraphModel } from '@noodl-models/nodegraphmodel'; +import { KeyCode } from '@noodl-utils/keyboard/KeyCode'; + +// 3. Relative imports (by depth, then alphabetical) +import { localHelper } from './helpers'; +import css from './Component.module.scss'; +``` + +### 2.4 Naming Conventions + +| Type | Convention | Example | +|------|------------|---------| +| Components | PascalCase | `NodeEditor.tsx` | +| Hooks | camelCase, use prefix | `useNodeSelection.ts` | +| Utils | camelCase | `formatNodeName.ts` | +| Constants | UPPER_SNAKE | `MAX_CONNECTIONS` | +| CSS Modules | kebab-case | `node-editor.module.scss` | +| Test files | Same + .test | `NodeEditor.test.tsx` | + +--- + +## 3. Documentation Requirements + +### 3.1 File Headers + +Every new file should have a header comment: + +```typescript +/** + * NodeProcessor + * + * Handles the processing of node graph updates and manages + * the execution order of connected nodes. + * + * @module noodl-runtime + * @since 1.2.0 + */ +``` + +### 3.2 Function Documentation + +```typescript +/** + * Processes a node and propagates changes to connected nodes. + * + * @param node - The node to process + * @param context - The execution context + * @param options - Processing options + * @param options.force - Force re-evaluation even if inputs unchanged + * @returns The processed output values + * @throws {NodeProcessingError} If the node definition is invalid + * + * @example + * ```typescript + * const output = processNode(myNode, context, { force: true }); + * console.log(output.value); + * ``` + */ +function processNode( + node: NodeInstance, + context: ExecutionContext, + options: ProcessOptions = {} +): NodeOutput { + // ... +} +``` + +### 3.3 Complex Logic Comments + +```typescript +// Calculate the topological sort order for node evaluation. +// This ensures nodes are processed after their dependencies. +// Uses Kahn's algorithm for O(V+E) complexity. +const sortedNodes = topologicalSort(nodes, connections); +``` + +--- + +## 4. Testing Requirements + +### 4.1 Test File Location + +Tests should be co-located or in a parallel `tests/` directory: + +``` +// Option A: Co-located +components/ +├── Button/ +│ ├── Button.tsx +│ ├── Button.test.tsx +│ └── Button.module.scss + +// Option B: Parallel (current pattern in noodl-editor) +packages/noodl-editor/ +├── src/ +│ └── components/Button.tsx +└── tests/ + └── components/Button.test.ts +``` + +### 4.2 Test Structure + +```typescript +import { describe, it, expect, beforeEach, jest } from '@jest/globals'; +import { renderHook, act } from '@testing-library/react-hooks'; + +describe('useNodeSelection', () => { + // Setup + let mockContext: NodeGraphContext; + + beforeEach(() => { + mockContext = createMockContext(); + }); + + // Group related tests + describe('when selecting a single node', () => { + it('should update selection state', () => { + const { result } = renderHook(() => useNodeSelection(mockContext)); + + act(() => { + result.current.selectNode('node-1'); + }); + + expect(result.current.selectedNodes).toContain('node-1'); + }); + + it('should clear previous selection by default', () => { + // ... + }); + }); + + describe('when multi-selecting nodes', () => { + // ... + }); +}); +``` + +### 4.3 What to Test + +| Priority | What to Test | +|----------|--------------| +| High | Utility functions | +| High | Data transformations | +| High | State management logic | +| Medium | React hooks | +| Medium | Component behavior | +| Low | Pure UI rendering | + +--- + +## 5. Git Workflow + +### 5.1 Branch Naming + +```bash +# Features +git checkout -b feature/add-vercel-deployment + +# Bug fixes +git checkout -b fix/page-router-scroll + +# Refactoring +git checkout -b refactor/remove-tsfixme-panels + +# Documentation +git checkout -b docs/update-node-api +``` + +### 5.2 Commit Messages + +Follow conventional commits: + +```bash +# Format: type(scope): description + +# Features +git commit -m "feat(editor): add breakpoint support for node connections" + +# Bug fixes +git commit -m "fix(viewer): resolve scroll position reset in nested Page Router" + +# Refactoring +git commit -m "refactor(runtime): replace TSFixme with proper types in node processor" + +# Documentation +git commit -m "docs(api): add JSDoc to all public node methods" + +# Tests +git commit -m "test(editor): add unit tests for node selection hook" + +# Chores +git commit -m "chore(deps): update react to 19.0.0" +``` + +### 5.3 Commit Frequency + +- Commit after each logical change +- Don't combine unrelated changes +- Commit working states (tests should pass) + +--- + +## 6. Codebase Navigation + +### 6.1 Key Directories + +``` +packages/ +├── noodl-editor/ +│ ├── src/ +│ │ ├── editor/src/ +│ │ │ ├── models/ # Data models (ProjectModel, NodeGraph, etc.) +│ │ │ ├── views/ # UI components and views +│ │ │ ├── utils/ # Helper utilities +│ │ │ ├── store/ # State stores (AI Assistant, etc.) +│ │ │ └── pages/ # Page-level components +│ │ ├── main/ # Electron main process +│ │ └── shared/ # Shared utilities +│ └── tests/ # Test files +│ +├── noodl-runtime/ +│ └── src/ +│ ├── nodes/ # Runtime node definitions +│ └── nodecontext.js # Execution context +│ +├── noodl-viewer-react/ +│ └── src/ +│ └── nodes/ # React-based visual nodes +│ +└── noodl-core-ui/ + └── src/ + └── components/ # Shared UI components +``` + +### 6.2 Finding Things + +```bash +# Find a component +find packages/ -name "*NodeEditor*" -type f + +# Find where something is imported +grep -r "import.*from.*NodeEditor" packages/ + +# Find where a function is called +grep -r "processNode(" packages/ --include="*.ts" --include="*.tsx" + +# Find all TODO comments +grep -rn "TODO\|FIXME" packages/noodl-editor/src + +# Find test files +find packages/ -name "*.test.ts" -o -name "*.spec.ts" +``` + +### 6.3 Understanding Data Flow + +1. **User Action** → `views/` components capture events +2. **State Update** → `models/` handle business logic +3. **Runtime Sync** → `ViewerConnection` sends to preview +4. **Persistence** → `ProjectModel` saves to disk + +--- + +## 7. Common Patterns + +### 7.1 Event Handling Pattern + +```typescript +// Models use EventDispatcher for pub/sub +import { EventDispatcher } from '../../../shared/utils/EventDispatcher'; + +class MyModel extends EventDispatcher { + doSomething() { + // ... logic + this.notifyListeners('updated', { data: result }); + } +} + +// Usage +const model = new MyModel(); +model.on('updated', (data) => { + console.log('Model updated:', data); +}); +``` + +### 7.2 React Hook Pattern + +```typescript +// Custom hook for model subscription +function useModel(model: EventDispatcher, event: string): T { + const [state, setState] = useState(model.getState()); + + useEffect(() => { + const handler = (newState: T) => setState(newState); + model.on(event, handler); + return () => model.off(event, handler); + }, [model, event]); + + return state; +} +``` + +### 7.3 Node Definition Pattern + +```javascript +// In noodl-runtime/src/nodes/ +const MyNode = { + name: 'My.Custom.Node', + displayName: 'My Custom Node', + category: 'Custom', + + inputs: { + inputValue: { + type: 'string', + displayName: 'Input Value', + default: '' + } + }, + + outputs: { + outputValue: { + type: 'string', + displayName: 'Output Value' + } + }, + + methods: { + setInputValue(value) { + this._internal.inputValue = value; + this.flagOutputDirty('outputValue'); + } + }, + + getOutputValue(name) { + if (name === 'outputValue') { + return this._internal.inputValue.toUpperCase(); + } + } +}; +``` + +--- + +## 8. Error Handling + +### 8.1 User-Facing Errors + +```typescript +import { ToastLayer } from '../views/ToastLayer/ToastLayer'; + +try { + await riskyOperation(); +} catch (error) { + // Log for debugging + console.error('Operation failed:', error); + + // Show user-friendly message + ToastLayer.showError('Unable to complete operation. Please try again.'); +} +``` + +### 8.2 Developer Errors + +```typescript +// Use assertions for developer errors +function processNode(node: NodeInstance) { + if (!node.id) { + throw new Error(`processNode: node.id is required`); + } + + if (!node.definition) { + throw new Error(`processNode: node "${node.id}" has no definition`); + } +} +``` + +### 8.3 Graceful Degradation + +```typescript +function getNodeIcon(node: NodeInstance): string { + try { + return node.definition.icon || 'default-icon'; + } catch { + console.warn(`Could not get icon for node ${node.id}`); + return 'default-icon'; + } +} +``` + +--- + +## 9. Performance Considerations + +### 9.1 Avoid Unnecessary Re-renders + +```tsx +// ✅ GOOD: Memoized callback +const handleClick = useCallback(() => { + onNodeSelect(node.id); +}, [node.id, onNodeSelect]); + +// ✅ GOOD: Memoized expensive computation +const sortedNodes = useMemo(() => { + return topologicalSort(nodes); +}, [nodes]); + +// ❌ BAD: New function on every render + + ); +} + +// ❌ DON'T: Class components (unless lifecycle methods required) +class Button extends React.Component { + render() { + return ; + } +} +``` + +### Hooks Usage + +```typescript +// ✅ DO: Proper hook dependencies +const handleChange = useCallback((value: string) => { + onChange(value); + onValidate?.(value); +}, [onChange, onValidate]); + +// ✅ DO: Cleanup in effects +useEffect(() => { + const handler = (e: Event) => { /* ... */ }; + window.addEventListener('resize', handler); + return () => window.removeEventListener('resize', handler); +}, []); + +// ❌ DON'T: Missing dependencies +const handleChange = useCallback((value: string) => { + onChange(value); // onChange not in deps! +}, []); +``` + +### Component Organization + +```typescript +// Component file structure +import React, { useState, useCallback, useEffect } from 'react'; + +// External imports +import classNames from 'classnames'; + +// Internal imports +import { Icon, IconName } from '@noodl-core-ui/components/common/Icon'; +import { useModel } from '@noodl-utils/hooks'; + +// Relative imports +import { ButtonProps } from './types'; +import { validateInput } from './utils'; + +// Styles last +import css from './Button.module.scss'; + +// Types (if not in separate file) +interface LocalState { + isHovered: boolean; +} + +// Component +export function Button({ label, onClick, variant = 'primary' }: ButtonProps) { + // Hooks first + const [state, setState] = useState({ isHovered: false }); + const model = useModel(SomeModel.instance); + + // Callbacks + const handleClick = useCallback(() => { + onClick(); + }, [onClick]); + + // Effects + useEffect(() => { + // Setup + return () => { + // Cleanup + }; + }, []); + + // Render helpers + const buttonClass = classNames(css.Button, css[variant]); + + // Render + return ( + + ); +} +``` + +## File Organization + +### Directory Structure + +``` +feature/ +├── index.ts # Public exports only +├── FeatureName.tsx # Main component +├── FeatureName.module.scss +├── FeatureName.test.ts +├── types.ts # Type definitions +├── utils.ts # Helper functions +└── hooks.ts # Custom hooks (if any) +``` + +### Index Files (Barrel Exports) + +```typescript +// index.ts - Export only public API +export { FeatureName } from './FeatureName'; +export type { FeatureNameProps } from './types'; + +// DON'T export internal utilities +``` + +### Import Order + +```typescript +// 1. React +import React, { useState, useEffect } from 'react'; + +// 2. External packages (alphabetical) +import classNames from 'classnames'; +import { motion } from 'framer-motion'; + +// 3. Internal packages (alphabetical by alias) +import { Icon } from '@noodl-core-ui/components/common/Icon'; +import { NodeGraphModel } from '@noodl-models/nodegraphmodel'; +import { guid } from '@noodl-utils/utils'; + +// 4. Relative imports (parent first, then siblings) +import { ParentComponent } from '../ParentComponent'; +import { SiblingComponent } from './SiblingComponent'; +import { localHelper } from './utils'; + +// 5. Types (if separate import needed) +import type { MyComponentProps } from './types'; + +// 6. Styles +import css from './MyComponent.module.scss'; +``` + +## Documentation Standards + +### JSDoc for Public APIs + +```typescript +/** + * Processes a node and returns the computed result. + * + * @param node - The node to process + * @param options - Processing options + * @returns The computed result with output values + * + * @example + * ```typescript + * const result = processNode(myNode, { validate: true }); + * console.log(result.outputs); + * ``` + */ +export function processNode( + node: NodeGraphNode, + options: ProcessOptions = {} +): ProcessResult { + // Implementation +} +``` + +### File Headers + +```typescript +/** + * NodeGraphModel - Manages the structure of a node graph. + * + * This model handles: + * - Node creation and deletion + * - Connection management + * - Graph traversal + * + * @module models/NodeGraphModel + */ +``` + +### Inline Comments + +```typescript +// ✅ DO: Explain "why", not "what" +// We batch updates here to prevent cascading re-renders +// when multiple inputs change in the same frame +this.scheduleAfterInputsHaveUpdated(() => { + this.processAllInputs(); +}); + +// ❌ DON'T: State the obvious +// Loop through items +for (const item of items) { + // Process item + process(item); +} +``` + +## Naming Conventions + +### Files + +| Type | Convention | Example | +|------|------------|---------| +| React Component | PascalCase | `NodePicker.tsx` | +| Utility | camelCase | `formatUtils.ts` | +| Types | camelCase or PascalCase | `types.ts` or `NodeTypes.ts` | +| Test | Match source + `.test` | `NodePicker.test.ts` | +| Styles | Match component + `.module` | `NodePicker.module.scss` | + +### Code + +```typescript +// Constants: UPPER_SNAKE_CASE +const MAX_RETRY_COUNT = 3; +const DEFAULT_TIMEOUT_MS = 5000; + +// Functions/Methods: camelCase +function processNodeGraph() {} +function calculateOffset() {} + +// Classes/Interfaces/Types: PascalCase +class NodeGraphModel {} +interface ProcessOptions {} +type NodeColor = 'data' | 'logic'; + +// Private members: underscore prefix +class MyClass { + private _internalState: State; + private _processInternal(): void {} +} + +// Boolean variables: is/has/should prefix +const isEnabled = true; +const hasChildren = node.children.length > 0; +const shouldUpdate = isDirty && isVisible; +``` + +## Error Handling + +```typescript +// ✅ DO: Specific error types +class NodeNotFoundError extends Error { + constructor(nodeId: string) { + super(`Node not found: ${nodeId}`); + this.name = 'NodeNotFoundError'; + } +} + +// ✅ DO: Handle errors gracefully +async function fetchData(): Promise { + try { + const response = await api.fetch(); + return { success: true, data: response }; + } catch (error) { + console.error('Failed to fetch data:', error); + return { success: false, error: getErrorMessage(error) }; + } +} + +// ✅ DO: Type-safe error messages +function getErrorMessage(error: unknown): string { + if (error instanceof Error) return error.message; + return String(error); +} +``` + +## Testing Standards + +### Test File Structure + +```typescript +import { render, screen, fireEvent } from '@testing-library/react'; +import { MyComponent } from './MyComponent'; + +describe('MyComponent', () => { + // Group related tests + describe('rendering', () => { + it('should render with default props', () => { + render(); + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + }); + + describe('interactions', () => { + it('should call onClick when clicked', () => { + const onClick = jest.fn(); + render(); + + fireEvent.click(screen.getByRole('button')); + + expect(onClick).toHaveBeenCalledTimes(1); + }); + }); +}); +``` + +### Test Naming + +```typescript +// ✅ DO: Descriptive test names +it('should display error message when validation fails', () => {}); +it('should disable submit button while loading', () => {}); + +// ❌ DON'T: Vague names +it('works', () => {}); +it('test 1', () => {}); +``` + +## Git Commit Messages + +### Format + +``` +type(scope): description + +[optional body] + +[optional footer] +``` + +### Types + +- `feat`: New feature +- `fix`: Bug fix +- `refactor`: Code change that neither fixes bug nor adds feature +- `docs`: Documentation only +- `test`: Adding or updating tests +- `chore`: Build process or auxiliary tool changes + +### Examples + +``` +feat(editor): add breakpoint support to connections + +fix(runtime): resolve memory leak in collection listener + +refactor(property-panel): convert to functional component + +docs(readme): update installation instructions + +test(nodes): add unit tests for REST node +``` + +## Performance Guidelines + +### React Performance + +```typescript +// ✅ DO: Memoize expensive computations +const sortedItems = useMemo(() => { + return items.sort((a, b) => a.name.localeCompare(b.name)); +}, [items]); + +// ✅ DO: Memoize callbacks passed to children +const handleChange = useCallback((value: string) => { + onChange(value); +}, [onChange]); + +// ✅ DO: Use React.memo for pure components +export const ListItem = React.memo(function ListItem({ item }: Props) { + return
{item.name}
; +}); +``` + +### General Performance + +```typescript +// ✅ DO: Batch DOM operations +function updateNodes(nodes: Node[]) { + // Collect all changes first + const changes = nodes.map(calculateChange); + + // Apply in single batch + requestAnimationFrame(() => { + changes.forEach(applyChange); + }); +} + +// ✅ DO: Debounce frequent events +const debouncedSearch = useMemo( + () => debounce((query: string) => performSearch(query), 300), + [] +); +``` diff --git a/dev-docs/guidelines/GIT-WORKFLOW.md b/dev-docs/guidelines/GIT-WORKFLOW.md new file mode 100644 index 0000000..cba5c1b --- /dev/null +++ b/dev-docs/guidelines/GIT-WORKFLOW.md @@ -0,0 +1,332 @@ +# Git Workflow Guide + +How to manage branches, commits, and pull requests for OpenNoodl development. + +## Branch Naming + +### Format + +``` +type/id-short-description +``` + +### Types + +| Type | Use For | Example | +|------|---------|---------| +| `task` | Task documentation work | `task/001-dependency-updates` | +| `feature` | New features | `feature/vercel-deployment` | +| `fix` | Bug fixes | `fix/page-router-scroll` | +| `refactor` | Code improvements | `refactor/property-panel-hooks` | +| `docs` | Documentation only | `docs/api-reference` | +| `test` | Test additions | `test/rest-node-coverage` | + +### Examples + +```bash +# Task branches (from dev-docs) +git checkout -b task/001-dependency-updates +git checkout -b task/002-typescript-cleanup + +# Feature branches +git checkout -b feature/add-oauth-support +git checkout -b feature/multi-project-windows + +# Fix branches +git checkout -b fix/nested-router-scroll +git checkout -b fix/array-change-tracking + +# Refactor branches +git checkout -b refactor/remove-class-components +git checkout -b refactor/data-node-architecture +``` + +## Commit Messages + +### Format + +``` +type(scope): short description + +[optional longer description] + +[optional footer with references] +``` + +### Types + +- `feat` - New feature +- `fix` - Bug fix +- `refactor` - Code restructuring (no behavior change) +- `docs` - Documentation changes +- `test` - Test additions/changes +- `chore` - Build/tooling changes +- `style` - Formatting (no code change) +- `perf` - Performance improvement + +### Scopes + +Use the affected area: + +- `editor` - Main editor code +- `runtime` - Runtime engine +- `viewer` - Viewer/preview +- `ui` - Core UI components +- `build` - Build system +- `deps` - Dependencies + +### Examples + +```bash +# Features +git commit -m "feat(editor): add connection breakpoints" +git commit -m "feat(runtime): implement retry logic for REST node" + +# Fixes +git commit -m "fix(viewer): resolve scroll jumping in nested routers" +git commit -m "fix(editor): prevent crash when deleting connected node" + +# Refactoring +git commit -m "refactor(ui): convert PropertyPanel to functional component" +git commit -m "refactor(runtime): simplify collection change tracking" + +# Docs +git commit -m "docs(readme): update installation instructions" +git commit -m "docs(api): add JSDoc to public methods" + +# Tests +git commit -m "test(runtime): add unit tests for REST node" +git commit -m "test(editor): add integration tests for import flow" + +# Chores +git commit -m "chore(deps): update webpack to 5.101.3" +git commit -m "chore(build): enable source maps in development" +``` + +### Multi-line Commits + +For complex changes: + +```bash +git commit -m "feat(editor): add AI-powered node suggestions + +- Integrate with OpenAI API for code analysis +- Add suggestion UI in node picker +- Cache suggestions for performance + +Closes #123" +``` + +## Workflow + +### Starting Work + +```bash +# 1. Ensure main is up to date +git checkout main +git pull origin main + +# 2. Create your branch +git checkout -b task/001-dependency-updates + +# 3. Make your changes... + +# 4. Stage and commit frequently +git add -A +git commit -m "feat(deps): update React to v19" +``` + +### During Development + +```bash +# Check status often +git status + +# View your changes +git diff + +# Stage specific files +git add packages/noodl-editor/package.json + +# Commit logical chunks +git commit -m "fix(deps): resolve peer dependency conflicts" + +# Push to remote (first time) +git push -u origin task/001-dependency-updates + +# Push subsequent commits +git push +``` + +### Keeping Up to Date + +```bash +# If main has changed, rebase your work +git fetch origin +git rebase origin/main + +# Resolve any conflicts, then continue +git add . +git rebase --continue + +# Force push after rebase (your branch only!) +git push --force-with-lease +``` + +### Creating Pull Request + +1. Push your branch to remote +2. Go to GitHub repository +3. Click "New Pull Request" +4. Select your branch +5. Fill in the template: + +```markdown +## Summary +Brief description of changes + +## Task Reference +TASK-001: Dependency Updates + +## Changes Made +- Updated React to v19 +- Fixed peer dependency conflicts +- Migrated to createRoot API + +## Testing +- [ ] All existing tests pass +- [ ] Manual testing completed +- [ ] New tests added (if applicable) + +## Checklist +- [ ] Code follows style guidelines +- [ ] Documentation updated +- [ ] CHANGELOG.md updated +- [ ] No console.log statements +``` + +### After PR Merged + +```bash +# Switch to main +git checkout main + +# Pull the merged changes +git pull origin main + +# Delete your local branch +git branch -d task/001-dependency-updates + +# Delete remote branch (if not auto-deleted) +git push origin --delete task/001-dependency-updates +``` + +## Common Scenarios + +### Oops, Wrong Branch + +```bash +# Stash your changes +git stash + +# Switch to correct branch +git checkout correct-branch + +# Apply your changes +git stash pop +``` + +### Need to Undo Last Commit + +```bash +# Undo commit but keep changes +git reset --soft HEAD~1 + +# Undo commit and discard changes +git reset --hard HEAD~1 +``` + +### Need to Update Commit Message + +```bash +# Most recent commit +git commit --amend -m "new message" + +# Older commit (interactive rebase) +git rebase -i HEAD~3 +# Change 'pick' to 'reword' for the commit +``` + +### Accidentally Committed to Main + +```bash +# Create a branch with your commits +git branch my-feature + +# Reset main to origin +git reset --hard origin/main + +# Switch to your feature branch +git checkout my-feature +``` + +### Merge Conflicts + +```bash +# During rebase or merge, if conflicts occur: + +# 1. Open conflicted files and resolve +# Look for <<<<<<< ======= >>>>>>> markers + +# 2. Stage resolved files +git add resolved-file.ts + +# 3. Continue the rebase/merge +git rebase --continue +# or +git merge --continue +``` + +## Branch Protection + +The `main` branch has these protections: + +- Requires pull request +- Requires passing CI checks +- Requires up-to-date branch +- No force pushes allowed + +## Tips + +### Useful Aliases + +Add to your `~/.gitconfig`: + +```ini +[alias] + st = status + co = checkout + br = branch + ci = commit + lg = log --oneline --graph --all + unstage = reset HEAD -- + last = log -1 HEAD + branches = branch -a +``` + +### Before Every PR + +1. Run tests: `npm run test:editor` +2. Run type check: `npx tsc --noEmit` +3. Run linter: `npx eslint packages/ --fix` +4. Review your diff: `git diff main` +5. Check commit history: `git log --oneline main..HEAD` + +### Good Commit Hygiene + +- Commit early and often +- Each commit should be atomic (one logical change) +- Commits should compile and pass tests +- Write meaningful commit messages +- Don't commit generated files +- Don't commit debug code diff --git a/dev-docs/reference/CODEBASE-MAP.md b/dev-docs/reference/CODEBASE-MAP.md new file mode 100644 index 0000000..1e38aaa --- /dev/null +++ b/dev-docs/reference/CODEBASE-MAP.md @@ -0,0 +1,378 @@ +# OpenNoodl Codebase Quick Navigation + +## 🗺️ Package Map + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ MONOREPO ROOT │ +├─────────────────────────────────────────────────────────────────────────┤ +│ package.json → Workspace config, global scripts │ +│ lerna.json → Monorepo management │ +│ scripts/ → Build and CI scripts │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ┌───────────────────────────┼───────────────────────────┐ + ▼ ▼ ▼ +┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ +│ EDITOR (GPL) │ │ RUNTIME (MIT) │ │ UI LIBRARY │ +│ noodl-editor │ │ noodl-runtime │ │ noodl-core-ui │ +│ │ │ │ │ │ +│ • Electron app │ │ • Node engine │ │ • React components│ +│ • React UI │ │ • Data flow │ │ • Storybook │ +│ • Property panels │ │ • Event system │ │ • Styling │ +└───────────────────┘ └───────────────────┘ └───────────────────┘ + │ │ + │ ▼ + │ ┌───────────────────┐ + │ │ VIEWER (MIT) │ + │ │ noodl-viewer-react│ + │ │ │ + │ │ • React runtime │ + │ │ • Visual nodes │ + │ │ • DOM handling │ + │ └───────────────────┘ + │ + ▼ +┌───────────────────────────────────────────────────────────────────────┐ +│ PLATFORM LAYER │ +├───────────────────┬───────────────────┬───────────────────────────────┤ +│ noodl-platform │ platform-electron │ platform-node │ +│ (abstraction) │ (desktop impl) │ (server impl) │ +└───────────────────┴───────────────────┴───────────────────────────────┘ +``` + +--- + +## 📁 Key Directories + +### noodl-editor (Main Application) + +``` +packages/noodl-editor/src/ +├── editor/src/ +│ ├── models/ # 🎯 Business logic & data +│ │ ├── projectmodel.ts → Project state +│ │ ├── nodegraphmodel.ts → Graph structure +│ │ ├── componentmodel.ts → Components +│ │ ├── nodelibrary/ → Node type registry +│ │ ├── AiAssistant/ → AI features +│ │ └── sidebar/ → Sidebar state +│ │ +│ ├── views/ # 🖥️ UI components +│ │ ├── nodegrapheditor.ts → Canvas/graph editor +│ │ ├── panels/ → Property panels +│ │ ├── NodePicker/ → Node creation UI +│ │ ├── documents/ → Document views +│ │ └── popups/ → Modal dialogs +│ │ +│ ├── utils/ # 🔧 Utilities +│ │ ├── CodeEditor/ → Monaco integration +│ │ ├── filesystem.ts → File operations +│ │ └── projectimporter.js → Import/export +│ │ +│ ├── store/ # 💾 Persistent state +│ │ └── AiAssistantStore.ts → AI settings +│ │ +│ └── pages/ # 📄 Page components +│ └── EditorPage/ → Main editor page +│ +├── main/ # ⚡ Electron main process +│ └── main.js → App entry point +│ +└── shared/ # 🔗 Shared utilities + └── utils/ + └── EventDispatcher.ts → Pub/sub system +``` + +### noodl-runtime (Execution Engine) + +``` +packages/noodl-runtime/ +├── src/ +│ ├── nodes/ # 📦 Node implementations +│ │ └── std-library/ +│ │ ├── data/ → Data nodes (REST, DB, etc.) +│ │ ├── logic/ → Logic nodes +│ │ └── events/ → Event nodes +│ │ +│ ├── node.js # Base node class +│ ├── nodedefinition.js # Node definition API +│ ├── noderegister.js # Node registry +│ ├── nodescope.js # Component scope +│ └── nodecontext.js # Runtime context +│ +└── noodl-runtime.js # Main runtime entry +``` + +### noodl-viewer-react (React Runtime) + +``` +packages/noodl-viewer-react/src/ +├── nodes/ # 🎨 Visual nodes +│ ├── basic/ → Group, Text, Image +│ ├── controls/ → Button, Input, Checkbox +│ ├── navigation/ → PageRouter, Page +│ └── std-library/ → Standard library nodes +│ +└── react-component-node.js # React node wrapper +``` + +### noodl-core-ui (Component Library) + +``` +packages/noodl-core-ui/src/ +├── components/ +│ ├── common/ # 🧩 Basic components +│ │ ├── Icon/ +│ │ └── ActivityIndicator/ +│ │ +│ ├── inputs/ # 📝 Form controls +│ │ ├── TextInput/ +│ │ ├── PrimaryButton/ +│ │ └── Checkbox/ +│ │ +│ ├── layout/ # 📐 Layout components +│ │ ├── Box/ +│ │ ├── Container/ +│ │ └── Tabs/ +│ │ +│ ├── popups/ # 💬 Dialogs & menus +│ │ ├── MenuDialog/ +│ │ └── PopupToolbar/ +│ │ +│ └── ai/ # 🤖 AI UI components +│ ├── AiChatBox/ +│ └── AiChatMessage/ +│ +└── styles/ # 🎨 Global styles +``` + +--- + +## 🔍 Finding Things + +### Search Patterns + +```bash +# Find a file by name +find packages/ -name "*NodeGraph*" -type f + +# Find where a function is defined +grep -rn "function processNode" packages/ + +# Find where something is imported/used +grep -r "import.*from.*nodegraphmodel" packages/ + +# Find all usages of a component +grep -r "; +} + +interface Connection { + fromId: string; + fromPort: string; + toId: string; + toPort: string; +} + +// Component structure (componentmodel.ts) +interface ComponentModel { + name: string; + graph: NodeGraphModel; + metadata: Record; +} +``` + +--- + +## 📝 Path Aliases + +```typescript +// Configured in tsconfig.json +@noodl-models/ → packages/noodl-editor/src/editor/src/models/ +@noodl-utils/ → packages/noodl-editor/src/editor/src/utils/ +@noodl-contexts/ → packages/noodl-editor/src/editor/src/contexts/ +@noodl-hooks/ → packages/noodl-editor/src/editor/src/hooks/ +@noodl-constants/ → packages/noodl-editor/src/editor/src/constants/ +@noodl-core-ui/ → packages/noodl-core-ui/src/ +@noodl/platform → packages/noodl-platform/src/ +``` + +--- + +## 🚨 Common Issues + +### Build Problems + +```bash +# Clear caches +rm -rf node_modules/.cache +rm -rf packages/*/node_modules/.cache + +# Reinstall dependencies +rm -rf node_modules +npm install +``` + +### TypeScript Errors + +```bash +# Check for circular dependencies +npx madge --circular packages/noodl-editor/src +``` + +### Electron Issues + +```bash +# Clear app data (macOS) +rm -rf ~/Library/Application\ Support/OpenNoodl/ + +# Rebuild native modules +npm run rebuild +``` + +--- + +*Quick reference card for OpenNoodl development. Print or pin to your IDE!* diff --git a/dev-docs/reference/COMMON-ISSUES.md b/dev-docs/reference/COMMON-ISSUES.md new file mode 100644 index 0000000..347ad91 --- /dev/null +++ b/dev-docs/reference/COMMON-ISSUES.md @@ -0,0 +1,253 @@ +# Common Issues & Troubleshooting + +Solutions to frequently encountered problems when developing OpenNoodl. + +## Build Issues + +### "Module not found" Errors + +**Symptom**: Build fails with `Cannot find module '@noodl-xxx/...'` + +**Solutions**: +1. Run `npm install` from root directory +2. Check if package exists in `packages/` +3. Verify tsconfig paths are correct +4. Try: `rm -rf node_modules && npm install` + +### "Peer dependency" Warnings + +**Symptom**: npm install shows peer dependency warnings + +**Solutions**: +1. Check if versions are compatible +2. Update the conflicting package +3. Last resort: `npm install --legacy-peer-deps` +4. Document why in CHANGELOG.md + +### TypeScript Errors After Update + +**Symptom**: Types that worked before now fail + +**Solutions**: +1. Run `npx tsc --noEmit` to see all errors +2. Check if `@types/*` packages need updating +3. Look for breaking changes in updated packages +4. Check `tsconfig.json` for configuration issues + +### Webpack Build Hangs + +**Symptom**: Build starts but never completes + +**Solutions**: +1. Check for circular imports: `npx madge --circular packages/` +2. Increase Node memory: `NODE_OPTIONS=--max_old_space_size=4096` +3. Check for infinite loops in build scripts +4. Try building individual packages + +## Runtime Issues + +### Hot Reload Not Working + +**Symptom**: Changes don't appear without full restart + +**Solutions**: +1. Check webpack dev server is running +2. Verify file is being watched (check webpack config) +3. Clear browser cache +4. Check for syntax errors preventing reload +5. Restart dev server: `npm run dev` + +### Node Not Appearing in Picker + +**Symptom**: Created a node but it doesn't show up + +**Solutions**: +1. Verify node is exported in `nodelibraryexport.js` +2. Check `category` is valid +3. Verify no JavaScript errors in node definition +4. Restart the editor + +### "Cannot read property of undefined" + +**Symptom**: Runtime error accessing object properties + +**Solutions**: +1. Add null checks: `obj?.property` +2. Verify data is loaded before access +3. Check async timing issues +4. Add defensive initialization + +### State Not Updating + +**Symptom**: Changed input but output doesn't update + +**Solutions**: +1. Verify `flagOutputDirty()` is called +2. Check if batching is interfering +3. Verify connection exists in graph +4. Check for conditional logic preventing update + +## Editor Issues + +### Preview Not Loading + +**Symptom**: Preview panel is blank or shows error + +**Solutions**: +1. Check browser console for errors +2. Verify viewer runtime is built +3. Check for JavaScript errors in project +4. Try creating a new empty project + +### Property Panel Empty + +**Symptom**: Selected node but no properties shown + +**Solutions**: +1. Verify node has `inputs` defined +2. Check `group` values are set +3. Look for errors in property panel code +4. Verify node type is registered + +### Canvas Performance Issues + +**Symptom**: Node graph is slow/laggy + +**Solutions**: +1. Reduce number of visible nodes +2. Check for expensive render operations +3. Verify no infinite update loops +4. Profile with Chrome DevTools + +## Git Issues + +### Merge Conflicts in package-lock.json + +**Symptom**: Complex conflicts in lock file + +**Solutions**: +1. Accept either version +2. Run `npm install` to regenerate +3. Commit the regenerated lock file + +### Large File Warnings + +**Symptom**: Git warns about large files + +**Solutions**: +1. Check `.gitignore` includes build outputs +2. Verify `node_modules` not committed +3. Use Git LFS for large assets if needed + +## Testing Issues + +### Tests Timeout + +**Symptom**: Tests hang or timeout + +**Solutions**: +1. Check for unresolved promises +2. Verify mocks are set up correctly +3. Increase timeout if legitimately slow +4. Check for infinite loops + +### Snapshot Tests Failing + +**Symptom**: Snapshot doesn't match + +**Solutions**: +1. Review the diff carefully +2. If change is intentional: `npm test -- -u` +3. If unexpected, investigate component changes + +## Debugging Tips + +### Enable Verbose Logging + +```javascript +// Add to see more info +console.log('[DEBUG]', variable); + +// For node execution +this.context.debugLog('Message', data); +``` + +### Use Chrome DevTools + +1. Open editor +2. Press `Cmd+Option+I` (Mac) or `Ctrl+Shift+I` (Windows) +3. Check Console for errors +4. Use Sources for breakpoints +5. Use Network for API issues + +### Inspect Node State + +```javascript +// In browser console +const node = NoodlRuntime.instance.getNodeById('node-id'); +console.log(node._internal); +``` + +### Check Event Flow + +```javascript +// Add listener to see all events +model.on('*', (event, data) => { + console.log('Event:', event, data); +}); +``` + +## Error Messages + +### "Maximum call stack size exceeded" + +**Cause**: Infinite recursion or circular dependency + +**Fix**: +1. Check for circular imports +2. Add base case to recursive functions +3. Break dependency cycles + +### "Cannot access before initialization" + +**Cause**: Temporal dead zone with `let`/`const` + +**Fix**: +1. Check import order +2. Move declaration before usage +3. Check for circular imports + +### "Unexpected token" + +**Cause**: Syntax error or wrong file type + +**Fix**: +1. Check file extension matches content +2. Verify JSON is valid +3. Check for missing brackets/quotes + +### "ENOENT: no such file or directory" + +**Cause**: Missing file or wrong path + +**Fix**: +1. Verify file exists +2. Check path is correct (case-sensitive) +3. Ensure build step completed + +## Getting Help + +1. Search this document first +2. Check existing task documentation +3. Search codebase for similar patterns +4. Check GitHub issues +5. Ask in community channels + +## Contributing Solutions + +Found a solution not listed here? Add it! + +1. Edit this file +2. Follow the format: Symptom → Solutions +3. Include specific commands when helpful +4. Submit PR with your addition diff --git a/dev-docs/reference/NODE-PATTERNS.md b/dev-docs/reference/NODE-PATTERNS.md new file mode 100644 index 0000000..3dcec71 --- /dev/null +++ b/dev-docs/reference/NODE-PATTERNS.md @@ -0,0 +1,446 @@ +# Node Patterns Reference + +How to create and modify nodes in OpenNoodl. + +## Node Types + +There are two main types of nodes: + +1. **Runtime Nodes** (`noodl-runtime`) - Logic, data, utilities +2. **Visual Nodes** (`noodl-viewer-react`) - React components for UI + +## Basic Node Structure + +### Runtime Node (JavaScript) + +Location: `packages/noodl-runtime/src/nodes/` + +```javascript +'use strict'; + +const MyNode = { + // === METADATA === + name: 'My.Custom.Node', // Unique identifier + displayName: 'My Custom Node', // Shown in UI + category: 'Custom', // Node picker category + color: 'data', // Node color theme + docs: 'https://docs.example.com', // Documentation link + + // === INITIALIZATION === + initialize() { + // Called when node is created + this._internal.myValue = ''; + this._internal.callbacks = []; + }, + + // === INPUTS === + inputs: { + // Simple input + textInput: { + type: 'string', + displayName: 'Text Input', + group: 'General', + default: '', + set(value) { + this._internal.textInput = value; + this.flagOutputDirty('result'); + } + }, + + // Number with validation + numberInput: { + type: 'number', + displayName: 'Number', + group: 'General', + default: 0, + set(value) { + if (typeof value !== 'number') return; + this._internal.numberInput = value; + this.flagOutputDirty('result'); + } + }, + + // Signal input (trigger) + doAction: { + type: 'signal', + displayName: 'Do Action', + group: 'Actions', + valueChangedToTrue() { + // Called when signal received + this.performAction(); + } + }, + + // Boolean toggle + enabled: { + type: 'boolean', + displayName: 'Enabled', + group: 'General', + default: true, + set(value) { + this._internal.enabled = value; + } + }, + + // Dropdown/enum + mode: { + type: { + name: 'enum', + enums: [ + { value: 'mode1', label: 'Mode 1' }, + { value: 'mode2', label: 'Mode 2' } + ] + }, + displayName: 'Mode', + group: 'General', + default: 'mode1', + set(value) { + this._internal.mode = value; + } + } + }, + + // === OUTPUTS === + outputs: { + // Value output + result: { + type: 'string', + displayName: 'Result', + group: 'General', + getter() { + return this._internal.result; + } + }, + + // Signal output + completed: { + type: 'signal', + displayName: 'Completed', + group: 'Events' + }, + + // Error output + error: { + type: 'string', + displayName: 'Error', + group: 'Error', + getter() { + return this._internal.error; + } + } + }, + + // === METHODS === + methods: { + performAction() { + if (!this._internal.enabled) return; + + try { + // Do something + this._internal.result = 'Success'; + this.flagOutputDirty('result'); + this.sendSignalOnOutput('completed'); + } catch (e) { + this._internal.error = e.message; + this.flagOutputDirty('error'); + } + }, + + // Called when node is deleted + _onNodeDeleted() { + // Cleanup + this._internal.callbacks = []; + } + }, + + // === INSPECTOR (Debug Panel) === + getInspectInfo() { + return { + type: 'text', + value: `Current: ${this._internal.result}` + }; + } +}; + +module.exports = { + node: MyNode +}; +``` + +### Visual Node (React) + +Location: `packages/noodl-viewer-react/src/nodes/` + +```javascript +'use strict'; + +const { Node } = require('@noodl/noodl-runtime'); + +const MyVisualNode = { + name: 'My.Visual.Node', + displayName: 'My Visual Node', + category: 'UI Elements', + + // Visual nodes need these + allowChildren: true, // Can have child nodes + allowChildrenWithCategory: ['UI Elements'], // Restrict child types + + getReactComponent() { + return MyReactComponent; + }, + + // Frame updates for animations + frame: { + // Called every frame if registered + update(context) { + // Animation logic + } + }, + + inputs: { + // Standard style inputs + backgroundColor: { + type: 'color', + displayName: 'Background Color', + group: 'Style', + default: 'transparent', + set(value) { + this.props.style.backgroundColor = value; + this.forceUpdate(); + } + }, + + // Dimension with units + width: { + type: { + name: 'number', + units: ['px', '%', 'vw'], + defaultUnit: 'px' + }, + displayName: 'Width', + group: 'Dimensions', + set(value) { + this.props.style.width = value.value + value.unit; + this.forceUpdate(); + } + } + }, + + outputs: { + // DOM event outputs + onClick: { + type: 'signal', + displayName: 'Click', + group: 'Events' + } + }, + + methods: { + // Called when mounted + didMount() { + // Setup + }, + + // Called when unmounted + willUnmount() { + // Cleanup + } + } +}; + +// React component +function MyReactComponent(props) { + const handleClick = () => { + props.noodlNode.sendSignalOnOutput('onClick'); + }; + + return ( +
+ {props.children} +
+ ); +} + +module.exports = { + node: MyVisualNode +}; +``` + +## Common Patterns + +### Scheduled Updates + +Batch multiple input changes before processing: + +```javascript +inputs: { + value1: { + set(value) { + this._internal.value1 = value; + this.scheduleProcess(); + } + }, + value2: { + set(value) { + this._internal.value2 = value; + this.scheduleProcess(); + } + } +}, +methods: { + scheduleProcess() { + if (this._internal.scheduled) return; + this._internal.scheduled = true; + + this.scheduleAfterInputsHaveUpdated(() => { + this._internal.scheduled = false; + this.processValues(); + }); + }, + processValues() { + // Process both values together + } +} +``` + +### Async Operations + +Handle promises and async work: + +```javascript +inputs: { + fetch: { + type: 'signal', + valueChangedToTrue() { + this.doFetch(); + } + } +}, +methods: { + async doFetch() { + try { + const response = await fetch(this._internal.url); + const data = await response.json(); + + this._internal.result = data; + this.flagOutputDirty('result'); + this.sendSignalOnOutput('success'); + } catch (error) { + this._internal.error = error.message; + this.flagOutputDirty('error'); + this.sendSignalOnOutput('failure'); + } + } +} +``` + +### Collection/Model Binding + +Work with Noodl's data system: + +```javascript +const Collection = require('../../../collection'); +const Model = require('../../../model'); + +inputs: { + items: { + type: 'array', + set(value) { + this.bindCollection(value); + } + } +}, +methods: { + bindCollection(collection) { + // Unbind previous + if (this._internal.collection) { + this._internal.collection.off('change', this._internal.onChange); + } + + this._internal.collection = collection; + + if (collection) { + this._internal.onChange = () => { + this.flagOutputDirty('count'); + }; + collection.on('change', this._internal.onChange); + } + } +} +``` + +### Dynamic Ports + +Add ports based on configuration: + +```javascript +inputs: { + properties: { + type: { name: 'stringlist', allowEditOnly: true }, + displayName: 'Properties', + set(value) { + // Register dynamic inputs/outputs based on list + value.forEach(prop => { + if (!this.hasInput('prop-' + prop)) { + this.registerInput('prop-' + prop, { + set(val) { + this._internal.values[prop] = val; + } + }); + } + }); + } + } +} +``` + +## Input Types Reference + +| Type | Description | Example | +|------|-------------|---------| +| `string` | Text input | `type: 'string'` | +| `number` | Numeric input | `type: 'number'` | +| `boolean` | Toggle | `type: 'boolean'` | +| `color` | Color picker | `type: 'color'` | +| `signal` | Trigger/event | `type: 'signal'` | +| `array` | Array/collection | `type: 'array'` | +| `object` | Object/model | `type: 'object'` | +| `component` | Component reference | `type: 'component'` | +| `enum` | Dropdown selection | `type: { name: 'enum', enums: [...] }` | +| `stringlist` | Editable list | `type: { name: 'stringlist' }` | +| `number` with units | Dimension | `type: { name: 'number', units: [...] }` | + +## Node Colors + +Available color themes for nodes: + +- `data` - Blue (data operations) +- `logic` - Purple (logic/control) +- `visual` - Green (UI elements) +- `component` - Orange (component utilities) +- `default` - Gray + +## Registering Nodes + +Add to the node library export: + +```javascript +// In packages/noodl-runtime/src/nodelibraryexport.js +const MyNode = require('./nodes/my-node'); + +// Add to appropriate category in coreNodes array +``` + +## Testing Nodes + +```javascript +// Example test structure +describe('MyNode', () => { + it('should process input correctly', () => { + const node = createNode('My.Custom.Node'); + node.setInput('textInput', 'hello'); + + expect(node.getOutput('result')).toBe('HELLO'); + }); +}); +``` diff --git a/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHANGELOG.md b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHANGELOG.md new file mode 100644 index 0000000..074c190 --- /dev/null +++ b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHANGELOG.md @@ -0,0 +1,140 @@ +# TASK-001 Changelog + +Track all changes made during this task. Update this file as you work. + +--- + +## [Date] - [Your Name/Handle] + +### Summary +[To be filled in as work progresses] + +### Starting Point +- Based on branch: `12-upgrade-dependencies` +- Previous work by: [previous developer] +- Previous commits include: + - Package.json dependency updates + - "Update rendering to use non-deprecated react-dom calls" + +--- + +## Dependency Fixes + +### Package: [package-name] +- **Issue**: [What was wrong] +- **Fix**: [What was changed] +- **File**: `path/to/package.json` + +--- + +## React 19 Migration + +### File: [filename] +- **Before**: `ReactDOM.render(, element)` +- **After**: `createRoot(element).render()` +- **Notes**: [Any relevant context] + +--- + +## react-instantsearch Migration + +### File: HelpCenter.tsx +- **Before**: `import { ... } from 'react-instantsearch-hooks-web'` +- **After**: `import { ... } from 'react-instantsearch'` +- **API Changes**: [List any API differences encountered] + +--- + +## Build Fixes + +### Error: [Error message] +- **Cause**: [Why it happened] +- **Fix**: [What was changed] +- **File**: `path/to/file` + +--- + +## Build Optimizations + +### Optimization: [Name] +- **Before**: Build time X seconds +- **After**: Build time Y seconds +- **Change**: [What was optimized] + +--- + +## Files Modified + + +- [ ] `package.json` (root) +- [ ] `packages/noodl-editor/package.json` +- [ ] `packages/noodl-core-ui/package.json` +- [ ] `packages/noodl-viewer-react/package.json` +- [ ] `packages/noodl-editor/src/editor/src/views/HelpCenter/HelpCenter.tsx` +- [ ] [Add more as needed] + +--- + +## Files Created + + +- None expected for this task + +--- + +## Files Deleted + + +- None expected for this task + +--- + +## Breaking Changes + +- React 19 requires Node.js 18+ (documented in root package.json engines) +- [Add any other breaking changes discovered] + +--- + +## Testing Notes + +### Automated Tests +- `npm run test:editor`: [PASS/FAIL] - [Notes] +- `npm run test:platform`: [PASS/FAIL] - [Notes] +- `npx tsc --noEmit`: [PASS/FAIL] - [Notes] + +### Manual Tests +- Dev server start: [PASS/FAIL] +- Create project: [PASS/FAIL] +- Node operations: [PASS/FAIL] +- Preview: [PASS/FAIL] +- Help Center search: [PASS/FAIL] +- Hot reload: [PASS/FAIL] + +--- + +## Known Issues + + + +1. [Issue description] - [Will be addressed in TASK-XXX] + +--- + +## Follow-up Tasks + + + +1. [Follow-up item] - Suggested for TASK-XXX + +--- + +## Final Summary + +[To be completed when task is done] + +**Total files modified**: X +**Total lines changed**: +X / -Y +**Build time**: Before X sec → After Y sec +**Tests**: All passing / X failures +**Confidence**: X/10 diff --git a/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHECKLIST.md b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHECKLIST.md new file mode 100644 index 0000000..41ae986 --- /dev/null +++ b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/CHECKLIST.md @@ -0,0 +1,123 @@ +# TASK-001 Checklist + +## Prerequisites +- [ ] Read README.md completely +- [ ] Understand React 19 breaking changes +- [ ] Have Node.js 18+ installed +- [ ] Clone the repository fresh (or ensure clean state) + +## Phase 1: Setup +- [ ] Checkout existing work: `git checkout 12-upgrade-dependencies` +- [ ] Create task branch: `git checkout -b task/001-dependency-updates` +- [ ] Delete node_modules: `rm -rf node_modules packages/*/node_modules` +- [ ] Clean install: `npm install` +- [ ] Document any install errors in NOTES.md +- [ ] Note: confidence level for this phase: __/10 + +## Phase 2: Dependency Conflicts +- [ ] List all peer dependency warnings +- [ ] Research each warning +- [ ] Fix conflicts in root package.json +- [ ] Fix conflicts in packages/noodl-editor/package.json +- [ ] Fix conflicts in packages/noodl-core-ui/package.json +- [ ] Fix conflicts in packages/noodl-viewer-react/package.json +- [ ] Fix conflicts in other packages as needed +- [ ] Verify clean `npm install` +- [ ] Document fixes in CHANGELOG.md +- [ ] Note: confidence level for this phase: __/10 + +## Phase 3: Build Errors +- [ ] Run `npm run build:editor` +- [ ] List all build errors +- [ ] Fix error 1: _______________ +- [ ] Fix error 2: _______________ +- [ ] Fix error 3: _______________ +- [ ] (add more as needed) +- [ ] Verify clean build +- [ ] Document fixes in CHANGELOG.md +- [ ] Note: confidence level for this phase: __/10 + +## Phase 4: React 19 Migration +- [ ] Search for ReactDOM.render usage: + ```bash + grep -rn "ReactDOM.render" packages/ --include="*.ts" --include="*.tsx" --include="*.js" + ``` +- [ ] List all files found: _______________ +- [ ] Update file 1: _______________ +- [ ] Update file 2: _______________ +- [ ] (add more as needed) +- [ ] Search for ReactDOM.hydrate usage +- [ ] Search for ReactDOM.unmountComponentAtNode usage +- [ ] Update any found +- [ ] Verify no legacy ReactDOM usage remains +- [ ] Document changes in CHANGELOG.md +- [ ] Note: confidence level for this phase: __/10 + +## Phase 5: react-instantsearch Migration +- [ ] Open `packages/noodl-editor/src/editor/src/views/HelpCenter/HelpCenter.tsx` +- [ ] Update import from `react-instantsearch-hooks-web` to `react-instantsearch` +- [ ] Check all hooks used are still available +- [ ] Search for other files using old package: + ```bash + grep -rn "react-instantsearch-hooks-web" packages/ + ``` +- [ ] Update any other files found +- [ ] Test search functionality works +- [ ] Document changes in CHANGELOG.md +- [ ] Note: confidence level for this phase: __/10 + +## Phase 6: Build Optimization (Optional but Recommended) +- [ ] Measure current build time: ___ seconds +- [ ] Check webpack config for cache settings +- [ ] Enable persistent caching if not enabled +- [ ] Check for unnecessary rebuilds +- [ ] Measure new build time: ___ seconds +- [ ] Document optimizations in CHANGELOG.md + +## Phase 7: Testing - Automated +- [ ] Run `npm run test:editor` + - [ ] All tests pass + - [ ] Note any failures: _______________ +- [ ] Run `npm run test:platform` + - [ ] All tests pass + - [ ] Note any failures: _______________ +- [ ] Run `npx tsc --noEmit` + - [ ] No TypeScript errors + - [ ] Note any errors: _______________ + +## Phase 8: Testing - Manual +- [ ] Start dev server: `npm run dev` + - [ ] Starts without errors + - [ ] No console warnings about deprecated APIs +- [ ] Create new project +- [ ] Add Group node to canvas +- [ ] Add Text node as child +- [ ] Connect nodes +- [ ] Open preview +- [ ] Edit text content, verify preview updates +- [ ] Save and reopen project +- [ ] Open Help Center, test search (react-instantsearch) +- [ ] Edit Function node code +- [ ] Change a file, verify hot reload works +- [ ] Build production: `npm run build:editor` + +## Phase 9: Cleanup & Documentation +- [ ] Remove any debug console.logs added +- [ ] Review all changes for code quality +- [ ] Complete CHANGELOG.md with summary +- [ ] Update NOTES.md with learnings +- [ ] Self-review: confidence level __/10 + +## Phase 10: Completion +- [ ] All success criteria met (see README.md) +- [ ] Create pull request +- [ ] PR title: "TASK-001: Dependency Updates & Build Modernization" +- [ ] PR description includes: + - [ ] Summary of changes + - [ ] Testing performed + - [ ] Any known issues or follow-ups +- [ ] Mark task complete + +## Final Confidence Check +- Overall confidence this task is complete and correct: __/10 +- Remaining concerns: _______________ diff --git a/dev-docs/tasks/phase-1/TASK-001-dependency-updates/NOTES.md b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/NOTES.md new file mode 100644 index 0000000..86cbb10 --- /dev/null +++ b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/NOTES.md @@ -0,0 +1,128 @@ +# TASK-001 Working Notes + +## Research + +### Previous Developer's Work + +**Branch**: `12-upgrade-dependencies` + +**Commits found**: +1. Package.json updates across all packages +2. "Update rendering to use non-deprecated react-dom calls" + +**What they changed**: +- React 17.0.2 → 19.0.0 +- react-instantsearch-hooks-web → react-instantsearch +- Removed deprecated react-json-view, added @microlink/react-json-view +- Updated webpack 5.74.0 → 5.101.3 +- Removed Node.js upper version cap (was <=18, now 16+) +- Removed Storybook 6.x packages + +### React 19 Breaking Changes to Watch For + +1. **Automatic Batching** - State updates are now automatically batched +2. **Concurrent Features** - May affect node graph rendering timing +3. **Strict Mode** - Double-renders effects for cleanup detection +4. **Removed APIs**: + - `ReactDOM.render()` → `createRoot()` + - `ReactDOM.hydrate()` → `hydrateRoot()` + - `ReactDOM.unmountComponentAtNode()` → `root.unmount()` + +### react-instantsearch Changes + +The package was renamed from `react-instantsearch-hooks-web` to `react-instantsearch`. + +Most APIs are compatible, but verify: +- `useHits` +- `useSearchBox` +- `InstantSearch` component props + +### Files to Search + +```bash +# Find ReactDOM.render usage +grep -rn "ReactDOM.render" packages/ --include="*.ts" --include="*.tsx" --include="*.js" + +# Find old instantsearch imports +grep -rn "react-instantsearch-hooks-web" packages/ + +# Find any remaining TSFixme (for awareness, not this task) +grep -rn "TSFixme" packages/ --include="*.ts" --include="*.tsx" +``` + +--- + +## Assumptions + +- [ ] Previous dev's changes are on `12-upgrade-dependencies` branch - **VERIFY** +- [ ] Build was working before their changes - **VERIFY by checking main** +- [ ] No other branches need to be merged first - **VERIFY** + +--- + +## Implementation Notes + +### Approach Decisions + +[To be filled in during work] + +### Gotchas / Surprises + +[To be filled in during work] + +--- + +## Debug Log + +### [Date/Time] +- **Trying**: [what you're attempting] +- **Result**: [what happened] +- **Next**: [what to try next] + +--- + +## Useful Commands + +```bash +# Clean install +rm -rf node_modules packages/*/node_modules +npm install + +# Build editor +npm run build:editor + +# Run tests +npm run test:editor + +# Type check +npx tsc --noEmit + +# Start dev server +npm run dev + +# Find files with pattern +grep -rn "pattern" packages/ --include="*.ts" --include="*.tsx" + +# Check git status +git status +git diff --stat + +# Compare with main +git diff main..HEAD --stat +``` + +--- + +## Questions to Resolve + +- [ ] Are there any other branches that should be merged first? +- [ ] Did the previous dev test the build? +- [ ] Are there any known issues documented anywhere? + +--- + +## Links & Resources + +- [React 19 Blog Post](https://react.dev/blog/2024/04/25/react-19) +- [React 19 Upgrade Guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) +- [react-instantsearch Migration](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/) diff --git a/dev-docs/tasks/phase-1/TASK-001-dependency-updates/README.md b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/README.md new file mode 100644 index 0000000..0f2d842 --- /dev/null +++ b/dev-docs/tasks/phase-1/TASK-001-dependency-updates/README.md @@ -0,0 +1,218 @@ +# TASK-001: Dependency Updates & Build Modernization + +## Metadata + +| Field | Value | +|-------|-------| +| **ID** | TASK-001 | +| **Phase** | Phase 1 - Foundation | +| **Priority** | 🔴 Critical | +| **Difficulty** | 🟡 Medium | +| **Estimated Time** | 2-3 days | +| **Prerequisites** | None (this is the first task) | +| **Branch** | `task/001-dependency-updates` | +| **Related Branches** | `12-upgrade-dependencies` (previous dev work) | + +## Objective + +Complete and validate all dependency updates, fully migrate to React 19, and modernize the build pipeline for reliable, fast development. + +## Background + +A previous developer started this work on the `12-upgrade-dependencies` branch. They updated package.json files across the monorepo, including: +- React 17 → 19 +- Various webpack, typescript, and tooling updates +- Removed Node.js version upper cap + +They also made a commit "Update rendering to use non-deprecated react-dom calls" which addressed some React 19 breaking changes. + +This task completes that work, validates everything works, and improves the overall build experience. + +## Current State + +### What Exists +- Branch `12-upgrade-dependencies` with package.json updates +- Some React 19 migration work done +- Build may have errors or warnings + +### Known Issues +- `react-instantsearch-hooks-web` renamed to `react-instantsearch` (breaking API change) +- `ReactDOM.render()` deprecated in React 18+ +- Potential peer dependency conflicts +- Hot reload may be unreliable +- Build times are slow + +### Key Package Changes (from previous dev) + +| Package | Old | New | Breaking Changes | +|---------|-----|-----|------------------| +| react | 17.0.2 | 19.0.0 | Yes - see below | +| react-dom | 17.0.0 | 19.0.0 | Yes - render API | +| react-instantsearch-hooks-web | 6.38.0 | react-instantsearch 7.16.2 | Yes - renamed | +| webpack | 5.74.0 | 5.101.3 | Minor | +| typescript | 4.8.3 | 4.9.5 | Minor | + +## Desired State + +After this task: +- All packages build without errors +- No deprecation warnings in console +- React 19 fully adopted (no legacy patterns) +- Hot reload works reliably +- Build completes in <60 seconds +- All existing tests pass + +## Scope + +### In Scope +- [x] Validate and fix dependency updates +- [x] Complete React 19 migration +- [x] Fix all build errors and warnings +- [x] Update react-instantsearch usage +- [x] Improve build performance +- [x] Fix hot reload issues + +### Out of Scope +- Major refactoring (that's later tasks) +- New features +- TSFixme cleanup (TASK-002) +- Storybook 9 migration (can be separate task) + +## Technical Approach + +### Key Files to Modify + +| File | Changes | +|------|---------| +| `package.json` (root) | Verify dependencies, fix conflicts | +| `packages/*/package.json` | Verify peer deps, fix conflicts | +| `packages/noodl-editor/src/editor/src/views/HelpCenter/HelpCenter.tsx` | Update react-instantsearch imports | +| Any file with `ReactDOM.render` | Migrate to createRoot | +| `packages/noodl-viewer-react/` | React 19 compatibility | + +### React 19 Migration Points + +```typescript +// OLD (React 17) +import ReactDOM from 'react-dom'; +ReactDOM.render(, document.getElementById('root')); + +// NEW (React 19) +import { createRoot } from 'react-dom/client'; +const root = createRoot(document.getElementById('root')); +root.render(); +``` + +Search for these patterns: +```bash +grep -r "ReactDOM.render" packages/ --include="*.ts" --include="*.tsx" --include="*.js" +grep -r "ReactDOM.hydrate" packages/ --include="*.ts" --include="*.tsx" --include="*.js" +grep -r "ReactDOM.unmountComponentAtNode" packages/ --include="*.ts" --include="*.tsx" --include="*.js" +``` + +### react-instantsearch Migration + +```typescript +// OLD +import { InstantSearch, Hits } from 'react-instantsearch-hooks-web'; + +// NEW +import { InstantSearch, Hits } from 'react-instantsearch'; +``` + +The API is mostly compatible, but verify all hooks used still exist. + +## Implementation Steps + +### Step 1: Setup and Initial Validation +1. Checkout the existing branch: `git checkout 12-upgrade-dependencies` +2. Create our task branch: `git checkout -b task/001-dependency-updates` +3. Clean install: `rm -rf node_modules && npm install` +4. Document any install errors in NOTES.md + +### Step 2: Fix Dependency Conflicts +1. Run `npm install` and note all peer dependency warnings +2. For each warning, determine the correct resolution +3. Update package.json files as needed +4. Repeat until `npm install` runs cleanly + +### Step 3: Fix Build Errors +1. Run `npm run build:editor` +2. Fix each error one at a time +3. Document each fix in CHANGELOG.md +4. Repeat until build succeeds + +### Step 4: React 19 Migration +1. Search for deprecated ReactDOM calls +2. Update each to use createRoot pattern +3. Check for class component lifecycle issues +4. Test each changed component + +### Step 5: react-instantsearch Update +1. Update imports in HelpCenter.tsx +2. Verify all hooks/components still available +3. Test search functionality + +### Step 6: Build Optimization +1. Analyze current build time +2. Check webpack configs for optimization opportunities +3. Enable caching if not already +4. Measure improvement + +### Step 7: Validation +1. Run full test suite +2. Manual testing of key workflows +3. Verify hot reload works +4. Check for console warnings + +## Testing Plan + +### Automated Tests +- [ ] Run `npm run test:editor` - all pass +- [ ] Run `npm run test:platform` - all pass +- [ ] Run `npx tsc --noEmit` - no type errors + +### Manual Testing Scenarios +- [ ] Start dev server: `npm run dev` - starts without errors +- [ ] Create new project - works +- [ ] Add nodes to canvas - works +- [ ] Connect nodes - works +- [ ] Preview project - works +- [ ] Edit code in Function node - works +- [ ] Hot reload when editing - works +- [ ] Search in Help Center - works (react-instantsearch) +- [ ] Build for production - works + +## Success Criteria + +- [ ] `npm install` completes with no errors +- [ ] `npm run build:editor` completes with no errors +- [ ] `npm run test:editor` all tests pass +- [ ] `npx tsc --noEmit` no TypeScript errors +- [ ] No deprecation warnings in browser console +- [ ] Hot reload works reliably +- [ ] All manual test scenarios pass + +## Risks & Mitigations + +| Risk | Mitigation | +|------|------------| +| React 19 breaks something subtle | Extensive manual testing, rollback plan ready | +| react-instantsearch API changes | Read migration guide, test search thoroughly | +| Build time regression | Measure before/after, optimize if needed | +| Peer dependency hell | Use `--legacy-peer-deps` as last resort, document why | + +## Rollback Plan + +If major issues discovered: +1. Checkout main branch +2. Cherry-pick any non-breaking fixes +3. Document issues for future attempt +4. Consider incremental approach (React 18 first, then 19) + +## References + +- [React 19 Release Notes](https://react.dev/blog/2024/04/25/react-19) +- [react-instantsearch v7 Migration](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/) +- Previous dev branch: `12-upgrade-dependencies` +- Previous dev commit: "Update rendering to use non-deprecated react-dom calls"