Skip to content

Commit 61ebd39

Browse files
authored
#9: Broadcast mode (#25)
Closes #9 and #17 * #9: WIP First steps into integrating party kit. Right now we can distinguish between instructor and atendee, to be able to send and receive the code for App.vue and update it realtime in the repl. * #9: Increasing functionality * Sending full files content to the clients * Distinguishing messages between system and repl-update * Creating a fixed id for instructor and variable ones for attendees * #9: WIP Cursors follow instructor now. This was a huge battle. I had to bring in some help from Gemini on VSCode or I couldn't have figured it out (or could I?) * #9: Fixing lint issues. * #9: Bringing in vue-repl fork. Otherwise the CI wont work. * #9: WIP Syncing of selections and cursor also working. Still missing syncing of multiple files. * build: Ignore nested package repositories * #9: WIP Real time cursor, selections, active file and order of the files work The gotcha: there's a memory leak * Build works, dev has warnings but seems to work. * #9: Abstracting the repl sync logic into a composable * #9: Compressing / decompressing the messages to be sent over the wire. * #9: fixing late joiner not syncing with current instructor's state. It requires an update in the repl though * #9: Sending instructors update right after they connect to the server * #9: Syncing the versions of monaco in the yv repo according to what vue-repl has. Finally both builds work again * fix: lint fix * #9: Splitting types of messages being sent by instructor Showing a toast for when receiving a system message. * #9: Major refactor on the repl sync composable. It should be more efficient now that it loads a modified version of the repl store that has more granular contol over the updates. It also now shows a a toast when the users connect to a workshop. And a 'ghost' cursor from the instructor. * #9: Syncing the full state for late joiners. Including current selections. * #9: Sync is ALMOST complete. Now we are only missing loading the proper sytles on the preview when the late joiners get the first update from the instructor. * #9: Leveraging new method in the repl store to sync instructor with late joiners seamlessly. * #9: Improving efficiency by only sending full_sync when actually required. * WIP * Refactor to move partykit composable into a pinia store. It has state that is shared and is reactive, so this is the quickest way to implement it. Also, implementing an icon status for the instuctor's presence. A toggle button for connecting / disconnecting from the workshop. TBD: if we bring in a switch instead. * Sort of working. Way too much memory in use. * Adding an instructios file * Updating pnpm * #9: Refactored to get proper behavior after introducing a way to disconnect/reconnect * #9: Updating flow. Fixing some minor details with real time. * Convert packages from submodules to regular files - Removed .git directories from packages/party-kit and packages/vue-repl - Packages will be converted to git subtrees next - This enables proper pnpm workspace integration and tooling * Remove party-kit to prepare for subtree conversion * Squashed 'packages/party-kit/' content from commit d10e4e0 git-subtree-dir: packages/party-kit git-subtree-split: d10e4e0db8b44c961bea1d3ed34e8056158abdc8 * Remove vue-repl to prepare for subtree conversion * Squashed 'packages/vue-repl/' content from commit c9a0b84 git-subtree-dir: packages/vue-repl git-subtree-split: c9a0b84 * Updating ESLint configuration. Party kit will use the same rules as yv and vue-repl will have its own rules * Adding a documentation file to provide more context when describing the changes that were done to vue-repl. Also adding scripts to pull from the repos if they were externally worked on. * Enhancing the approach to exposing the editor from the vue-repl * Adding pending work notes. Codemirror not exposed yet * Deprecating exposing the editor in the store It is now exposed through its own composable. * #9: Quick refactor to stop watching for the editor to be available * Addressing lint issues * #9: Abstracting duplicated types into their own package. I might convert this into its own repo * docs: updating readme and recommended extensions for VSCode * #9: Adding tests * #9: Refactoring online status Creating a component to display the online status for the user and the instructor Updating the icon for a more descriptive one Updating tests to reflect that change. * #9: Deprecating old message format for good. Updating tests to reflect this. * Adding testing to the pipeline * #9: WIP refactor for online status Implementing a 'pause' following the instructor instead of disconneting the attendees entirely. Abstracting a couple components that consume the Party Kit Store * #9: fixing tests * Swapping the dark and light themes for the ones used in amoxtli-vue * Renaming file to reflect the composable in a more conventional way * #9: Updating screenshots for readme.
1 parent 21cd7e0 commit 61ebd39

File tree

124 files changed

+22734
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+22734
-205
lines changed

.github/copilot-instructions.md

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
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

.github/workflows/pipeline.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,18 @@ jobs:
2525
run: pnpm install
2626
- name: Run linter
2727
run: pnpm lint
28+
29+
test:
30+
runs-on: ubuntu-22.04
31+
steps:
32+
- uses: actions/checkout@v4
33+
- name: Use Node.js from .node-version
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version-file: .node-version
37+
- name: Install pnpm
38+
uses: pnpm/action-setup@v4
39+
- name: Install dependencies
40+
run: pnpm install
41+
- name: Run Vitest unit tests (party-kit)
42+
run: pnpm --filter @calmecac-vue/party-kit test

0 commit comments

Comments
 (0)