feat(recording-panel): Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation

This commit is contained in:
2025-12-11 12:16:48 +00:00
parent 6cbfd714eb
commit 53c5d839ca
6 changed files with 418 additions and 145 deletions

View File

@@ -1,5 +1,14 @@
# Changelog
## 2025-12-11 - 1.3.0 - feat(recording-panel)
Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation
- Add dees-demowrapper (ts_demotools) with runAfterRender callback to run post-render demo logic (supports async callbacks).
- Improve recording UI and trimming: handle WebM files with Infinity/NaN durations by falling back to tracked recording duration; replace numeric handle positioning with CSS calc strings for responsive trim handles.
- Harden property extraction: implement recursive element search (including shadowRoots), add an initial delay and retry loop to wait for demo rendering, and add an advanced JSON editor for Object/Array properties with open/save/cancel and per-editor error reporting.
- Add and expand documentation: new ts_web/ and ts_demotools/ READMEs, reorganized main README with clearer feature list, usage examples, and API reference.
- Minor exports and module/docs housekeeping (index exports, readme reorder, examples updated to import classes).
## 2025-11-16 - 1.2.1 - fix(dependencies)
Bump dependencies and developer tooling versions

246
readme.md
View File

@@ -1,14 +1,18 @@
# @design.estate/dees-wcctools
Web Component Development Tools - A powerful framework for building, testing, and documenting web components
🛠️ **Web Component Development Tools** — A powerful framework for building, testing, documenting, and recording web components
## Overview
`@design.estate/dees-wcctools` provides a comprehensive development environment for web components, featuring:
- 🎨 Interactive component catalogue with live preview
- 🔧 Real-time property editing
- 🌓 Theme switching (light/dark modes)
- 📱 Responsive viewport testing
- 🧪 Advanced demo tools for component testing
- 🚀 Zero-config setup with TypeScript and Lit support
- 🎨 **Interactive Component Catalogue** — Live preview with sidebar navigation
- 🔧 **Real-time Property Editing** — Modify component props on the fly with auto-detected editors
- 🌓 **Theme Switching** — Test light/dark modes instantly
- 📱 **Responsive Viewport Testing** — Phone, phablet, tablet, and desktop views
- 🎬 **Screen Recording** — Record component demos with audio support and video trimming
- 🧪 **Advanced Demo Tools** — Post-render hooks for interactive testing
- 🚀 **Zero-config Setup** — TypeScript and Lit support out of the box
## Issue Reporting and Security
@@ -17,11 +21,11 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
## Installation
```bash
# Using npm
npm install @design.estate/dees-wcctools --save-dev
# Using pnpm (recommended)
pnpm add -D @design.estate/dees-wcctools
# Using npm
npm install @design.estate/dees-wcctools --save-dev
```
## Quick Start
@@ -89,8 +93,8 @@ import { setupWccTools } from '@design.estate/dees-wcctools';
import { html } from 'lit';
// Import your components
import './components/my-button.js';
import './components/my-card.js';
import { MyButton } from './components/my-button.js';
import { MyCard } from './components/my-card.js';
// Define elements for the catalogue
const elements = {
@@ -136,21 +140,28 @@ setupWccTools(elements, pages);
## Features
### 🎯 Live Property Editing
The properties panel automatically detects and allows editing of:
- **String** properties with text inputs
- **Number** properties with number inputs
- **Boolean** properties with checkboxes
- **Enum** properties with select dropdowns
- **Object** and **Array** properties (read-only display)
| Property Type | Editor |
|--------------|--------|
| **String** | Text input |
| **Number** | Number input |
| **Boolean** | Checkbox |
| **Enum** | Select dropdown |
| **Object/Array** | JSON editor modal |
### 📱 Viewport Testing
Test your components across different screen sizes:
- **Phone** (320px width)
- **Phablet** (600px width)
- **Tablet** (768px width)
- **Desktop** (full width)
- **Phone** — 320px width
- **Phablet** — 600px width
- **Tablet** — 768px width
- **Desktop** — Full width (native)
### 🌓 Theme Support
Components automatically adapt to light/dark themes using the `goBright` property:
```typescript
@@ -163,7 +174,8 @@ public render() {
}
```
Or use CSS custom properties:
Or use CSS custom properties with the theme manager:
```typescript
import { cssManager } from '@design.estate/dees-element';
@@ -177,39 +189,44 @@ public static styles = [
];
```
### 🧪 Advanced Demo Tools
### 🎬 Screen Recording
The demo tools provide enhanced testing capabilities:
Record component demos directly from the catalogue! The built-in recorder supports:
- **Viewport Recording** — Record just the component viewport
- **Full Screen Recording** — Capture the entire screen
- **Audio Support** — Add microphone commentary with live level monitoring
- **Video Trimming** — Trim start/end before export with visual timeline
- **WebM Export** — High-quality video output
Click the red record button in the bottom toolbar to start.
### 🧪 Demo Tools
The demotools module provides enhanced testing capabilities with `dees-demowrapper`:
```typescript
import * as demoTools from '@design.estate/dees-wcctools/demotools';
import '@design.estate/dees-wcctools/demotools';
@customElement('my-component')
export class MyComponent extends DeesElement {
public static demo = () => html`
<dees-demowrapper .runAfterRender=${async (wrapper) => {
// Use querySelector to find specific elements
const myComponent = wrapper.querySelector('my-component') as MyComponent;
console.log('Component found:', myComponent);
// Access all children via wrapper.children
console.log('Total children:', wrapper.children.length);
// Use querySelectorAll for multiple elements
const allDivs = wrapper.querySelectorAll('div');
console.log('Found divs:', allDivs.length);
// Find elements using standard DOM APIs
const myComponent = wrapper.querySelector('my-component');
// Simulate user interactions
myComponent.value = 'Test value';
await myComponent.updateComplete;
// Work with all children
Array.from(wrapper.children).forEach((child, index) => {
console.log(`Child ${index}:`, child.tagName);
// Work with multiple elements
wrapper.querySelectorAll('.item').forEach((el, i) => {
console.log(`Item ${i}:`, el.textContent);
});
}}>
<my-component></my-component>
<div>Additional content</div>
<div class="item">Item 1</div>
<div class="item">Item 2</div>
</dees-demowrapper>
`;
}
@@ -217,20 +234,18 @@ export class MyComponent extends DeesElement {
### ⏳ Async Demos
If your catalogue needs additional setup before rendering, return a `Promise` from the `demo` function. The dashboard waits for the result before inserting it into the viewport:
Return a `Promise` from `demo` for async setup. The dashboard waits for resolution:
```typescript
public static demo = async () => {
await Promise.resolve(); // e.g. fetch data, load fixtures, or await wrappers
return html`<my-component .value=${'Loaded asynchronously'}></my-component>`;
const data = await fetchSomeData();
return html`<my-component .data=${data}></my-component>`;
};
```
The same pattern works for page factories you pass into `setupWccTools`, enabling asynchronous data preparation across the entire demo surface.
### 🎭 Container Queries
### 🎭 Container Queries Support
Components can respond to their container size:
Components can respond to their container size using the `wccToolsViewport` container:
```typescript
public static styles = [
@@ -240,7 +255,7 @@ public static styles = [
flex-direction: row;
}
}
@container wccToolsViewport (max-width: 767px) {
:host {
flex-direction: column;
@@ -253,11 +268,13 @@ public static styles = [
## Component Guidelines
### Required for Catalogue Display
1. Components must expose a static `demo` property returning a Lit template
2. Use `@property()` decorators for properties you want to be editable
2. Use `@property()` decorators with the `accessor` keyword for editable properties
3. Export component classes for proper detection
### Best Practices
```typescript
@customElement('best-practice-component')
export class BestPracticeComponent extends DeesElement {
@@ -269,7 +286,7 @@ export class BestPracticeComponent extends DeesElement {
></best-practice-component>
`;
// ✅ Typed properties with defaults (TC39 decorator syntax with accessor)
// ✅ Typed properties with defaults (TC39 decorators)
@property({ type: String })
accessor title: string = 'Default Title';
@@ -286,28 +303,59 @@ export class BestPracticeComponent extends DeesElement {
## URL Routing
The catalogue uses URL routing for deep linking:
```
/wcctools-route/:type/:name/:viewport/:theme
Example:
Examples:
/wcctools-route/element/my-button/desktop/dark
/wcctools-route/page/home/tablet/bright
```
## Development Workflow
## API Reference
### Build and Watch
```json
{
"scripts": {
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element",
"watch": "tswatch element",
"serve": "http-server ./dist"
}
}
### `setupWccTools(elements, pages?)`
Initialize the WCC Tools dashboard.
| Parameter | Type | Description |
|-----------|------|-------------|
| `elements` | `Record<string, typeof LitElement>` | Map of element names to classes |
| `pages` | `Record<string, TTemplateFactory>` | Optional map of page names to template functions |
### `DeesDemoWrapper`
Component for wrapping demos with post-render logic.
| Property | Type | Description |
|----------|------|-------------|
| `runAfterRender` | `(wrapper) => void \| Promise<void>` | Callback after wrapped elements render |
The wrapper provides full DOM API access:
- `wrapper.querySelector()` — Find single element
- `wrapper.querySelectorAll()` — Find multiple elements
- `wrapper.children` — Access child elements directly
### Recording Components (Advanced)
For custom recording integrations:
```typescript
import { RecorderService } from '@design.estate/dees-wcctools';
const recorder = new RecorderService({
onDurationUpdate: (duration) => console.log(`${duration}s`),
onRecordingComplete: (blob) => console.log('Recording done!', blob),
onAudioLevelUpdate: (level) => console.log(`Audio: ${level}%`),
});
await recorder.startRecording({ mode: 'viewport' });
// ... later
recorder.stopRecording();
```
### Project Structure
## Project Structure
```
my-components/
├── src/
@@ -320,74 +368,12 @@ my-components/
└── package.json
```
## Advanced Features
### Custom Property Handlers
For complex property types, implement custom logic in your demo:
```typescript
public static demo = () => html`
<dees-demowrapper .runAfterRender=${(wrapper) => {
// Use querySelector to target specific elements
const component = wrapper.querySelector('my-component');
if (component) {
component.addEventListener('property-change', (e) => {
console.log('Property changed:', e.detail);
});
}
// Or handle all elements of a type
wrapper.querySelectorAll('my-component').forEach(el => {
el.addEventListener('click', () => console.log('Clicked!'));
});
}}>
<my-component></my-component>
</dees-demowrapper>
`;
```
### Responsive Testing Helpers
```typescript
import * as domtools from '@design.estate/dees-domtools';
public static styles = [
// Media query helpers
domtools.breakpoints.cssForPhone(css`
:host { font-size: 14px; }
`),
domtools.breakpoints.cssForTablet(css`
:host { font-size: 16px; }
`),
domtools.breakpoints.cssForDesktop(css`
:host { font-size: 18px; }
`)
];
```
## API Reference
### setupWccTools(elements, pages?)
Initialize the WCC Tools dashboard.
- `elements`: Object mapping element names to element classes
- `pages`: Optional object mapping page names to template functions
### DeesDemoWrapper
Component for wrapping demos with post-render logic.
- `runAfterRender`: Function called after the wrapped elements render
- Receives the wrapper element itself, providing full DOM API access
- Use `wrapper.querySelector()` and `wrapper.querySelectorAll()` for element selection
- Access children via `wrapper.children` property
- Supports async operations
## Browser Support
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers with Web Components support
- ✅ Chrome/Edge (latest)
- ✅ Firefox (latest)
- ✅ Safari (latest)
- ✅ Mobile browsers with Web Components support
## License and Legal Information

147
ts_demotools/readme.md Normal file
View File

@@ -0,0 +1,147 @@
# @design.estate/dees-wcctools/demotools
🧪 **Demo Wrapper Utilities** — Enhanced testing tools for web component demos
## Overview
The demotools module provides `dees-demowrapper`, a utility component for executing post-render logic in component demos. Perfect for simulating user interactions, setting up test data, or validating component state.
## Installation
This module is included with `@design.estate/dees-wcctools`:
```bash
pnpm add -D @design.estate/dees-wcctools
```
## Usage
Import the demotools subpath:
```typescript
import '@design.estate/dees-wcctools/demotools';
```
## DeesDemoWrapper
A wrapper component that executes a callback after its slotted content renders.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| `runAfterRender` | `(wrapper: DeesDemoWrapper) => void \| Promise<void>` | Callback executed after content renders |
### Example: Basic Usage
```typescript
import { html } from 'lit';
import '@design.estate/dees-wcctools/demotools';
public static demo = () => html`
<dees-demowrapper .runAfterRender=${(wrapper) => {
const button = wrapper.querySelector('my-button');
console.log('Button found:', button);
}}>
<my-button>Click Me</my-button>
</dees-demowrapper>
`;
```
### Example: Async Operations
```typescript
public static demo = () => html`
<dees-demowrapper .runAfterRender=${async (wrapper) => {
const form = wrapper.querySelector('my-form');
// Wait for component initialization
await form.updateComplete;
// Simulate user input
form.values = { name: 'Test User', email: 'test@example.com' };
// Trigger validation
await form.validate();
console.log('Form state:', form.isValid);
}}>
<my-form></my-form>
</dees-demowrapper>
`;
```
### Example: Multiple Elements
```typescript
public static demo = () => html`
<dees-demowrapper .runAfterRender=${(wrapper) => {
// Find all cards
const cards = wrapper.querySelectorAll('my-card');
console.log(`Found ${cards.length} cards`);
// Access by index
Array.from(wrapper.children).forEach((child, i) => {
console.log(`Child ${i}:`, child.tagName);
});
// Add event listeners
wrapper.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', () => console.log('Clicked!'));
});
}}>
<my-card title="Card 1"></my-card>
<my-card title="Card 2"></my-card>
<my-card title="Card 3"></my-card>
</dees-demowrapper>
`;
```
### Example: Component State Manipulation
```typescript
public static demo = () => html`
<dees-demowrapper .runAfterRender=${async (wrapper) => {
const tabs = wrapper.querySelector('my-tabs');
// Programmatically switch tabs
tabs.activeTab = 'settings';
await tabs.updateComplete;
// Verify content updated
const content = tabs.shadowRoot.querySelector('.tab-content');
console.log('Active content:', content.textContent);
}}>
<my-tabs>
<div slot="home">Home Content</div>
<div slot="settings">Settings Content</div>
</my-tabs>
</dees-demowrapper>
`;
```
## How It Works
1. The wrapper renders its slot content immediately
2. After a brief delay (50ms) to allow slotted content to initialize
3. The `runAfterRender` callback is invoked with the wrapper element
4. You have full DOM API access to query and manipulate children
## Key Features
- 📦 **Light DOM Access** — Slotted elements remain accessible via standard DOM APIs
- ⏱️ **Async Support** — Return a Promise for async operations
- 🎯 **Full DOM API** — Use `querySelector`, `querySelectorAll`, `children`, etc.
- 🛡️ **Error Handling** — Errors in callbacks are caught and logged
## CSS Behavior
The wrapper uses `display: contents` so it doesn't affect layout:
```css
:host {
display: contents;
}
```
This means the wrapper is "invisible" in the layout — its children render as if they were direct children of the wrapper's parent.

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-wcctools',
version: '1.2.1',
version: '1.3.0',
description: 'A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.'
}

View File

@@ -678,16 +678,16 @@ export class WccRecordingPanel extends DeesElement {
<div class="trim-track"></div>
<div
class="trim-selected"
style="left: ${this.getHandlePosition(this.trimStart)}px; right: ${this.getHandlePositionFromEnd(this.trimEnd)}px;"
style="left: ${this.getHandlePositionStyle(this.trimStart)}; right: ${this.getHandlePositionFromEndStyle(this.trimEnd)};"
></div>
<div
class="trim-handle start-handle"
style="left: ${this.getHandlePosition(this.trimStart)}px;"
style="left: ${this.getHandlePositionStyle(this.trimStart)};"
@mousedown=${(e: MouseEvent) => { e.stopPropagation(); this.isDraggingTrim = 'start'; }}
></div>
<div
class="trim-handle end-handle"
style="left: ${this.getHandlePosition(this.trimEnd)}px;"
style="left: ${this.getHandlePositionStyle(this.trimEnd)};"
@mousedown=${(e: MouseEvent) => { e.stopPropagation(); this.isDraggingTrim = 'end'; }}
></div>
</div>
@@ -850,9 +850,12 @@ export class WccRecordingPanel extends DeesElement {
// ==================== Trim Methods ====================
private handleVideoLoaded(video: HTMLVideoElement): void {
this.videoDuration = video.duration;
// WebM files from MediaRecorder may have Infinity/NaN duration
// Fall back to the tracked recording duration
const duration = Number.isFinite(video.duration) ? video.duration : this.recordingDuration;
this.videoDuration = duration;
this.trimStart = 0;
this.trimEnd = video.duration;
this.trimEnd = duration;
}
private formatDuration(seconds: number): string {
@@ -861,18 +864,23 @@ export class WccRecordingPanel extends DeesElement {
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
private getHandlePosition(time: number): number {
if (this.videoDuration === 0) return 12;
private getHandlePositionStyle(time: number): string {
if (this.videoDuration === 0) return '12px';
const percentage = time / this.videoDuration;
const trackWidth = 336;
return 12 + (percentage * trackWidth);
// Formula: 12px padding + percentage of remaining width (total - 24px padding)
// At 0%: 12px (left edge of track)
// At 100%: calc(100% - 12px) (right edge of track)
return `calc(12px + ${(percentage * 100).toFixed(2)}% - ${(percentage * 24).toFixed(2)}px)`;
}
private getHandlePositionFromEnd(time: number): number {
if (this.videoDuration === 0) return 12;
const percentage = (this.videoDuration - time) / this.videoDuration;
const trackWidth = 336;
return 12 + (percentage * trackWidth);
private getHandlePositionFromEndStyle(time: number): string {
if (this.videoDuration === 0) return '12px';
const percentage = time / this.videoDuration;
const remainingPercentage = 1 - percentage;
// For CSS 'right' property: distance from right edge
// At trimEnd = 100%: right = 12px (at right edge of track)
// At trimEnd = 0%: right = calc(100% - 12px) (at left edge of track)
return `calc(12px + ${(remainingPercentage * 100).toFixed(2)}% - ${(remainingPercentage * 24).toFixed(2)}px)`;
}
private handleTimelineClick(e: MouseEvent): void {

123
ts_web/readme.md Normal file
View File

@@ -0,0 +1,123 @@
# @design.estate/dees-wcctools
🛠️ **Web Component Catalogue Tools** — The core dashboard and UI components for building interactive component catalogues
## Overview
This is the main module of `@design.estate/dees-wcctools`, providing the complete dashboard experience for developing, testing, and documenting web components.
## Installation
```bash
pnpm add -D @design.estate/dees-wcctools
```
## Usage
```typescript
import { setupWccTools } from '@design.estate/dees-wcctools';
import { MyButton } from './components/my-button.js';
setupWccTools({
'my-button': MyButton,
});
```
## Exports
### Main Entry Point
| Export | Description |
|--------|-------------|
| `setupWccTools` | Initialize the component catalogue dashboard |
### Recording Components
| Export | Description |
|--------|-------------|
| `RecorderService` | Service class for screen/viewport recording |
| `WccRecordButton` | Record button UI component |
| `WccRecordingPanel` | Recording options and preview panel |
| `IRecorderEvents` | TypeScript interface for recorder callbacks |
| `IRecordingOptions` | TypeScript interface for recording options |
## Internal Components
The module includes these internal web components:
| Component | Description |
|-----------|-------------|
| `wcc-dashboard` | Main dashboard container with routing |
| `wcc-sidebar` | Navigation sidebar with element/page listing |
| `wcc-frame` | Iframe viewport with responsive sizing |
| `wcc-properties` | Property panel with live editing |
| `wcc-record-button` | Recording state indicator button |
| `wcc-recording-panel` | Recording workflow UI |
## RecorderService API
For programmatic recording control:
```typescript
import { RecorderService, type IRecorderEvents } from '@design.estate/dees-wcctools';
const events: IRecorderEvents = {
onDurationUpdate: (duration) => console.log(`Recording: ${duration}s`),
onRecordingComplete: (blob) => saveBlob(blob),
onAudioLevelUpdate: (level) => updateMeter(level),
onError: (error) => console.error(error),
onStreamEnded: () => console.log('User stopped sharing'),
};
const recorder = new RecorderService(events);
// Load available microphones
const mics = await recorder.loadMicrophones(true); // true = request permission
// Start audio level monitoring
await recorder.startAudioMonitoring(mics[0].deviceId);
// Start recording
await recorder.startRecording({
mode: 'viewport', // or 'screen'
audioDeviceId: mics[0].deviceId,
viewportElement: document.querySelector('.viewport'),
});
// Stop recording
recorder.stopRecording();
// Export trimmed video
const trimmedBlob = await recorder.exportTrimmedVideo(videoElement, startTime, endTime);
// Cleanup
recorder.dispose();
```
## Architecture
```
ts_web/
├── index.ts # Main exports
├── elements/
│ ├── wcc-dashboard.ts # Root dashboard component
│ ├── wcc-sidebar.ts # Navigation sidebar
│ ├── wcc-frame.ts # Responsive iframe viewport
│ ├── wcc-properties.ts # Property editing panel
│ ├── wcc-record-button.ts # Recording button
│ ├── wcc-recording-panel.ts # Recording options/preview
│ └── wcctools.helpers.ts # Shared utilities
├── services/
│ └── recorder.service.ts # MediaRecorder abstraction
└── pages/
└── index.ts # Built-in pages
```
## Features
- 🎨 Interactive component preview
- 🔧 Real-time property editing with type detection
- 🌓 Theme switching (light/dark)
- 📱 Responsive viewport testing
- 🎬 Screen recording with trimming
- 🔗 URL-based deep linking