Started tasks to migrate runtime to React 19. Added phase 3 projects

This commit is contained in:
Richard Osborne
2025-12-13 22:37:44 +01:00
parent 8dd4f395c0
commit 1477a29ff7
55 changed files with 49205 additions and 281 deletions

View File

@@ -0,0 +1,220 @@
# DASH-001: Tabbed Navigation System
## Overview
Replace the current single-view dashboard with a proper tabbed interface. This is the foundation task that enables all other dashboard improvements.
## Context
The current Noodl editor dashboard (`projectsview.ts`) uses a basic pane-switching mechanism with jQuery. A new launcher is being developed in `packages/noodl-core-ui/src/preview/launcher/` using React, which already has a sidebar-based navigation but needs proper tab support for the main content area.
This task focuses on the **new React-based launcher** only. The old jQuery launcher will be deprecated.
## Current State
### Existing New Launcher Structure
```
packages/noodl-core-ui/src/preview/launcher/
├── Launcher/
│ ├── Launcher.tsx # Main component with PAGES array
│ ├── components/
│ │ ├── LauncherSidebar/ # Left navigation
│ │ ├── LauncherPage/ # Page wrapper
│ │ ├── LauncherProjectCard/
│ │ └── LauncherSearchBar/
│ └── views/
│ ├── Projects.tsx # Current projects view
│ └── LearningCenter.tsx # Empty learning view
└── template/
└── LauncherApp/ # App shell template
```
### Current Page Definition
```typescript
// In Launcher.tsx
export enum LauncherPageId {
LocalProjects,
LearningCenter
}
export const PAGES: LauncherPageMetaData[] = [
{ id: LauncherPageId.LocalProjects, displayName: 'Recent Projects', icon: IconName.CircleDot },
{ id: LauncherPageId.LearningCenter, displayName: 'Learn', icon: IconName.Rocket }
];
```
## Requirements
### Functional Requirements
1. **Tab Bar Component**
- Horizontal tab bar at the top of the main content area
- Visual indicator for active tab
- Smooth transition when switching tabs
- Keyboard navigation support (arrow keys, Enter)
2. **Tab Configuration**
- Projects tab (default, opens first)
- Learn tab (tutorials, guides)
- Templates tab (project starters)
- Extensible for future tabs (Marketplace, Settings)
3. **State Persistence**
- Remember last active tab across sessions
- Store in localStorage or electron-store
4. **URL/Deep Linking (Optional)**
- Support for `noodl://dashboard/projects` style deep links
- Query params for tab state
### Non-Functional Requirements
- Tab switching should feel instant (<100ms)
- No layout shift when switching tabs
- Accessible (WCAG 2.1 AA compliant)
- Consistent with existing noodl-core-ui design system
## Technical Approach
### 1. Create Tab Bar Component
Create a new component in `noodl-core-ui` that can be reused:
```
packages/noodl-core-ui/src/components/layout/TabBar/
├── TabBar.tsx
├── TabBar.module.scss
├── TabBar.stories.tsx
└── index.ts
```
### 2. Update Launcher Structure
```typescript
// New page structure
export enum LauncherPageId {
Projects = 'projects',
Learn = 'learn',
Templates = 'templates'
}
export interface LauncherTab {
id: LauncherPageId;
label: string;
icon?: IconName;
component: React.ComponentType;
}
export const LAUNCHER_TABS: LauncherTab[] = [
{ id: LauncherPageId.Projects, label: 'Projects', icon: IconName.Folder, component: Projects },
{ id: LauncherPageId.Learn, label: 'Learn', icon: IconName.Book, component: LearningCenter },
{ id: LauncherPageId.Templates, label: 'Templates', icon: IconName.Components, component: Templates }
];
```
### 3. State Management
Use React context for tab state:
```typescript
// LauncherContext.tsx
interface LauncherContextValue {
activeTab: LauncherPageId;
setActiveTab: (tab: LauncherPageId) => void;
}
```
### 4. Persistence Hook
```typescript
// usePersistentTab.ts
function usePersistentTab(key: string, defaultTab: LauncherPageId) {
// Load from localStorage on mount
// Save to localStorage on change
}
```
## Files to Create
1. `packages/noodl-core-ui/src/components/layout/TabBar/TabBar.tsx`
2. `packages/noodl-core-ui/src/components/layout/TabBar/TabBar.module.scss`
3. `packages/noodl-core-ui/src/components/layout/TabBar/TabBar.stories.tsx`
4. `packages/noodl-core-ui/src/components/layout/TabBar/index.ts`
5. `packages/noodl-core-ui/src/preview/launcher/Launcher/LauncherContext.tsx`
6. `packages/noodl-core-ui/src/preview/launcher/Launcher/hooks/usePersistentTab.ts`
7. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/Templates.tsx`
## Files to Modify
1. `packages/noodl-core-ui/src/preview/launcher/Launcher/Launcher.tsx`
- Import and use TabBar
- Implement tab switching logic
- Wrap with LauncherContext
2. `packages/noodl-core-ui/src/components/layout/index.ts`
- Export TabBar component
## Implementation Steps
### Phase 1: TabBar Component
1. Create TabBar component with basic functionality
2. Add styling consistent with noodl-core-ui
3. Write Storybook stories for testing
4. Add keyboard navigation
### Phase 2: Launcher Integration
1. Create LauncherContext
2. Create usePersistentTab hook
3. Integrate TabBar into Launcher.tsx
4. Create empty Templates view
### Phase 3: Polish
1. Add tab transition animations
2. Test accessibility
3. Add deep link support (if time permits)
## Testing Checklist
- [ ] Tabs render correctly
- [ ] Clicking tab switches content
- [ ] Active tab is visually indicated
- [ ] Keyboard navigation works (Tab, Arrow keys, Enter)
- [ ] Tab state persists after closing/reopening
- [ ] No layout shift on tab switch
- [ ] Works at different viewport sizes
- [ ] Screen reader announces tab changes
## Design Reference
The tab bar should follow the existing Tabs component style in noodl-core-ui but be optimized for the launcher context (larger, more prominent).
See: `packages/noodl-core-ui/src/components/layout/Tabs/`
## Dependencies
- None (this is a foundation task)
## Blocked By
- None
## Blocks
- DASH-002 (Project List Redesign)
- DASH-003 (Project Organization)
- DASH-004 (Tutorial Section Redesign)
## Estimated Effort
- Component creation: 2-3 hours
- Launcher integration: 2-3 hours
- Polish and testing: 1-2 hours
- **Total: 5-8 hours**
## Success Criteria
1. User can switch between Projects, Learn, and Templates tabs
2. Tab state persists across sessions
3. Component is reusable for other contexts
4. Passes accessibility audit
5. Matches existing design system aesthetics

View File

@@ -0,0 +1,292 @@
# DASH-002: Project List Redesign
## Overview
Transform the project list from a thumbnail grid into a more functional table/list view optimized for users with many projects. Add sorting, better information density, and optional view modes.
## Context
The current dashboard shows projects as large cards with auto-generated thumbnails. This works for users with a few projects but becomes unwieldy with many projects. The thumbnails add visual noise without providing much value.
The new launcher in `noodl-core-ui/src/preview/launcher/` already has the beginnings of a table layout with columns for Name, Version Control, and Contributors.
## Current State
### Existing LauncherProjectCard
```typescript
// From LauncherProjectCard.tsx
export interface LauncherProjectData {
id: string;
title: string;
cloudSyncMeta: {
type: CloudSyncType;
source?: string;
};
localPath: string;
lastOpened: string;
pullAmount?: number;
pushAmount?: number;
uncommittedChangesAmount?: number;
imageSrc: string;
contributors?: UserBadgeProps[];
}
```
### Current Layout (Projects.tsx)
- Table header with Name, Version control, Contributors columns
- Cards with thumbnail images
- Basic search functionality via LauncherSearchBar
## Requirements
### Functional Requirements
1. **List View (Primary)**
- Compact row-based layout
- Columns: Name, Last Modified, Git Status, Local Path (truncated)
- Row hover state with quick actions
- Sortable columns (click header to sort)
- Resizable columns (stretch goal)
2. **Grid View (Secondary)**
- Card-based layout for visual preference
- Smaller cards than current (2-3x more per row)
- Optional thumbnails (can be disabled)
- View toggle in toolbar
3. **Sorting**
- Sort by Name (A-Z, Z-A)
- Sort by Last Modified (newest, oldest)
- Sort by Git Status (synced first, needs attention first)
- Persist sort preference
4. **Information Display**
- Project name (primary)
- Last modified timestamp (relative: "2 hours ago")
- Git status indicator (icon + tooltip)
- Local path (truncated with tooltip for full path)
- Quick action buttons on hover (Open, Folder, Settings, Delete)
5. **Empty State**
- Friendly message when no projects exist
- Call-to-action to create new project or import
### Non-Functional Requirements
- Handle 100+ projects smoothly (virtual scrolling if needed)
- Row click opens project
- Right-click context menu
- Responsive to window resize
## Technical Approach
### 1. Data Layer
Create a hook for project data with sorting:
```typescript
// useProjectList.ts
interface UseProjectListOptions {
sortField: 'name' | 'lastModified' | 'gitStatus';
sortDirection: 'asc' | 'desc';
filter?: string;
}
interface UseProjectListReturn {
projects: LauncherProjectData[];
isLoading: boolean;
sortField: string;
sortDirection: string;
setSorting: (field: string, direction: string) => void;
}
```
### 2. List View Component
```
packages/noodl-core-ui/src/preview/launcher/Launcher/components/
├── ProjectList/
│ ├── ProjectList.tsx # Main list component
│ ├── ProjectListRow.tsx # Individual row
│ ├── ProjectListHeader.tsx # Sortable header
│ ├── ProjectList.module.scss
│ └── index.ts
```
### 3. View Mode Toggle
```typescript
// ViewModeToggle.tsx
export enum ViewMode {
List = 'list',
Grid = 'grid'
}
interface ViewModeToggleProps {
mode: ViewMode;
onChange: (mode: ViewMode) => void;
}
```
### 4. Git Status Display
```typescript
// GitStatusBadge.tsx
export enum GitStatusType {
NotInitialized = 'not-initialized',
LocalOnly = 'local-only',
Synced = 'synced',
Ahead = 'ahead', // Have local commits to push
Behind = 'behind', // Have remote commits to pull
Diverged = 'diverged', // Both ahead and behind
Uncommitted = 'uncommitted'
}
interface GitStatusBadgeProps {
status: GitStatusType;
details?: {
ahead?: number;
behind?: number;
uncommitted?: number;
};
}
```
## Files to Create
1. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectList.tsx`
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectListRow.tsx`
3. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectListHeader.tsx`
4. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectList.module.scss`
5. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/index.ts`
6. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ViewModeToggle/ViewModeToggle.tsx`
7. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/GitStatusBadge/GitStatusBadge.tsx`
8. `packages/noodl-core-ui/src/preview/launcher/Launcher/hooks/useProjectList.ts`
9. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/EmptyProjectsState/EmptyProjectsState.tsx`
## Files to Modify
1. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/Projects.tsx`
- Replace current layout with ProjectList component
- Add view mode toggle
- Wire up sorting
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/LauncherProjectCard/LauncherProjectCard.tsx`
- Refactor for grid view (smaller)
- Make thumbnail optional
3. `packages/noodl-core-ui/src/preview/launcher/Launcher/Launcher.tsx`
- Update mock data if needed
- Add view mode to context
## Implementation Steps
### Phase 1: Core List View
1. Create ProjectListHeader with sortable columns
2. Create ProjectListRow with project info
3. Create ProjectList combining header and rows
4. Add basic sorting logic
### Phase 2: Git Status Display
1. Create GitStatusBadge component
2. Define status types and icons
3. Add tooltips with details
### Phase 3: View Modes
1. Create ViewModeToggle component
2. Refactor LauncherProjectCard for grid mode
3. Add view mode to Projects view
4. Persist preference
### Phase 4: Polish
1. Add empty state
2. Add hover actions
3. Implement virtual scrolling (if needed)
4. Test with large project counts
## Component Specifications
### ProjectListHeader
| Column | Width | Sortable | Content |
|--------|-------|----------|---------|
| Name | 40% | Yes | Project name |
| Last Modified | 20% | Yes | Relative timestamp |
| Git Status | 15% | Yes | Status badge |
| Path | 25% | No | Truncated local path |
### ProjectListRow
```
┌──────────────────────────────────────────────────────────────────────┐
│ 📁 My Project Name 2 hours ago ⚡ Ahead (3) ~/dev/... │
│ [hover: Open 📂 ⚙️ 🗑️] │
└──────────────────────────────────────────────────────────────────────┘
```
### GitStatusBadge Icons
| Status | Icon | Color | Tooltip |
|--------|------|-------|---------|
| not-initialized | ⚪ | Gray | "No version control" |
| local-only | 💾 | Yellow | "Local git only, not synced" |
| synced | ✅ | Green | "Up to date with remote" |
| ahead | ⬆️ | Blue | "3 commits to push" |
| behind | ⬇️ | Orange | "5 commits to pull" |
| diverged | ⚠️ | Red | "3 ahead, 5 behind" |
| uncommitted | ● | Yellow | "Uncommitted changes" |
## Testing Checklist
- [ ] List renders with mock data
- [ ] Clicking row opens project (or shows FIXME alert)
- [ ] Sorting by each column works
- [ ] Sort direction toggles on repeated click
- [ ] Sort preference persists
- [ ] View mode toggle switches layouts
- [ ] View mode preference persists
- [ ] Git status badges display correctly
- [ ] Tooltips show on hover
- [ ] Right-click shows context menu
- [ ] Empty state shows when no projects
- [ ] Search filters projects correctly
- [ ] Performance acceptable with 100+ mock projects
## Dependencies
- DASH-001 (Tabbed Navigation System) - for launcher context
## Blocked By
- DASH-001
## Blocks
- DASH-003 (needs list infrastructure for folder/tag filtering)
## Estimated Effort
- ProjectList components: 3-4 hours
- GitStatusBadge: 1-2 hours
- View mode toggle: 1-2 hours
- Sorting & persistence: 2-3 hours
- Polish & testing: 2-3 hours
- **Total: 9-14 hours**
## Success Criteria
1. Projects display in a compact, sortable list
2. Git status is immediately visible
3. Users can switch to grid view if preferred
4. Sorting and view preferences persist
5. Empty state guides new users
6. Context menu provides quick actions
## Design Notes
The list view should feel similar to:
- VS Code's file explorer
- macOS Finder list view
- GitHub repository list
Keep information density high but avoid clutter. Use icons where possible to save space, with tooltips for details.

View File

@@ -0,0 +1,357 @@
# DASH-003: Project Organization - Folders & Tags
## Overview
Add the ability to organize projects using folders and tags. This enables users with many projects to group related work, filter their view, and find projects quickly.
## Context
Currently, projects are displayed in a flat list sorted by recency. Users with many projects (10+) struggle to find specific projects. There's no way to group related projects (e.g., "Client Work", "Personal", "Tutorials").
This task adds a folder/tag system that works entirely client-side, storing metadata separately from the Noodl projects themselves.
## Requirements
### Functional Requirements
1. **Folders**
- Create, rename, delete folders
- Drag-and-drop projects into folders
- Nested folders (1 level deep max)
- "All Projects" virtual folder (shows everything)
- "Uncategorized" virtual folder (shows unorganized projects)
- Folder displayed in sidebar
2. **Tags**
- Create, rename, delete tags
- Assign multiple tags per project
- Color-coded tags
- Tag filtering (show projects with specific tags)
- Tags displayed as pills on project rows
3. **Filtering**
- Filter by folder (sidebar click)
- Filter by tag (tag click or dropdown)
- Combine folder + tag filters
- Search within filtered view
- Clear all filters button
4. **Persistence**
- Store folder/tag data in electron-store (not in project files)
- Data structure keyed by project path (stable identifier)
- Export/import organization data (stretch goal)
### Non-Functional Requirements
- Organization changes feel instant
- Drag-and-drop is smooth
- Works offline
- Survives app restart
## Data Model
### Storage Structure
```typescript
// Stored in electron-store under 'projectOrganization'
interface ProjectOrganizationData {
version: 1;
folders: Folder[];
tags: Tag[];
projectMeta: Record<string, ProjectMeta>; // keyed by project path
}
interface Folder {
id: string;
name: string;
parentId: string | null; // null = root level
order: number;
createdAt: string;
}
interface Tag {
id: string;
name: string;
color: string; // hex color
createdAt: string;
}
interface ProjectMeta {
folderId: string | null;
tagIds: string[];
customName?: string; // optional override
notes?: string; // stretch goal
}
```
### Color Palette for Tags
```typescript
const TAG_COLORS = [
'#EF4444', // Red
'#F97316', // Orange
'#EAB308', // Yellow
'#22C55E', // Green
'#06B6D4', // Cyan
'#3B82F6', // Blue
'#8B5CF6', // Purple
'#EC4899', // Pink
'#6B7280', // Gray
];
```
## Technical Approach
### 1. Storage Service
```typescript
// packages/noodl-editor/src/editor/src/services/ProjectOrganizationService.ts
class ProjectOrganizationService {
private static instance: ProjectOrganizationService;
// Folder operations
createFolder(name: string, parentId?: string): Folder;
renameFolder(id: string, name: string): void;
deleteFolder(id: string): void;
reorderFolder(id: string, newOrder: number): void;
// Tag operations
createTag(name: string, color: string): Tag;
renameTag(id: string, name: string): void;
deleteTag(id: string): void;
changeTagColor(id: string, color: string): void;
// Project organization
moveProjectToFolder(projectPath: string, folderId: string | null): void;
addTagToProject(projectPath: string, tagId: string): void;
removeTagFromProject(projectPath: string, tagId: string): void;
// Queries
getFolders(): Folder[];
getTags(): Tag[];
getProjectMeta(projectPath: string): ProjectMeta | null;
getProjectsInFolder(folderId: string | null): string[];
getProjectsWithTag(tagId: string): string[];
}
```
### 2. Sidebar Folder Tree
```
packages/noodl-core-ui/src/preview/launcher/Launcher/components/
├── FolderTree/
│ ├── FolderTree.tsx # Tree container
│ ├── FolderTreeItem.tsx # Individual folder row
│ ├── FolderTree.module.scss
│ └── index.ts
```
### 3. Tag Components
```
├── TagPill/
│ ├── TagPill.tsx # Small colored tag display
│ └── TagPill.module.scss
├── TagSelector/
│ ├── TagSelector.tsx # Dropdown to add/remove tags
│ └── TagSelector.module.scss
├── TagFilter/
│ ├── TagFilter.tsx # Filter bar with active tags
│ └── TagFilter.module.scss
```
### 4. Drag and Drop
Use `@dnd-kit/core` for drag-and-drop:
```typescript
// DragDropContext for launcher
import { DndContext, DragOverlay } from '@dnd-kit/core';
// Draggable project row
import { useDraggable } from '@dnd-kit/core';
// Droppable folder
import { useDroppable } from '@dnd-kit/core';
```
## Files to Create
1. `packages/noodl-editor/src/editor/src/services/ProjectOrganizationService.ts`
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/FolderTree/FolderTree.tsx`
3. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/FolderTree/FolderTreeItem.tsx`
4. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/FolderTree/FolderTree.module.scss`
5. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TagPill/TagPill.tsx`
6. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TagSelector/TagSelector.tsx`
7. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TagFilter/TagFilter.tsx`
8. `packages/noodl-core-ui/src/preview/launcher/Launcher/hooks/useProjectOrganization.ts`
9. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/CreateFolderModal/CreateFolderModal.tsx`
10. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/CreateTagModal/CreateTagModal.tsx`
## Files to Modify
1. `packages/noodl-core-ui/src/preview/launcher/Launcher/Launcher.tsx`
- Add DndContext wrapper
- Add organization state to context
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/LauncherSidebar/LauncherSidebar.tsx`
- Add FolderTree component
- Add "Create Folder" button
3. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/Projects.tsx`
- Add TagFilter bar
- Filter projects based on folder/tag selection
- Make project rows draggable
4. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProjectList/ProjectListRow.tsx`
- Add tag pills
- Add tag selector on hover/context menu
- Make row draggable
## UI Mockups
### Sidebar with Folders
```
┌─────────────────────────┐
│ 📁 All Projects (24) │
│ 📁 Uncategorized (5) │
├─────────────────────────┤
│ + Create Folder │
├─────────────────────────┤
│ 📂 Client Work (8) │
│ └─ 📁 Acme Corp (3) │
│ └─ 📁 BigCo (5) │
│ 📂 Personal (6) │
│ 📂 Tutorials (5) │
└─────────────────────────┘
```
### Project Row with Tags
```
┌──────────────────────────────────────────────────────────────────────────┐
│ 📁 E-commerce Dashboard 2h ago ✅ [🔴 Urgent] [🔵 Client] ~/dev/... │
└──────────────────────────────────────────────────────────────────────────┘
```
### Tag Filter Bar
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Filters: [🔴 Urgent ×] [🔵 Client ×] [+ Add Filter] [Clear All] │
└─────────────────────────────────────────────────────────────────────────┘
```
## Implementation Steps
### Phase 1: Storage Foundation
1. Create ProjectOrganizationService
2. Define data model and storage
3. Create useProjectOrganization hook
4. Add to launcher context
### Phase 2: Folders
1. Create FolderTree component
2. Add to sidebar
3. Create folder modal
4. Implement folder filtering
5. Add context menu (rename, delete)
### Phase 3: Tags
1. Create TagPill component
2. Create TagSelector dropdown
3. Create TagFilter bar
4. Add tags to project rows
5. Implement tag filtering
### Phase 4: Drag and Drop
1. Add dnd-kit dependency
2. Wrap launcher in DndContext
3. Make project rows draggable
4. Make folders droppable
5. Handle drop events
### Phase 5: Polish
1. Add keyboard shortcuts
2. Improve animations
3. Handle edge cases (deleted projects, etc.)
4. Test thoroughly
## Testing Checklist
- [ ] Can create a folder
- [ ] Can rename a folder
- [ ] Can delete a folder (projects go to Uncategorized)
- [ ] Can create nested folder
- [ ] Clicking folder filters project list
- [ ] Can create a tag
- [ ] Can assign tag to project
- [ ] Can remove tag from project
- [ ] Clicking tag filters project list
- [ ] Can combine folder + tag filters
- [ ] Search works within filtered view
- [ ] Clear filters button works
- [ ] Drag project to folder works
- [ ] Data persists after app restart
- [ ] Removing project from disk shows appropriate state
## Dependencies
- DASH-001 (Tabbed Navigation System)
- DASH-002 (Project List Redesign) - for project rows
### External Dependencies
Add to `package.json`:
```json
{
"@dnd-kit/core": "^6.0.0",
"@dnd-kit/sortable": "^7.0.0"
}
```
## Blocked By
- DASH-002
## Blocks
- None (this is end of the DASH chain)
## Estimated Effort
- Storage service: 2-3 hours
- Folder tree UI: 3-4 hours
- Tag components: 3-4 hours
- Drag and drop: 3-4 hours
- Filtering logic: 2-3 hours
- Polish & testing: 3-4 hours
- **Total: 16-22 hours**
## Success Criteria
1. Users can create folders and organize projects
2. Users can create tags and assign them to projects
3. Filtering by folder and tag works correctly
4. Drag-and-drop feels natural
5. Organization data persists across sessions
6. System handles edge cases gracefully (deleted projects, etc.)
## Future Enhancements
- Export/import organization data
- Folder color customization
- Project notes/descriptions
- Bulk operations (move/tag multiple projects)
- Smart folders (auto-organize by criteria)
## Design Notes
The folder tree should feel familiar like:
- macOS Finder sidebar
- VS Code Explorer
- Notion page tree
Keep interactions lightweight - organization should help, not hinder, the workflow of quickly opening projects.

View File

@@ -0,0 +1,413 @@
# DASH-004: Tutorial Section Redesign
## Overview
Redesign the tutorial section (Learn tab) to be more compact, informative, and useful. Move from large tiles to a structured learning center with categories, progress tracking, and better discoverability.
## Context
The current tutorial section (`projectsview.ts` and lessons model) shows tutorials as large tiles with progress bars. The tiles take up significant screen space, making it hard to browse many tutorials. There's no categorization beyond a linear list.
The new launcher has an empty `LearningCenter.tsx` view that needs to be built out.
### Current Tutorial System
The existing system uses:
- `LessonProjectsModel` - manages lesson templates and progress
- `lessonprojectsmodel.ts` - fetches from docs endpoint
- Templates stored in docs repo with progress in localStorage
## Requirements
### Functional Requirements
1. **Category Organization**
- Categories: Getting Started, Building UIs, Data & Logic, Advanced Topics, Integrations
- Collapsible category sections
- Category icons/colors
2. **Tutorial Cards (Compact)**
- Title
- Short description (1-2 lines)
- Estimated duration
- Difficulty level (Beginner, Intermediate, Advanced)
- Progress indicator (not started, in progress, completed)
- Thumbnail (small, optional)
3. **Progress Tracking**
- Visual progress bar per tutorial
- Overall progress stats ("5 of 12 completed")
- "Continue where you left off" section at top
- Reset progress option
4. **Filtering & Search**
- Search tutorials by name/description
- Filter by difficulty
- Filter by category
- Filter by progress (Not Started, In Progress, Completed)
5. **Tutorial Detail View**
- Expanded description
- Learning objectives
- Prerequisites
- "Start Tutorial" / "Continue" / "Restart" button
- Estimated time remaining (for in-progress)
6. **Additional Content Types**
- Video tutorials (embedded or linked)
- Written guides
- Interactive lessons (existing)
- External resources
### Non-Functional Requirements
- Fast loading (tutorials list cached)
- Works offline for previously loaded tutorials
- Responsive layout
- Accessible navigation
## Data Model
### Enhanced Tutorial Structure
```typescript
interface Tutorial {
id: string;
title: string;
description: string;
longDescription?: string;
category: TutorialCategory;
difficulty: 'beginner' | 'intermediate' | 'advanced';
estimatedMinutes: number;
type: 'interactive' | 'video' | 'guide';
thumbnailUrl?: string;
objectives?: string[];
prerequisites?: string[];
// For interactive tutorials
templateUrl?: string;
// For video tutorials
videoUrl?: string;
// For guides
guideUrl?: string;
}
interface TutorialCategory {
id: string;
name: string;
icon: IconName;
color: string;
order: number;
}
interface TutorialProgress {
tutorialId: string;
status: 'not-started' | 'in-progress' | 'completed';
lastAccessedAt: string;
completedAt?: string;
currentStep?: number;
totalSteps?: number;
}
```
### Default Categories
```typescript
const TUTORIAL_CATEGORIES: TutorialCategory[] = [
{ id: 'getting-started', name: 'Getting Started', icon: IconName.Rocket, color: '#22C55E', order: 0 },
{ id: 'ui', name: 'Building UIs', icon: IconName.Palette, color: '#3B82F6', order: 1 },
{ id: 'data', name: 'Data & Logic', icon: IconName.Database, color: '#8B5CF6', order: 2 },
{ id: 'advanced', name: 'Advanced Topics', icon: IconName.Cog, color: '#F97316', order: 3 },
{ id: 'integrations', name: 'Integrations', icon: IconName.Plug, color: '#EC4899', order: 4 },
];
```
## Technical Approach
### 1. Tutorial Service
Extend or replace `LessonProjectsModel`:
```typescript
// packages/noodl-editor/src/editor/src/services/TutorialService.ts
class TutorialService {
private static instance: TutorialService;
// Data fetching
async fetchTutorials(): Promise<Tutorial[]>;
async getTutorialById(id: string): Promise<Tutorial | null>;
// Progress
getProgress(tutorialId: string): TutorialProgress;
updateProgress(tutorialId: string, progress: Partial<TutorialProgress>): void;
resetProgress(tutorialId: string): void;
// Queries
getInProgressTutorials(): Tutorial[];
getCompletedTutorials(): Tutorial[];
getTutorialsByCategory(categoryId: string): Tutorial[];
}
```
### 2. Component Structure
```
packages/noodl-core-ui/src/preview/launcher/Launcher/
├── views/
│ └── LearningCenter/
│ ├── LearningCenter.tsx # Main view
│ ├── LearningCenter.module.scss
│ ├── ContinueLearning.tsx # "Continue" section
│ ├── TutorialCategory.tsx # Category section
│ └── TutorialFilters.tsx # Filter bar
├── components/
│ ├── TutorialCard/
│ │ ├── TutorialCard.tsx # Compact card
│ │ ├── TutorialCard.module.scss
│ │ └── index.ts
│ ├── TutorialDetailModal/
│ │ ├── TutorialDetailModal.tsx # Expanded detail view
│ │ └── TutorialDetailModal.module.scss
│ ├── DifficultyBadge/
│ │ └── DifficultyBadge.tsx # Beginner/Intermediate/Advanced
│ ├── ProgressRing/
│ │ └── ProgressRing.tsx # Circular progress indicator
│ └── DurationLabel/
│ └── DurationLabel.tsx # "15 min" display
```
### 3. Learning Center Layout
```
┌─────────────────────────────────────────────────────────────────────┐
│ Learn [🔍 Search... ] │
├─────────────────────────────────────────────────────────────────────┤
│ Filters: [All ▾] [All Difficulties ▾] [All Progress ▾] [Clear] │
├─────────────────────────────────────────────────────────────────────┤
│ ⏸️ Continue Learning │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📚 OpenNoodl Basics 47% [●●●●●○○○○○] [Continue →] │ │
│ │ Data-driven Components 12% [●○○○○○○○○○] [Continue →] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ 🚀 Getting Started ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ AI Walkthru │ │ Basics │ │ Layout │ │
│ │ 🟢 Beginner │ │ 🟢 Beginner │ │ 🟢 Beginner │ │
│ │ 15 min │ │ 15 min │ │ 15 min │ │
│ │ ✓ Complete │ │ ● 47% │ │ ○ Not started│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ 🎨 Building UIs ▼ │
│ ... │
└─────────────────────────────────────────────────────────────────────┘
```
## Files to Create
1. `packages/noodl-editor/src/editor/src/services/TutorialService.ts`
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter/LearningCenter.tsx`
3. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter/LearningCenter.module.scss`
4. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter/ContinueLearning.tsx`
5. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter/TutorialCategory.tsx`
6. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter/TutorialFilters.tsx`
7. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TutorialCard/TutorialCard.tsx`
8. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TutorialCard/TutorialCard.module.scss`
9. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/TutorialDetailModal/TutorialDetailModal.tsx`
10. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/DifficultyBadge/DifficultyBadge.tsx`
11. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/ProgressRing/ProgressRing.tsx`
12. `packages/noodl-core-ui/src/preview/launcher/Launcher/components/DurationLabel/DurationLabel.tsx`
13. `packages/noodl-core-ui/src/preview/launcher/Launcher/hooks/useTutorials.ts`
## Files to Modify
1. `packages/noodl-core-ui/src/preview/launcher/Launcher/views/LearningCenter.tsx`
- Replace empty component with full implementation
- Move to folder structure
2. `packages/noodl-core-ui/src/preview/launcher/Launcher/Launcher.tsx`
- Update import for LearningCenter
3. `packages/noodl-editor/src/editor/src/models/lessonprojectsmodel.ts`
- Either extend or create adapter for new TutorialService
## Implementation Steps
### Phase 1: Data Layer
1. Create TutorialService
2. Define data types
3. Create useTutorials hook
4. Migrate existing lesson data structure
### Phase 2: Core Components
1. Create TutorialCard component
2. Create DifficultyBadge
3. Create ProgressRing
4. Create DurationLabel
### Phase 3: Main Layout
1. Build LearningCenter view
2. Create TutorialCategory sections
3. Add ContinueLearning section
4. Implement category collapse/expand
### Phase 4: Filtering
1. Create TutorialFilters component
2. Implement search
3. Implement filter dropdowns
4. Wire up filter state
### Phase 5: Detail View
1. Create TutorialDetailModal
2. Add start/continue/restart logic
3. Show objectives and prerequisites
### Phase 6: Polish
1. Add loading states
2. Add empty states
3. Smooth animations
4. Accessibility review
## Component Specifications
### TutorialCard
```
┌────────────────────────────┐
│ [📹] OpenNoodl Basics │ <- Type icon + Title
│ Learn the fundamentals │ <- Description (truncated)
│ 🟢 Beginner ⏱️ 15 min │ <- Difficulty + Duration
│ [●●●●●○○○○○] 47% │ <- Progress bar
└────────────────────────────┘
```
Props:
- `tutorial: Tutorial`
- `progress: TutorialProgress`
- `onClick: () => void`
- `variant?: 'compact' | 'expanded'`
### DifficultyBadge
| Level | Color | Icon |
|-------|-------|------|
| Beginner | Green (#22C55E) | 🟢 |
| Intermediate | Yellow (#EAB308) | 🟡 |
| Advanced | Red (#EF4444) | 🔴 |
### ProgressRing
Small circular progress indicator:
- Size: 24px
- Stroke width: 3px
- Background: gray
- Fill: green (completing), green (complete)
- Center: percentage or checkmark
## Compatibility Notes
### Existing Lesson System
The current system uses:
```typescript
// lessonprojectsmodel.ts
interface LessonTemplate {
name: string;
description: string;
iconURL: string;
templateURL: string;
progress?: number;
}
```
The new system should:
1. Be backwards compatible with existing templates
2. Migrate progress data from old format
3. Support new enhanced metadata
### Migration Path
1. Keep `lessonprojectsmodel.ts` working during transition
2. Create adapter in TutorialService to read old data
3. Enhance existing tutorials with new metadata
4. Eventually deprecate old model
## Testing Checklist
- [ ] Tutorials load from docs endpoint
- [ ] Categories display correctly
- [ ] Category collapse/expand works
- [ ] Progress displays correctly
- [ ] Continue Learning section shows in-progress tutorials
- [ ] Search filters tutorials
- [ ] Difficulty filter works
- [ ] Progress filter works
- [ ] Clicking card shows detail modal
- [ ] Start Tutorial launches tutorial
- [ ] Continue Tutorial resumes from last point
- [ ] Restart Tutorial resets progress
- [ ] Progress persists across sessions
- [ ] Empty states display appropriately
- [ ] Responsive at different window sizes
## Dependencies
- DASH-001 (Tabbed Navigation System)
### External Dependencies
None - uses existing noodl-core-ui components.
## Blocked By
- DASH-001
## Blocks
- None
## Estimated Effort
- TutorialService: 2-3 hours
- TutorialCard components: 2-3 hours
- LearningCenter layout: 3-4 hours
- Filtering: 2-3 hours
- Detail modal: 2-3 hours
- Polish & testing: 2-3 hours
- **Total: 13-19 hours**
## Success Criteria
1. Tutorials are organized by category
2. Users can easily find tutorials by search/filter
3. Progress is clearly visible
4. "Continue Learning" helps users resume work
5. Tutorial cards are compact but informative
6. Detail modal provides all needed information
7. System is backwards compatible with existing tutorials
## Future Enhancements
- Video tutorial playback within app
- Community-contributed tutorials
- Tutorial recommendations based on usage
- Learning paths (curated sequences)
- Achievements/badges for completion
- Tutorial ratings/feedback
## Design Notes
The learning center should feel like:
- Duolingo's course browser (compact, progress-focused)
- Coursera's course catalog (categorized, searchable)
- VS Code's Getting Started (helpful, not overwhelming)
Prioritize getting users to relevant content quickly. The most common flow is:
1. See "Continue Learning" → resume last tutorial
2. Browse category → find new tutorial → start
3. Search for specific topic → find tutorial → start
Don't make users click through multiple screens to start learning.

View File

@@ -0,0 +1,150 @@
# DASH Series: Dashboard UX Foundation
## Overview
The DASH series modernizes the OpenNoodl editor dashboard, transforming it from a basic project launcher into a proper workspace management hub. These tasks focus on the **new React 19 launcher** in `packages/noodl-core-ui/src/preview/launcher/`.
## Target Environment
- **Editor**: React 19 version only
- **Runtime**: React 19 version (if applicable)
- **Backwards Compatibility**: Not required for old launcher
## Task Dependency Graph
```
DASH-001 (Tabbed Navigation)
├── DASH-002 (Project List Redesign)
│ │
│ └── DASH-003 (Project Organization)
└── DASH-004 (Tutorial Section Redesign)
```
## Task Summary
| Task ID | Name | Est. Hours | Priority |
|---------|------|------------|----------|
| DASH-001 | Tabbed Navigation System | 5-8 | Critical |
| DASH-002 | Project List Redesign | 9-14 | High |
| DASH-003 | Project Organization | 16-22 | Medium |
| DASH-004 | Tutorial Section Redesign | 13-19 | Medium |
**Total Estimated: 43-63 hours**
## Implementation Order
### Week 1: Foundation
1. **DASH-001** - Tabbed navigation (foundation for everything)
2. **DASH-004** - Tutorial redesign (can parallel with DASH-002)
### Week 2: Project Management
3. **DASH-002** - Project list redesign
4. **DASH-003** - Folders and tags
## Key Technical Decisions
### Location
All new components go in:
```
packages/noodl-core-ui/src/preview/launcher/Launcher/
```
### State Management
- Use React Context for launcher-wide state
- Use electron-store for persistence
- Keep component state minimal
### Styling
- Use existing noodl-core-ui components
- CSS Modules for custom styling
- Follow existing color/spacing tokens
### Data
- Services in `packages/noodl-editor/src/editor/src/services/`
- Hooks in launcher `hooks/` folder
- Types in component folders or shared types file
## Shared Components to Create
These components will be reused across DASH tasks:
| Component | Created In | Used By |
|-----------|------------|---------|
| TabBar | DASH-001 | All views |
| GitStatusBadge | DASH-002 | Project list |
| ViewModeToggle | DASH-002 | Project list |
| FolderTree | DASH-003 | Sidebar |
| TagPill | DASH-003 | Project rows |
| ProgressRing | DASH-004 | Tutorial cards |
| DifficultyBadge | DASH-004 | Tutorial cards |
## Testing Strategy
Each task includes a testing checklist. Additionally:
1. **Visual Testing**: Use Storybook for component development
2. **Integration Testing**: Test in actual launcher context
3. **Persistence Testing**: Verify data survives app restart
4. **Performance Testing**: Check with 100+ projects/tutorials
## Cline Usage Notes
### Before Starting Each Task
1. Read the task document completely
2. Explore the existing code in `packages/noodl-core-ui/src/preview/launcher/`
3. Check existing components in `packages/noodl-core-ui/src/components/`
4. Understand the data flow
### During Implementation
1. Create components incrementally with Storybook stories
2. Test in isolation before integration
3. Update imports/exports in index files
4. Follow existing code style
### Confidence Checkpoints
Rate confidence (1-10) at these points:
- After reading task document
- After exploring existing code
- Before creating first component
- After completing each phase
- Before marking task complete
### Common Gotchas
1. **Mock Data**: The launcher currently uses mock data - don't try to connect to real data yet
2. **FIXME Alerts**: Many click handlers are `alert('FIXME: ...')` - that's expected
3. **Storybook**: Run `npm run storybook` in noodl-core-ui to test components
4. **Imports**: noodl-core-ui uses path aliases - check existing imports for patterns
## Success Criteria (Series Complete)
1. ✅ Launcher has tabbed navigation (Projects, Learn, Templates)
2. ✅ Projects display in sortable list with git status
3. ✅ Projects can be organized with folders and tags
4. ✅ Tutorials are organized by category with progress tracking
5. ✅ All preferences persist across sessions
6. ✅ UI is responsive and accessible
7. ✅ New components are reusable
## Future Work (Post-DASH)
The DASH series sets up infrastructure for:
- **GIT series**: GitHub integration, sync status
- **COMP series**: Shared components system
- **AI series**: AI project creation
- **DEPLOY series**: Deployment automation
These will be documented separately.
## Files in This Series
- `DASH-001-tabbed-navigation.md`
- `DASH-002-project-list-redesign.md`
- `DASH-003-project-organization.md`
- `DASH-004-tutorial-section-redesign.md`
- `DASH-OVERVIEW.md` (this file)