@ericrisco/rsc 3.3.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +3 -3
  2. package/manifest.json +43 -22
  3. package/package.json +1 -1
  4. package/scripts/lib/default-skill-floor.js +5 -1
  5. package/scripts/lib/domains.js +2 -2
  6. package/scripts/lib/retired-skills.js +4 -0
  7. package/scripts/rsc.js +1 -1
  8. package/skills/design/SKILL.md +4 -3
  9. package/skills/design/evals/cases.yaml +2 -2
  10. package/skills/ftd/SKILL.md +1 -1
  11. package/skills/react/SKILL.md +2 -1
  12. package/skills/ux/SKILL.md +149 -0
  13. package/skills/ux/THIRD_PARTY.md +32 -0
  14. package/skills/ux/evals/cases.yaml +40 -0
  15. package/skills/ux/references/flows.md +124 -0
  16. package/skills/ux/upstream/UPSTREAM.json +59 -0
  17. package/skills/ux/upstream/ux-designer/GUIDE.md +180 -0
  18. package/skills/ux/upstream/ux-designer/LICENSE +21 -0
  19. package/skills/ux/upstream/ux-designer/references/01-core-principles.md +251 -0
  20. package/skills/ux/upstream/ux-designer/references/02-laws-of-ux.md +330 -0
  21. package/skills/ux/upstream/ux-designer/references/03-accessibility.md +411 -0
  22. package/skills/ux/upstream/ux-designer/references/04-visual-design.md +433 -0
  23. package/skills/ux/upstream/ux-designer/references/05-information-architecture.md +432 -0
  24. package/skills/ux/upstream/ux-designer/references/06-interaction-design.md +495 -0
  25. package/skills/ux/upstream/ux-designer/references/07-forms-and-inputs.md +491 -0
  26. package/skills/ux/upstream/ux-designer/references/08-mobile-ux.md +539 -0
  27. package/skills/ux/upstream/ux-designer/references/09-ux-writing.md +493 -0
  28. package/skills/ux/upstream/ux-designer/references/10-user-research.md +533 -0
  29. package/skills/ux/upstream/ux-designer/references/11-design-systems.md +618 -0
  30. package/skills/ux/upstream/ux-designer/references/12a-presence-awareness.md +395 -0
  31. package/skills/ux/upstream/ux-designer/references/12b-conflict-resolution-sync.md +420 -0
  32. package/skills/ux/upstream/ux-designer/references/13a-canvas-navigation.md +388 -0
  33. package/skills/ux/upstream/ux-designer/references/13b-canvas-objects-performance.md +559 -0
  34. package/skills/ux/upstream/ux-designer/references/14-ai-ux-patterns.md +511 -0
  35. package/skills/ux/upstream/ux-designer/references/15-ethical-design.md +419 -0
  36. package/skills/ux/upstream/ux-designer/references/16-onboarding.md +430 -0
  37. package/skills/ux/upstream/ux-designer/references/17-notifications.md +461 -0
  38. package/skills/ux/upstream/ux-designer/references/18-data-visualization.md +453 -0
  39. package/skills/ux/upstream/ux-designer/references/19-search-ux.md +479 -0
  40. package/skills/ux/upstream/ux-designer/references/20-emotional-design.md +382 -0
  41. package/skills/ux/upstream/ux-designer/references/21-data-tables.md +629 -0
  42. package/skills/ux/upstream/ux-designer/references/22-performance-ux.md +626 -0
  43. package/skills/ux/upstream/ux-designer/references/23-internationalization.md +347 -0
  44. package/skills/ux/upstream/ux-designer/references/24-voice-and-multimodal.md +308 -0
  45. package/skills/ux/upstream/ux-designer/references/25a-barcode-camera-scanning.md +342 -0
  46. package/skills/ux/upstream/ux-designer/references/25b-barcode-scanner-workflows.md +412 -0
  47. package/skills/ux/upstream/ux-designer/references/25c-barcode-display.md +277 -0
  48. package/skills/ux/upstream/ux-usability-foundations/GUIDE.md +285 -0
  49. package/skills/ux/upstream/ux-usability-foundations/LICENSE +21 -0
  50. package/skills/ux/upstream/ux-usability-foundations/references/anti-patterns.md +267 -0
  51. package/skills/ux/upstream/ux-usability-foundations/references/checklists.md +130 -0
  52. package/skills/ux/upstream/ux-usability-foundations/references/decision-prompts.md +340 -0
  53. package/skills/ux/upstream/ux-usability-foundations/references/principle-cards.md +449 -0
  54. package/skills/worktrees/SKILL.md +19 -5
  55. package/targets/branch-guard.mjs +59 -4
  56. package/targets/clone-bootstrap.mjs +1 -1
  57. package/targets/session-memory-adapter.mjs +2 -2
  58. package/targets/team-safe-start.mjs +1 -0
  59. package/targets/worktree-reaper.mjs +14 -1
  60. package/skills/ui-engineering/SKILL.md +0 -113
  61. package/skills/ui-engineering/evals/cases.yaml +0 -38
