|
| 1 | +# AI Assistant Instructions for yehyecoa-vue |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +> **[yeh-yeh-CO-ah](https://nahuatl.wired-humanities.org/content/yeyecoa)** in Nahuatl means to try something; to try or experiment with something; to rehearse |
| 5 | +
|
| 6 | +Yehyecoa-vue is a collaborative Vue REPL (Read-Eval-Print Loop) platform originally designed to serve as the interactive playground component within [TutorialKit](https://tutorialkit.dev/) workshops. It extends the Vue Playground with: |
| 7 | + |
| 8 | +1. **Real-time Collaboration**: |
| 9 | + - Instructor-led code demonstrations |
| 10 | + - Live synced editing sessions |
| 11 | + - Cursor and selection synchronization |
| 12 | + - Uses PartyKit for WebSocket communication |
| 13 | + |
| 14 | +2. **Simplified Interface**: |
| 15 | + - Focused on basic Vue concepts |
| 16 | + - Removes version switching complexity |
| 17 | + - Hides compiled output for cleaner experience |
| 18 | + - Optimized for workshop learning scenarios |
| 19 | + |
| 20 | +3. **TutorialKit Integration**: |
| 21 | + - Seamlessly embeds in TutorialKit preview windows |
| 22 | + - Dynamically updates with lesson content changes |
| 23 | + - Integrates with TutorialKit's webcontainer environment |
| 24 | + - Enables interactive Vue.js tutorials and workshops |
| 25 | + |
| 26 | +While primarily designed for TutorialKit integration, it can also function as a standalone collaborative REPL for Vue.js workshops and pair programming sessions. |
| 27 | + |
| 28 | +### Workspace Structure |
| 29 | +This is a pnpm workspace project containing multiple packages: |
| 30 | + |
| 31 | +1. **Root Project**: Main application integrating all components |
| 32 | +2. **`packages/party-kit/`**: |
| 33 | + - PartyKit server implementation |
| 34 | + - Handles real-time WebSocket communication |
| 35 | + - Manages room state and instructor/attendee connections |
| 36 | + |
| 37 | +3. **`packages/vue-repl/`**: |
| 38 | + - Forked from official `@vue/repl` |
| 39 | + - Enhanced store management for better external control |
| 40 | + - Improved state exposure and management patterns |
| 41 | + - Makes REPL state more accessible for workshop use cases |
| 42 | + - No direct real-time features, but enables integration |
| 43 | + |
| 44 | +The workspace structure enables tight integration while maintaining separate concerns for: |
| 45 | +- Real-time server logic (party-kit) |
| 46 | +- Core REPL functionality and state management (vue-repl) |
| 47 | +- Main application features and real-time integration (root) |
| 48 | + |
| 49 | +### TutorialKit Integration |
| 50 | +The project is designed to work as a standalone application or as an embedded component within a [TutorialKit](https://tutorialkit.dev/) project: |
| 51 | + |
| 52 | +1. **Template Installation**: |
| 53 | + - When used in TutorialKit, the project is installed as a template in `amoxtli-vue/src/templates/yehyecoa/` |
| 54 | + - Build and install command: `npm bi` (builds project and copies `/dist` to template directory) |
| 55 | + |
| 56 | +2. **Lesson File Integration**: |
| 57 | + - `server.js` in the template directory watches for lesson file changes |
| 58 | + - Dynamic updates are pushed to the REPL editor through the server |
| 59 | + - Enables seamless integration with TutorialKit's webcontainer environment |
| 60 | + |
| 61 | +3. **File Change Detection**: |
| 62 | + ```javascript |
| 63 | + // amoxtli-vue/src/templates/yehyecoa/server.js |
| 64 | + // Notifies yehyecoa-vue when lesson file contents are updated |
| 65 | + // Enables dynamic content updates in the embedded REPL |
| 66 | + ``` |
| 67 | + |
| 68 | +## Technology Stack |
| 69 | + |
| 70 | +### State Management |
| 71 | +- **Pinia Stores**: |
| 72 | + - Central to application state management |
| 73 | + - Key stores in `/src/composables/`: |
| 74 | + ```typescript |
| 75 | + usePartyKitStore; // WebSocket and real-time state |
| 76 | + useFileManagerStore; // REPL file management |
| 77 | + useSettingsStore; // Application configuration |
| 78 | + useToastsStore; // UI notifications |
| 79 | + ``` |
| 80 | + - Leverages Vue Composition API integration |
| 81 | + - Enables clean separation of concerns |
| 82 | + - Facilitates state persistence where needed |
| 83 | + |
| 84 | +### UI Layer |
| 85 | +1. **UnoCSS Configuration**: |
| 86 | + - Uses UnoCSS with multiple presets for utility-first styling: |
| 87 | + ```typescript |
| 88 | + // uno.config.ts |
| 89 | + presets: [ |
| 90 | + presetWind4(), // Tailwind v4 compatible utilities |
| 91 | + presetAnimations(), // Animation utilities |
| 92 | + presetTypography(), // Typography utilities |
| 93 | + presetWebFonts(), // Web fonts support |
| 94 | + presetShadcn(), // shadcn integration |
| 95 | + presetIcons({ |
| 96 | + collections: { |
| 97 | + carbon: () => import('@iconify-json/carbon/icons.json'), |
| 98 | + mynaui: () => import('@iconify-json/mynaui/icons.json'), |
| 99 | + } |
| 100 | + }) |
| 101 | + ]; |
| 102 | + ``` |
| 103 | + |
| 104 | +2. **shadcn-vue Components**: |
| 105 | + - Based on the "New York" style variant |
| 106 | + - Uses CSS variables for theming |
| 107 | + - Key components in `/src/components/ui/`: |
| 108 | + - Sheet components for overlays |
| 109 | + - Custom scroll areas |
| 110 | + - Select components with virtual scrolling |
| 111 | + - Tooltips and popovers |
| 112 | + - Drawer components |
| 113 | + |
| 114 | +## Core Architecture |
| 115 | + |
| 116 | +### Component Structure |
| 117 | +- `/src/composables/`: Core state and sync functionality |
| 118 | + - `usePartyKitStore.ts`: Real-time communication store using PartyKit |
| 119 | + - `useReplSync.ts`: REPL state synchronization logic |
| 120 | + - `useFileManagerStore.ts`: File management and state |
| 121 | + |
| 122 | +### Real-time Collaboration Model |
| 123 | +- **Instructor-Attendee Pattern**: |
| 124 | + ```typescript |
| 125 | + // src/composables/useReplSync.ts |
| 126 | + const isInstructor = sessionStorage.getItem('isInstructor') === 'yes'; |
| 127 | + ``` |
| 128 | +- **Message Types**: |
| 129 | + ```typescript |
| 130 | + const AvailableMessageTypes = { |
| 131 | + FULL_SYNC: 'full_sync', // Complete REPL state sync |
| 132 | + BATCH_UPDATE: 'batch_update', // Incremental file changes |
| 133 | + SELECTION: 'selection', // Cursor/selection sync |
| 134 | + SCROLL: 'scroll', // Viewport sync |
| 135 | + SYSTEM: 'system' // System messages |
| 136 | + }; |
| 137 | + ``` |
| 138 | + |
| 139 | +### State Management |
| 140 | +1. **PartyKit Store** (`usePartyKitStore.ts`): |
| 141 | + - Manages WebSocket connections and real-time messaging |
| 142 | + - Handles instructor presence and connection state |
| 143 | + - Compression using `fflate` for efficient message transfer |
| 144 | + |
| 145 | +2. **REPL Sync** (`useReplSync.ts`): |
| 146 | + - Synchronizes code editor state between instructor and attendees |
| 147 | + - Handles file changes, selections, and scroll positions |
| 148 | + - Uses Monaco Editor's view state for precise editor sync |
| 149 | + |
| 150 | +## Key Workflows |
| 151 | + |
| 152 | +### 1. Real-time Synchronization |
| 153 | +The sync process follows a specific order: |
| 154 | +1. Full state sync on initial connection |
| 155 | +2. Incremental updates for file changes |
| 156 | +3. Real-time cursor and selection sync |
| 157 | +4. Scroll position synchronization |
| 158 | + |
| 159 | +### 2. File Management |
| 160 | +- Files are managed through batch operations: |
| 161 | + ```typescript |
| 162 | + type BatchUpdateOperation = { |
| 163 | + type: 'create' | 'update' | 'delete' | 'setActive'; |
| 164 | + filename: string; |
| 165 | + content?: string; |
| 166 | + }; |
| 167 | + ``` |
| 168 | + |
| 169 | +### 3. Error Handling |
| 170 | +Error states are managed through the toast system: |
| 171 | +```typescript |
| 172 | +// Example from useReplSync.ts |
| 173 | +catch (error) { |
| 174 | + console.error('Error during full sync:', error); |
| 175 | + toast.error('Failed to synchronize with instructor'); |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +## Integration Points |
| 180 | + |
| 181 | +### 1. PartyKit Server (`packages/party-kit/src/server.ts`) |
| 182 | +- Handles WebSocket connections and message routing |
| 183 | +- Maintains latest state for new connections |
| 184 | +- Manages instructor presence and activity |
| 185 | + |
| 186 | +### 2. Monaco Editor Integration |
| 187 | +- Editor state synchronization includes: |
| 188 | + - File content |
| 189 | + - Cursor positions |
| 190 | + - Selections |
| 191 | + - Scroll state |
| 192 | + - View state |
| 193 | + |
| 194 | +## Common Patterns |
| 195 | + |
| 196 | +### 1. State Compression |
| 197 | +Always compress state before sending over WebSocket: |
| 198 | +```typescript |
| 199 | +compressSync(strToU8(JSON.stringify(message))); |
| 200 | +``` |
| 201 | + |
| 202 | +### 2. File State Management |
| 203 | +Track file changes using local cache and diffs: |
| 204 | +```typescript |
| 205 | +const fileContents = new Map<string, string>(); |
| 206 | +// Update on changes |
| 207 | +watch(() => store.files, (newFilesRecord) => { |
| 208 | + const updates: BatchUpdateOperation[] = []; |
| 209 | + // ... compute changes |
| 210 | +}); |
| 211 | +``` |
| 212 | + |
| 213 | +### 3. Error Handling |
| 214 | +Use toast notifications for user feedback and console logs for debugging: |
| 215 | +```typescript |
| 216 | +toast.error('Failed to synchronize'); |
| 217 | +console.error('Error details:', error); |
| 218 | +``` |
| 219 | + |
| 220 | +## Project Setup |
| 221 | + |
| 222 | +### Standalone Mode |
| 223 | +1. Install dependencies: |
| 224 | + ```bash |
| 225 | + pnpm install |
| 226 | + ``` |
| 227 | + |
| 228 | +2. Run local PartyKit server: |
| 229 | + ```bash |
| 230 | + # In packages/party-kit |
| 231 | + pnpm dev |
| 232 | + ``` |
| 233 | + |
| 234 | +3. Development mode with hot reload: |
| 235 | + ```bash |
| 236 | + # In root project |
| 237 | + pnpm dev |
| 238 | + ``` |
| 239 | + |
| 240 | +4. For working on vue-repl package: |
| 241 | + ```bash |
| 242 | + # In packages/vue-repl |
| 243 | + pnpm dev |
| 244 | + ``` |
| 245 | + |
| 246 | +### TutorialKit Template Mode |
| 247 | +1. Build and install as template: |
| 248 | + ```bash |
| 249 | + npm bi # Builds and copies to amoxtli-vue template directory |
| 250 | + ``` |
| 251 | +2. The template's `server.js` will automatically: |
| 252 | + - Watch for lesson file changes |
| 253 | + - Update REPL content dynamically |
| 254 | + - Handle webcontainer integration |
| 255 | + |
| 256 | +## Code Style and Conventions |
| 257 | + |
| 258 | +### TypeScript and Vue Patterns |
| 259 | + |
| 260 | +1. **Function Declarations**: |
| 261 | + ```typescript |
| 262 | + // In components/composables, prefer function declarations for methods |
| 263 | + function handleFileChange() { |
| 264 | + // Implementation |
| 265 | + } |
| 266 | +
|
| 267 | + // Use arrow functions for callbacks in higher-order methods |
| 268 | + files.forEach((file) => { |
| 269 | + processFile(file); |
| 270 | + }); |
| 271 | + ``` |
| 272 | + |
| 273 | +2. **Variable Naming**: |
| 274 | + - Boolean values start with "is": `isConnected`, `isLoading` |
| 275 | + - Descriptive identifiers that convey purpose |
| 276 | + - Store instances can use abbreviated names for readability: |
| 277 | + ```typescript |
| 278 | + const pks = usePartyKitStore(); // PartyKit store |
| 279 | + const fm = useFileManagerStore(); // File manager store |
| 280 | + ``` |
| 281 | + |
| 282 | +3. **Destructuring Guidelines**: |
| 283 | + ```typescript |
| 284 | + // Good: Clear source context |
| 285 | + const { toast } = useToastsStore() |
| 286 | +
|
| 287 | + // Prefer: Maintain context for complex objects |
| 288 | + const store = useStore() |
| 289 | + store.files.forEach(...) // Clear that 'files' comes from store |
| 290 | + ``` |
| 291 | + |
| 292 | +4. **Store Composition**: |
| 293 | + ```typescript |
| 294 | + // Prefer composables with defineStore |
| 295 | + export const usePartyKitStore = defineStore('partyKitStore', () => { |
| 296 | + // State as refs |
| 297 | + const isConnected = ref(false); |
| 298 | + const currentRoomId = ref(''); |
| 299 | +
|
| 300 | + // Computed as readonly when exposed |
| 301 | + return { |
| 302 | + isConnected: readonly(isConnected), |
| 303 | + currentRoomId: readonly(currentRoomId) |
| 304 | + }; |
| 305 | + }); |
| 306 | + ``` |
| 307 | + |
| 308 | +5. **Type Definitions**: |
| 309 | + - Use TypeScript `type` over `interface` |
| 310 | + - Explicit message type definitions |
| 311 | + ```typescript |
| 312 | + type ReplMessageType = typeof AvailableMessageTypes[keyof typeof AvailableMessageTypes]; |
| 313 | + type ReplMessage<T extends ReplMessageType = ReplMessageType> = { |
| 314 | + type: T; |
| 315 | + id: string; |
| 316 | + payload: ReplMessagePayloads[T]; |
| 317 | + }; |
| 318 | + ``` |
| 319 | + |
| 320 | +3. **Vue Component Structure**: |
| 321 | + - Props and emits defined at top |
| 322 | + - Composables initialized next |
| 323 | + - Computed and reactive state follow |
| 324 | + - Methods grouped by functionality |
| 325 | + - Event handlers last |
| 326 | + |
| 327 | +4. **Error Handling**: |
| 328 | + ```typescript |
| 329 | + try { |
| 330 | + // Operation |
| 331 | + } |
| 332 | + catch (error) { |
| 333 | + console.error('Descriptive error:', error); |
| 334 | + toast.error('User-friendly message'); |
| 335 | + } |
| 336 | + ``` |
| 337 | + |
| 338 | +### Code Organization |
| 339 | +1. **File Structure**: |
| 340 | + - Composables in `/src/composables` |
| 341 | + - UI components in `/src/components/ui` |
| 342 | + - Stores follow pattern: `use[Feature]Store.ts` |
| 343 | + - Types near related functionality |
| 344 | + |
| 345 | +2. **Naming Conventions**: |
| 346 | + - Component files: PascalCase.vue |
| 347 | + - Composables: camelCase.ts |
| 348 | + - Custom events: kebab-case |
| 349 | + - Store exports: `use` prefix |
| 350 | + |
| 351 | +3. **Documentation**: |
| 352 | + - Clear type definitions |
| 353 | + - Comments for complex logic |
| 354 | + - Use JSDoc for public APIs |
| 355 | + - Include examples in comments |
| 356 | + |
| 357 | +### ESLint Configuration |
| 358 | +- Based on @antfu/eslint-config |
| 359 | +- Single quotes, 2-space indent |
| 360 | +- Strict TypeScript checks |
| 361 | +- Vue template formatting rules |
| 362 | +- Consistent component structure |
| 363 | + |
| 364 | +## Testing Guidelines |
| 365 | +- Unit tests should mock PartyKit connections |
| 366 | +- Test both instructor and attendee scenarios |
| 367 | +- Verify compression/decompression of messages |
| 368 | +- Test synchronization edge cases |
| 369 | + |
| 370 | +### Playwright E2E Testing |
| 371 | +- **UnoCSS Icon Selectors**: Icon utilities like `i-ph-broadcast-fill` become HTML attributes, not CSS classes |
| 372 | + ```typescript |
| 373 | + // ✅ Correct - UnoCSS creates attributes |
| 374 | + page.locator('[i-ph-broadcast-fill]') |
| 375 | + |
| 376 | + // ❌ Incorrect - looking for classes |
| 377 | + page.locator('[class*="i-ph-broadcast-fill"]') |
| 378 | + ``` |
| 379 | +- Use attribute selectors for UnoCSS icon utilities in tests |
| 380 | +- Test real-time collaboration with dual browser contexts |
0 commit comments