From 53c5d839caa93de1f2f1cbfccb428144c97419ee Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Thu, 11 Dec 2025 12:16:48 +0000 Subject: [PATCH] feat(recording-panel): Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation --- changelog.md | 9 + readme.md | 246 ++++++++++++------------- ts_demotools/readme.md | 147 +++++++++++++++ ts_web/00_commitinfo_data.ts | 2 +- ts_web/elements/wcc-recording-panel.ts | 36 ++-- ts_web/readme.md | 123 +++++++++++++ 6 files changed, 418 insertions(+), 145 deletions(-) create mode 100644 ts_demotools/readme.md create mode 100644 ts_web/readme.md diff --git a/changelog.md b/changelog.md index 83ad7cd..6f52f66 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/readme.md b/readme.md index 475fd38..e665e0c 100644 --- a/readme.md +++ b/readme.md @@ -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` { - // 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); }); }}> -
Additional content
+
Item 1
+
Item 2
`; } @@ -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``; + const data = await fetchSomeData(); + return html``; }; ``` -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 { > `; - // โœ… 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` | Map of element names to classes | +| `pages` | `Record` | Optional map of page names to template functions | + +### `DeesDemoWrapper` + +Component for wrapping demos with post-render logic. + +| Property | Type | Description | +|----------|------|-------------| +| `runAfterRender` | `(wrapper) => void \| Promise` | 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` - { - // 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!')); - }); - }}> - - -`; -``` - -### 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 diff --git a/ts_demotools/readme.md b/ts_demotools/readme.md new file mode 100644 index 0000000..b55ad6f --- /dev/null +++ b/ts_demotools/readme.md @@ -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` | Callback executed after content renders | + +### Example: Basic Usage + +```typescript +import { html } from 'lit'; +import '@design.estate/dees-wcctools/demotools'; + +public static demo = () => html` + { + const button = wrapper.querySelector('my-button'); + console.log('Button found:', button); + }}> + Click Me + +`; +``` + +### Example: Async Operations + +```typescript +public static demo = () => html` + { + 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); + }}> + + +`; +``` + +### Example: Multiple Elements + +```typescript +public static demo = () => html` + { + // 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!')); + }); + }}> + + + + +`; +``` + +### Example: Component State Manipulation + +```typescript +public static demo = () => html` + { + 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); + }}> + +
Home Content
+
Settings Content
+
+
+`; +``` + +## 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. diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 486b109..4b3a3c8 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -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.' } diff --git a/ts_web/elements/wcc-recording-panel.ts b/ts_web/elements/wcc-recording-panel.ts index 572fbef..4abe107 100644 --- a/ts_web/elements/wcc-recording-panel.ts +++ b/ts_web/elements/wcc-recording-panel.ts @@ -678,16 +678,16 @@ export class WccRecordingPanel extends DeesElement {
{ e.stopPropagation(); this.isDraggingTrim = 'start'; }} >
{ e.stopPropagation(); this.isDraggingTrim = 'end'; }} >
@@ -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 { diff --git a/ts_web/readme.md b/ts_web/readme.md new file mode 100644 index 0000000..3b6788c --- /dev/null +++ b/ts_web/readme.md @@ -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