@@ -0,0 +1,559 @@
1
+ # Canvas Apps: Objects, Performance & Patterns
2
+
3
+ This reference covers smart guides, layer management, canvas elements, performance optimization, whiteboard-specific patterns, and accessibility for canvas-based applications.
4
+
5
+ ---
6
+
7
+ ## Contents
8
+
9
+ - Smart Guides & Snapping
10
+ - Layers & Hierarchy
11
+ - Canvas Elements
12
+ - Performance Optimization
13
+ - Whiteboard-Specific Patterns
14
+ - Accessibility
15
+ - Key Metrics
16
+ - Anti-Patterns
17
+ - Sources
18
+
19
+ ---
20
+
21
+ ## Smart Guides & Snapping
22
+
23
+ ### Alignment Guides
24
+
25
+ ```javascript
26
+ function calculateAlignmentGuides(draggedObject, allObjects) {
27
+ const guides = [];
28
+ const threshold = 5; // Snap within 5px
29
+
30
+ allObjects.forEach(other => {
31
+ if (other === draggedObject) return;
32
+
33
+ // Horizontal center alignment
34
+ if (Math.abs(draggedObject.centerY - other.centerY) < threshold) {
35
+ guides.push({
36
+ type: 'horizontal',
37
+ y: other.centerY,
38
+ from: Math.min(draggedObject.left, other.left),
39
+ to: Math.max(draggedObject.right, other.right)
40
+ });
41
+ }
42
+
43
+ // Vertical center alignment
44
+ if (Math.abs(draggedObject.centerX - other.centerX) < threshold) {
45
+ guides.push({
46
+ type: 'vertical',
47
+ x: other.centerX,
48
+ from: Math.min(draggedObject.top, other.top),
49
+ to: Math.max(draggedObject.bottom, other.bottom)
50
+ });
51
+ }
52
+
53
+ // Edge alignments (top, bottom, left, right)
54
+ // ... similar checks for edges
55
+ });
56
+
57
+ return guides;
58
+ }
59
+ ```
60
+
61
+ ### Smart Guide Visuals
62
+
63
+ ```css
64
+ /* Alignment guides */
65
+ .smart-guide {
66
+ position: absolute;
67
+ background: #f43f5e;
68
+ pointer-events: none;
69
+ z-index: 1000;
70
+ }
71
+
72
+ .smart-guide--horizontal {
73
+ height: 1px;
74
+ width: 100%;
75
+ }
76
+
77
+ .smart-guide--vertical {
78
+ width: 1px;
79
+ height: 100%;
80
+ }
81
+
82
+ /* Distance indicator */
83
+ .distance-indicator {
84
+ position: absolute;
85
+ padding: 2px 6px;
86
+ background: #f43f5e;
87
+ color: white;
88
+ font-size: 11px;
89
+ border-radius: 3px;
90
+ white-space: nowrap;
91
+ }
92
+ ```
93
+
94
+ ### Grid Snapping
95
+
96
+ ```javascript
97
+ const GRID_SIZE = 8; // 8px grid
98
+
99
+ function snapToGrid(value) {
100
+ return Math.round(value / GRID_SIZE) * GRID_SIZE;
101
+ }
102
+
103
+ function snapPosition(x, y) {
104
+ return {
105
+ x: snapToGrid(x),
106
+ y: snapToGrid(y)
107
+ };
108
+ }
109
+ ```
110
+
111
+ ### Snapping Options
112
+
113
+ ```
114
+ ┌─────────────────────────────────────────────┐
115
+ │ Snapping (⌘;) [Toggle] │
116
+ ├─────────────────────────────────────────────┤
117
+ │ ✓ Snap to grid │
118
+ │ ✓ Snap to objects │
119
+ │ ✓ Snap to guides │
120
+ │ Snap to pixel │
121
+ ├─────────────────────────────────────────────┤
122
+ │ Grid size: [8px ▼] │
123
+ └─────────────────────────────────────────────┘
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Layers & Hierarchy
129
+
130
+ ### Layer Panel
131
+
132
+ ```
133
+ ┌─────────────────────────────────────────────────────┐
134
+ │ Layers [+] [⋮] │
135
+ ├─────────────────────────────────────────────────────┤
136
+ │ ▼ 📁 Header 👁 🔒 │
137
+ │ │ ├─ 📄 Logo 👁 🔓 │
138
+ │ │ ├─ 📄 Navigation 👁 🔓 │
139
+ │ │ └─ 📄 Search 👁 🔓 │
140
+ │ ▶ 📁 Main Content 👁 🔓 │
141
+ │ ▶ 📁 Footer 👁 🔓 │
142
+ │ 📄 Background 👁 🔓 │
143
+ └─────────────────────────────────────────────────────┘
144
+ ```
145
+
146
+ ```css
147
+ /* Layer item */
148
+ .layer-item {
149
+ display: flex;
150
+ align-items: center;
151
+ padding: 4px 8px;
152
+ cursor: pointer;
153
+ }
154
+
155
+ .layer-item:hover {
156
+ background: #f3f4f6;
157
+ }
158
+
159
+ .layer-item--selected {
160
+ background: #dbeafe;
161
+ }
162
+
163
+ .layer-item--dragging {
164
+ opacity: 0.5;
165
+ }
166
+
167
+ /* Indent for hierarchy */
168
+ .layer-item[data-depth="1"] { padding-left: 20px; }
169
+ .layer-item[data-depth="2"] { padding-left: 36px; }
170
+ .layer-item[data-depth="3"] { padding-left: 52px; }
171
+ ```
172
+
173
+ ### Z-Index Operations
174
+
175
+ | Shortcut | Action |
176
+ |----------|--------|
177
+ | Cmd/Ctrl + ] | Bring forward |
178
+ | Cmd/Ctrl + [ | Send backward |
179
+ | Cmd/Ctrl + Shift + ] | Bring to front |
180
+ | Cmd/Ctrl + Shift + [ | Send to back |
181
+
182
+ ### Grouping
183
+
184
+ ```javascript
185
+ function groupSelection() {
186
+ const group = {
187
+ type: 'group',
188
+ id: generateId(),
189
+ children: [...selectedObjects],
190
+ bounds: calculateBounds(selectedObjects)
191
+ };
192
+
193
+ // Remove from root, add to group
194
+ selectedObjects.forEach(obj => {
195
+ removeFromCanvas(obj);
196
+ obj.parent = group;
197
+ });
198
+
199
+ addToCanvas(group);
200
+ setSelection([group]);
201
+ }
202
+
203
+ function ungroupSelection() {
204
+ selectedObjects.forEach(group => {
205
+ if (group.type !== 'group') return;
206
+
207
+ group.children.forEach(child => {
208
+ child.parent = null;
209
+ addToCanvas(child);
210
+ });
211
+
212
+ removeFromCanvas(group);
213
+ });
214
+ }
215
+ ```
216
+
217
+ | Shortcut | Action |
218
+ |----------|--------|
219
+ | Cmd/Ctrl + G | Group selection |
220
+ | Cmd/Ctrl + Shift + G | Ungroup selection |
221
+ | Double-click | Enter group (edit children) |
222
+ | Escape | Exit group |
223
+
224
+ ---
225
+
226
+ ## Canvas Elements
227
+
228
+ ### Sticky Notes
229
+
230
+ Common in whiteboard apps.
231
+
232
+ ```html
233
+ <div class="sticky-note" style="--sticky-color: #fef08a;">
234
+ <div class="sticky-note__content" contenteditable>
235
+ Type your idea here...
236
+ </div>
237
+ <div class="sticky-note__author">Alice</div>
238
+ </div>
239
+ ```
240
+
241
+ ```css
242
+ .sticky-note {
243
+ width: 200px;
244
+ min-height: 200px;
245
+ padding: 16px;
246
+ background: var(--sticky-color);
247
+ box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.15);
248
+ border-radius: 2px;
249
+ font-family: 'Comic Sans MS', cursive; /* Classic sticky feel */
250
+ }
251
+
252
+ .sticky-note__content {
253
+ font-size: 16px;
254
+ line-height: 1.4;
255
+ outline: none;
256
+ }
257
+
258
+ .sticky-note__author {
259
+ position: absolute;
260
+ bottom: 8px;
261
+ right: 8px;
262
+ font-size: 11px;
263
+ opacity: 0.6;
264
+ }
265
+ ```
266
+
267
+ **Sticky Note Colors:**
268
+ - Yellow (#fef08a) - General ideas
269
+ - Pink (#fbcfe8) - Questions
270
+ - Blue (#bfdbfe) - Insights
271
+ - Green (#bbf7d0) - Actions
272
+ - Orange (#fed7aa) - Concerns
273
+
274
+ ### Connectors & Arrows
275
+
276
+ ```javascript
277
+ // Simple line connector
278
+ function drawConnector(from, to, ctx) {
279
+ // Calculate connection points (center of objects)
280
+ const start = { x: from.centerX, y: from.centerY };
281
+ const end = { x: to.centerX, y: to.centerY };
282
+
283
+ // Draw line
284
+ ctx.beginPath();
285
+ ctx.moveTo(start.x, start.y);
286
+ ctx.lineTo(end.x, end.y);
287
+ ctx.stroke();
288
+
289
+ // Draw arrowhead
290
+ const angle = Math.atan2(end.y - start.y, end.x - start.x);
291
+ drawArrowhead(end, angle, ctx);
292
+ }
293
+ ```
294
+
295
+ ### Shapes Library
296
+
297
+ ```
298
+ ┌─────────────────────────────────────────────────────┐
299
+ │ Shapes │
300
+ ├─────────────────────────────────────────────────────┤
301
+ │ Basic │
302
+ │ ┌─┐ ○ ◇ △ ─ → ⬭ │
303
+ │ │
304
+ │ Flowchart │
305
+ │ ▭ ◇ ⬭ ▱ ⬡ │
306
+ │ │
307
+ │ Arrows │
308
+ │ → ⇒ ↔ ↕ ↗ │
309
+ └─────────────────────────────────────────────────────┘
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Performance Optimization
315
+
316
+ ### Viewport Culling
317
+
318
+ Only render objects visible in the current viewport.
319
+
320
+ ```javascript
321
+ function getVisibleObjects(camera, objects) {
322
+ const viewport = getViewportBounds(camera);
323
+
324
+ return objects.filter(obj =>
325
+ intersects(obj.bounds, viewport)
326
+ );
327
+ }
328
+
329
+ function render() {
330
+ const visibleObjects = getVisibleObjects(camera, allObjects);
331
+
332
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
333
+ ctx.save();
334
+
335
+ // Apply camera transform
336
+ ctx.translate(camera.x, camera.y);
337
+ ctx.scale(camera.zoom, camera.zoom);
338
+
339
+ // Only render visible objects
340
+ visibleObjects.forEach(obj => obj.render(ctx));
341
+
342
+ ctx.restore();
343
+ }
344
+ ```
345
+
346
+ ### Level of Detail (LOD)
347
+
348
+ Simplify rendering at low zoom levels.
349
+
350
+ ```javascript
351
+ function renderObject(obj, ctx, zoom) {
352
+ if (zoom < 0.1) {
353
+ // Very zoomed out: just show bounding box
354
+ ctx.fillRect(obj.x, obj.y, obj.width, obj.height);
355
+ } else if (zoom < 0.5) {
356
+ // Moderately zoomed out: simplified render
357
+ obj.renderSimplified(ctx);
358
+ } else {
359
+ // Normal/zoomed in: full detail
360
+ obj.renderFull(ctx);
361
+ }
362
+ }
363
+ ```
364
+
365
+ ### Performance Guidelines
366
+
367
+ | Technique | When to Apply |
368
+ |-----------|--------------|
369
+ | Viewport culling | Always for >100 objects |
370
+ | LOD rendering | Zoom levels <50% |
371
+ | Debounced render | Rapid camera movement |
372
+ | Canvas layering | Static vs. dynamic content |
373
+ | Web Workers | Heavy calculations |
374
+ | Virtual scrolling | Layer panel with 1000+ items |
375
+
376
+ ### Canvas Size Limits
377
+
378
+ ```
379
+ Practical limits:
380
+ - Browser canvas max: ~16,000 x 16,000 pixels
381
+ - Virtual canvas: Unlimited (render only visible portion)
382
+ - Object count: 10,000+ with proper culling
383
+ - Performance target: 60fps during pan/zoom
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Whiteboard-Specific Patterns
389
+
390
+ ### Timer / Timeboxing
391
+
392
+ ```html
393
+ <div class="timer">
394
+ <div class="timer__display">5:00</div>
395
+ <div class="timer__controls">
396
+ <button class="timer__btn--start">▶</button>
397
+ <button class="timer__btn--pause">⏸</button>
398
+ <button class="timer__btn--reset">↺</button>
399
+ </div>
400
+ </div>
401
+ ```
402
+
403
+ ### Voting Sessions
404
+
405
+ ```javascript
406
+ const votingSession = {
407
+ id: "vote-123",
408
+ topic: "Which design do we prefer?",
409
+ options: [
410
+ { id: "a", label: "Option A", votes: [] },
411
+ { id: "b", label: "Option B", votes: [] }
412
+ ],
413
+ anonymous: true,
414
+ allowMultiple: false,
415
+ endTime: new Date("2024-12-20T14:00:00")
416
+ };
417
+
418
+ function castVote(sessionId, optionId, userId) {
419
+ const session = getSession(sessionId);
420
+ const vote = session.anonymous ? "anonymous" : userId;
421
+
422
+ session.options.find(o => o.id === optionId).votes.push(vote);
423
+ broadcastVoteUpdate(session);
424
+ }
425
+ ```
426
+
427
+ ### Facilitation Mode
428
+
429
+ Presenter controls for workshops.
430
+
431
+ ```
432
+ ┌─────────────────────────────────────────────────────┐
433
+ │ Facilitation Tools │
434
+ ├─────────────────────────────────────────────────────┤
435
+ │ [Spotlight] [Timer] [Vote] [Lock Canvas] │
436
+ │ │
437
+ │ Participants: 12 active │
438
+ │ [Mute all annotations] [Bring everyone to me] │
439
+ └─────────────────────────────────────────────────────┘
440
+ ```
441
+
442
+ ### Clustering / Affinity Mapping
443
+
444
+ ```javascript
445
+ // Auto-cluster similar sticky notes
446
+ function clusterStickies(stickies) {
447
+ const clusters = [];
448
+
449
+ stickies.forEach(sticky => {
450
+ // Find cluster by proximity or AI similarity
451
+ const nearbyCluster = findNearbyCluster(sticky, clusters);
452
+
453
+ if (nearbyCluster) {
454
+ nearbyCluster.add(sticky);
455
+ } else {
456
+ clusters.push(new Cluster([sticky]));
457
+ }
458
+ });
459
+
460
+ return clusters;
461
+ }
462
+ ```
463
+
464
+ ---
465
+
466
+ ## Accessibility
467
+
468
+ ### Keyboard Navigation
469
+
470
+ ```javascript
471
+ // Full keyboard support for canvas
472
+ const keyboardControls = {
473
+ 'Tab': () => selectNextObject(),
474
+ 'Shift+Tab': () => selectPreviousObject(),
475
+ 'Enter': () => enterEditMode(),
476
+ 'Escape': () => exitEditMode(),
477
+ 'Delete': () => deleteSelection(),
478
+ 'ArrowUp': () => moveSelection(0, -1),
479
+ 'ArrowDown': () => moveSelection(0, 1),
480
+ 'ArrowLeft': () => moveSelection(-1, 0),
481
+ 'ArrowRight': () => moveSelection(1, 0),
482
+ 'Cmd+A': () => selectAll(),
483
+ 'Cmd+Z': () => undo(),
484
+ 'Cmd+Shift+Z': () => redo()
485
+ };
486
+ ```
487
+
488
+ ### Screen Reader Support
489
+
490
+ ```html
491
+ <!-- Announce canvas state changes -->
492
+ <div role="application" aria-label="Design canvas">
493
+ <div aria-live="polite" class="sr-only" id="canvas-announcer">
494
+ <!-- Dynamic announcements -->
495
+ </div>
496
+
497
+ <div
498
+ role="img"
499
+ tabindex="0"
500
+ aria-describedby="canvas-description"
501
+ >
502
+ <canvas></canvas>
503
+ </div>
504
+ </div>
505
+
506
+ <script>
507
+ function announceSelection(objects) {
508
+ const announcer = document.getElementById('canvas-announcer');
509
+ announcer.textContent = `Selected ${objects.length} objects`;
510
+ }
511
+ </script>
512
+ ```
513
+
514
+ ### Drag and Drop Alternatives
515
+
516
+ Per WCAG 2.5.7, provide non-dragging alternatives:
517
+
518
+ ```html
519
+ <!-- Context menu for move operations -->
520
+ <menu class="context-menu">
521
+ <button>Move to...</button>
522
+ <button>Bring to front</button>
523
+ <button>Send to back</button>
524
+ <button>Align left</button>
525
+ <button>Align center</button>
526
+ <button>Align right</button>
527
+ </menu>
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Key Metrics
533
+
534
+ | Metric | Value | Context |
535
+ |--------|-------|---------|
536
+ | Grid size | 4, 8, or 16px | Common options |
537
+ | Snap threshold | 2-8px | Distance before snapping |
538
+ | Undo stack | 100+ actions | Memory permitting |
539
+ | Object limit | 10,000+ | With culling |
540
+
541
+ ---
542
+
543
+ ## Anti-Patterns
544
+
545
+ 1. **No keyboard navigation** - Excludes users who can't use mouse
546
+ 2. **Missing undo** - Critical for experimental exploration
547
+ 3. **No snapping controls** - Users need to disable snapping sometimes
548
+ 4. **Invisible layers** - Hard to find/select hidden objects
549
+ 5. **No minimap** - Lost on large canvases
550
+
551
+ ---
552
+
553
+ ## Sources
554
+
555
+ - [Figma Engineering Blog](https://www.figma.com/blog/category/engineering/)
556
+ - [Miro - Infinite Canvas](https://miro.com/online-canvas-for-design/)
557
+ - [Konva.js Documentation](https://konvajs.org/)
558
+ - [LogRocket - Drag and Drop UI](https://blog.logrocket.com/ux-design/drag-and-drop-ui-examples/)
559
+ - [Cloudscape - Drag and Drop Patterns](https://cloudscape.design/patterns/general/drag-and-drop/)