@design.estate/wcctools 5.1.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 (55) hide show
  1. package/.smartconfig.json +65 -0
  2. package/changelog.md +414 -0
  3. package/dist_bundle/bundle.js +92419 -0
  4. package/dist_bundle/bundle.js.map +1 -0
  5. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  6. package/dist_ts_web/00_commitinfo_data.js +9 -0
  7. package/dist_ts_web/elements/wcc-contextmenu.d.ts +41 -0
  8. package/dist_ts_web/elements/wcc-contextmenu.js +329 -0
  9. package/dist_ts_web/elements/wcc-dashboard.d.ts +119 -0
  10. package/dist_ts_web/elements/wcc-dashboard.js +700 -0
  11. package/dist_ts_web/elements/wcc-frame.d.ts +22 -0
  12. package/dist_ts_web/elements/wcc-frame.js +256 -0
  13. package/dist_ts_web/elements/wcc-properties.d.ts +43 -0
  14. package/dist_ts_web/elements/wcc-properties.js +1147 -0
  15. package/dist_ts_web/elements/wcc-record-button.d.ts +12 -0
  16. package/dist_ts_web/elements/wcc-record-button.js +165 -0
  17. package/dist_ts_web/elements/wcc-recording-panel.d.ts +44 -0
  18. package/dist_ts_web/elements/wcc-recording-panel.js +1103 -0
  19. package/dist_ts_web/elements/wcc-sidebar.d.ts +91 -0
  20. package/dist_ts_web/elements/wcc-sidebar.js +1497 -0
  21. package/dist_ts_web/elements/wcctools.helpers.d.ts +46 -0
  22. package/dist_ts_web/elements/wcctools.helpers.js +106 -0
  23. package/dist_ts_web/index.d.ts +18 -0
  24. package/dist_ts_web/index.js +30 -0
  25. package/dist_ts_web/pages/index.d.ts +1 -0
  26. package/dist_ts_web/pages/index.js +2 -0
  27. package/dist_ts_web/services/recorder.service.d.ts +55 -0
  28. package/dist_ts_web/services/recorder.service.js +353 -0
  29. package/dist_ts_web/wcctools.interfaces.d.ts +29 -0
  30. package/dist_ts_web/wcctools.interfaces.js +2 -0
  31. package/dist_ts_web/wcctools.plugins.d.ts +2 -0
  32. package/dist_ts_web/wcctools.plugins.js +3 -0
  33. package/license.md +19 -0
  34. package/package.json +64 -0
  35. package/readme.md +568 -0
  36. package/ts_web/00_commitinfo_data.ts +8 -0
  37. package/ts_web/elements/wcc-contextmenu.ts +291 -0
  38. package/ts_web/elements/wcc-dashboard.ts +672 -0
  39. package/ts_web/elements/wcc-frame.ts +189 -0
  40. package/ts_web/elements/wcc-properties.ts +1089 -0
  41. package/ts_web/elements/wcc-record-button.ts +108 -0
  42. package/ts_web/elements/wcc-recording-panel.ts +1010 -0
  43. package/ts_web/elements/wcc-sidebar.ts +1470 -0
  44. package/ts_web/elements/wcctools.helpers.ts +153 -0
  45. package/ts_web/index.ts +38 -0
  46. package/ts_web/pages/index.ts +1 -0
  47. package/ts_web/readme.md +177 -0
  48. package/ts_web/services/recorder.service.ts +451 -0
  49. package/ts_web/tspublish.json +3 -0
  50. package/ts_web/types/dom-mediacapture-stub/index.d.ts +12 -0
  51. package/ts_web/types/dom-mediacapture-stub/package.json +6 -0
  52. package/ts_web/types/dom-webcodecs-stub/index.d.ts +2 -0
  53. package/ts_web/types/dom-webcodecs-stub/package.json +6 -0
  54. package/ts_web/wcctools.interfaces.ts +31 -0
  55. package/ts_web/wcctools.plugins.ts +5 -0
@@ -0,0 +1,153 @@
1
+ import type { TemplateResult } from 'lit';
2
+
3
+ export type TTemplateFactory = () => TemplateResult | Promise<TemplateResult>;
4
+
5
+ /** Find the nearest matching instance through light DOM and open shadow roots. */
6
+ export const findElementInstance = <T extends HTMLElement>(
7
+ containerArg: Element | ShadowRoot,
8
+ elementClassArg: new (...args: any[]) => T,
9
+ ): T | null => {
10
+ const pending = Array.from(containerArg.children);
11
+ for (let index = 0; index < pending.length; index++) {
12
+ const element = pending[index];
13
+ if (element instanceof elementClassArg) return element;
14
+ pending.push(...element.children);
15
+ if (element.shadowRoot) pending.push(...element.shadowRoot.children);
16
+ }
17
+ return null;
18
+ };
19
+
20
+ // Demo can be a single function or an array of functions
21
+ export type TDemoDefinition = TTemplateFactory | TTemplateFactory[];
22
+
23
+ /**
24
+ * How a catalog export is treated in an elements section:
25
+ * - `demo`: a custom element class with a usable static `demo`; listed and navigable
26
+ * - `no-demo`: a registered custom element without a usable demo; listed under "Without demo"
27
+ * - `ignored`: anything else (helpers, constants, types at runtime, abstract base classes)
28
+ */
29
+ export type TCatalogEntryKind = 'demo' | 'no-demo' | 'ignored';
30
+
31
+ /** The static surface wcctools reads from a catalog element class. */
32
+ export interface ICatalogElementConstructor extends CustomElementConstructor {
33
+ demo?: unknown;
34
+ demoGroups?: unknown;
35
+ }
36
+
37
+ /** A demo is one template factory or a non-empty array of template factories. */
38
+ export const isDemoDefinition = (demoArg: unknown): demoArg is TDemoDefinition => {
39
+ if (typeof demoArg === 'function') {
40
+ return true;
41
+ }
42
+ return Array.isArray(demoArg)
43
+ && demoArg.length > 0
44
+ && demoArg.every((factoryArg) => typeof factoryArg === 'function');
45
+ };
46
+
47
+ /** True for classes that extend HTMLElement (registered or not). */
48
+ export const isElementConstructor = (itemArg: unknown): itemArg is ICatalogElementConstructor => {
49
+ return typeof HTMLElement !== 'undefined'
50
+ && typeof itemArg === 'function'
51
+ && itemArg.prototype instanceof HTMLElement;
52
+ };
53
+
54
+ /** Classify one export of an elements section. */
55
+ export const classifyCatalogEntry = (itemArg: unknown): TCatalogEntryKind => {
56
+ if (!isElementConstructor(itemArg)) {
57
+ return 'ignored';
58
+ }
59
+ if (isDemoDefinition(itemArg.demo)) {
60
+ return 'demo';
61
+ }
62
+ return customElements.getName(itemArg) ? 'no-demo' : 'ignored';
63
+ };
64
+
65
+ /** The tag name an element class is registered under, if any. */
66
+ export const getElementTagName = (itemArg: unknown): string | null => {
67
+ return isElementConstructor(itemArg) ? customElements.getName(itemArg) : null;
68
+ };
69
+
70
+ /** The demo groups an element declares, normalized to an array. */
71
+ export const getElementDemoGroups = (itemArg: unknown): string[] => {
72
+ if (!isElementConstructor(itemArg)) {
73
+ return [];
74
+ }
75
+ const groups = itemArg.demoGroups;
76
+ const list = Array.isArray(groups) ? groups : [groups];
77
+ return list.filter((groupArg): groupArg is string => typeof groupArg === 'string' && groupArg.length > 0);
78
+ };
79
+
80
+ export type TPropertyType = 'String' | 'Number' | 'Boolean' | 'Object' | 'Enum' | 'Array';
81
+
82
+ export interface IReactivePropertyDeclaration {
83
+ state?: boolean;
84
+ type?: unknown;
85
+ }
86
+
87
+ const isEnumerationType = (typeArg: unknown): boolean => {
88
+ if (!typeArg || typeof typeArg !== 'object' || Array.isArray(typeArg)) {
89
+ return false;
90
+ }
91
+
92
+ const values = Object.values(typeArg);
93
+ return values.length > 0 && values.every((valueArg) =>
94
+ typeof valueArg === 'string' || typeof valueArg === 'number'
95
+ );
96
+ };
97
+
98
+ export const determinePropertyType = (
99
+ propertyArg: IReactivePropertyDeclaration
100
+ ): TPropertyType | null => {
101
+ if (!propertyArg || typeof propertyArg !== 'object' || propertyArg.state) {
102
+ return null;
103
+ }
104
+
105
+ switch (propertyArg.type) {
106
+ case String:
107
+ return 'String';
108
+ case Number:
109
+ return 'Number';
110
+ case Boolean:
111
+ return 'Boolean';
112
+ case Object:
113
+ return 'Object';
114
+ case Array:
115
+ return 'Array';
116
+ default:
117
+ return isEnumerationType(propertyArg.type) ? 'Enum' : null;
118
+ }
119
+ };
120
+
121
+ export const resolveTemplateFactory = async (
122
+ factoryArg: TTemplateFactory
123
+ ): Promise<TemplateResult> => {
124
+ return await Promise.resolve(factoryArg());
125
+ };
126
+
127
+ /**
128
+ * Get the number of demos for an element
129
+ */
130
+ export const getDemoCount = (demo: TDemoDefinition): number => {
131
+ if (Array.isArray(demo)) {
132
+ return demo.length;
133
+ }
134
+ return 1;
135
+ };
136
+
137
+ /**
138
+ * Get a specific demo by index (0-based internally, displayed as 1-based)
139
+ */
140
+ export const getDemoAtIndex = (demo: TDemoDefinition, index: number): TTemplateFactory | null => {
141
+ if (Array.isArray(demo)) {
142
+ return demo[index] ?? null;
143
+ }
144
+ // Single demo - only index 0 is valid
145
+ return index === 0 ? demo : null;
146
+ };
147
+
148
+ /**
149
+ * Check if an element has multiple demos
150
+ */
151
+ export const hasMultipleDemos = (demo: TDemoDefinition): boolean => {
152
+ return Array.isArray(demo) && demo.length > 1;
153
+ };
@@ -0,0 +1,38 @@
1
+ import { WccDashboard } from './elements/wcc-dashboard.js';
2
+ import type { IWccConfig, IWccSection } from './wcctools.interfaces.js';
3
+
4
+ // Export recording components and service
5
+ export { RecorderService, type IRecorderEvents, type IRecordingOptions, type TOutputFormat } from './services/recorder.service.js';
6
+ export { WccRecordButton } from './elements/wcc-record-button.js';
7
+ export { WccRecordingPanel } from './elements/wcc-recording-panel.js';
8
+
9
+ // Export types for external use
10
+ export type { IWccConfig, IWccSection } from './wcctools.interfaces.js';
11
+
12
+ /**
13
+ * Set up the WCC Tools dashboard:
14
+ * ```typescript
15
+ * setupWccTools({
16
+ * sections: [
17
+ * { name: 'Elements', type: 'elements', items: elements },
18
+ * { name: 'Pages', type: 'pages', items: pages },
19
+ * ]
20
+ * });
21
+ * ```
22
+ */
23
+ const setupWccTools = (config: IWccConfig) => {
24
+ let hasRun = false;
25
+ const runWccToolsSetup = async () => {
26
+ if (document.readyState === 'complete' && !hasRun) {
27
+ hasRun = true;
28
+ const wccTools = new WccDashboard(config);
29
+ document.querySelector('body').append(wccTools);
30
+ }
31
+ };
32
+ document.addEventListener('readystatechange', runWccToolsSetup);
33
+ runWccToolsSetup();
34
+ };
35
+
36
+ export {
37
+ setupWccTools
38
+ };
@@ -0,0 +1 @@
1
+ export const page1 = null;
@@ -0,0 +1,177 @@
1
+ # @design.estate/wcctools
2
+
3
+ 🛠️ **Web Component Catalogue Tools** — The core dashboard and UI components for building interactive component catalogues
4
+
5
+ ## Overview
6
+
7
+ This is the main module of `@design.estate/wcctools`, providing the complete dashboard experience for developing, testing, and documenting web components.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pnpm add -D @design.estate/wcctools
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### Sections-based Configuration (Recommended)
18
+
19
+ ```typescript
20
+ import { setupWccTools } from '@design.estate/wcctools';
21
+ import * as elements from './elements/index.js';
22
+ import * as views from './views/index.js';
23
+ import * as pages from './pages/index.js';
24
+
25
+ setupWccTools({
26
+ sections: [
27
+ {
28
+ name: 'Pages',
29
+ type: 'pages',
30
+ items: pages,
31
+ },
32
+ {
33
+ name: 'Views',
34
+ type: 'elements',
35
+ items: views,
36
+ icon: 'web',
37
+ },
38
+ {
39
+ name: 'Elements',
40
+ type: 'elements',
41
+ items: elements,
42
+ filter: (name) => !name.startsWith('internal-'),
43
+ sort: ([a], [b]) => a.localeCompare(b),
44
+ },
45
+ ],
46
+ });
47
+ ```
48
+
49
+ ### Migration from the Two-Argument Form
50
+
51
+ `setupWccTools(elements, pages)` is removed in 5.0.0; pass the same maps as `pages` and `elements` sections.
52
+
53
+ ### What the Sidebar Lists
54
+
55
+ In an `elements` section, only classes extending `HTMLElement` with a static `demo` are listed and navigable. Registered custom elements without a demo appear in a collapsed, non-clickable **Without demo** group; every other export (helpers, constants, unregistered base classes) is ignored. Classification runs before the section's `filter` and `sort`. `pages` sections list template factories only. See the package readme for details and keyboard use.
56
+
57
+ ## Exports
58
+
59
+ ### Main Entry Point
60
+
61
+ | Export | Description |
62
+ |--------|-------------|
63
+ | `setupWccTools` | Initialize the component catalogue dashboard |
64
+ | `IWccConfig` | TypeScript interface for sections configuration |
65
+ | `IWccSection` | TypeScript interface for individual section |
66
+
67
+ ### Recording Components
68
+
69
+ | Export | Description |
70
+ |--------|-------------|
71
+ | `RecorderService` | Service class for screen/viewport recording and MP4 conversion |
72
+ | `WccRecordButton` | Record button UI component |
73
+ | `WccRecordingPanel` | Recording options and preview panel |
74
+ | `IRecorderEvents` | TypeScript interface for recorder callbacks |
75
+ | `IRecordingOptions` | TypeScript interface for recording options |
76
+ | `TOutputFormat` | Type for output format selection (`'webm' \| 'mp4'`) |
77
+
78
+ ## Section Configuration
79
+
80
+ | Property | Type | Description |
81
+ |----------|------|-------------|
82
+ | `name` | `string` | Display name for the section header |
83
+ | `type` | `'elements' \| 'pages'` | Rendering behavior |
84
+ | `items` | `Record<string, any>` | Element classes or page factories |
85
+ | `filter` | `(name, item) => boolean` | Optional filter function |
86
+ | `sort` | `([a, itemA], [b, itemB]) => number` | Optional sort function |
87
+ | `icon` | `string` | Material Symbols icon name |
88
+ | `collapsed` | `boolean` | Start section collapsed (default: false) |
89
+
90
+ ## Internal Components
91
+
92
+ The module includes these internal web components:
93
+
94
+ | Component | Description |
95
+ |-----------|-------------|
96
+ | `wcc-dashboard` | Main dashboard container with routing |
97
+ | `wcc-sidebar` | Navigation sidebar with collapsible sections and search |
98
+ | `wcc-frame` | Responsive viewport with size controls |
99
+ | `wcc-properties` | Property panel with live editing |
100
+ | `wcc-record-button` | Recording state indicator button |
101
+ | `wcc-recording-panel` | Recording workflow UI with format selection |
102
+
103
+ ## RecorderService API
104
+
105
+ For programmatic recording and MP4 conversion:
106
+
107
+ ```typescript
108
+ import { RecorderService, type IRecorderEvents } from '@design.estate/wcctools';
109
+
110
+ const events: IRecorderEvents = {
111
+ onDurationUpdate: (duration) => console.log(`Recording: ${duration}s`),
112
+ onRecordingComplete: (blob) => saveBlob(blob),
113
+ onAudioLevelUpdate: (level) => updateMeter(level),
114
+ onError: (error) => console.error(error),
115
+ onStreamEnded: () => console.log('User stopped sharing'),
116
+ };
117
+
118
+ const recorder = new RecorderService(events);
119
+
120
+ // Load available microphones
121
+ const mics = await recorder.loadMicrophones(true);
122
+
123
+ // Start audio level monitoring
124
+ await recorder.startAudioMonitoring(mics[0].deviceId);
125
+
126
+ // Start recording (always captures as WebM internally at up to 60fps)
127
+ await recorder.startRecording({
128
+ mode: 'viewport',
129
+ audioDeviceId: mics[0].deviceId,
130
+ viewportElement: document.querySelector('.viewport'),
131
+ });
132
+
133
+ // Stop recording
134
+ recorder.stopRecording();
135
+
136
+ // Convert to MP4 for universal playback (H.264 + AAC via WebCodecs)
137
+ const mp4Blob = await recorder.convertToMp4(recorder.recordedBlob);
138
+
139
+ // Or export trimmed video
140
+ const trimmedBlob = await recorder.exportTrimmedVideo(videoElement, startTime, endTime);
141
+
142
+ // Cleanup
143
+ recorder.dispose();
144
+ ```
145
+
146
+ ## Architecture
147
+
148
+ ```
149
+ ts_web/
150
+ ├── index.ts # Main exports
151
+ ├── wcctools.interfaces.ts # Type definitions
152
+ ├── types/
153
+ │ ├── dom-webcodecs-stub/ # TS6 compatibility shim
154
+ │ └── dom-mediacapture-stub/ # MediaCapture Transform types
155
+ ├── elements/
156
+ │ ├── wcc-dashboard.ts # Root dashboard component
157
+ │ ├── wcc-sidebar.ts # Navigation sidebar
158
+ │ ├── wcc-frame.ts # Responsive viewport
159
+ │ ├── wcc-properties.ts # Property editing panel
160
+ │ ├── wcc-record-button.ts # Recording button
161
+ │ ├── wcc-recording-panel.ts # Recording options/preview
162
+ │ └── wcctools.helpers.ts # Shared utilities
163
+ ├── services/
164
+ │ └── recorder.service.ts # MediaRecorder + mediabunny MP4 conversion
165
+ └── pages/
166
+ └── index.ts # Built-in pages
167
+ ```
168
+
169
+ ## Features
170
+
171
+ - 🎨 Interactive component preview
172
+ - 📂 Section-based sidebar with filtering, sorting & search (by name, tag, or group)
173
+ - 🔧 Real-time property editing with type detection
174
+ - 🌓 Theme switching (light/dark)
175
+ - 📱 Responsive viewport testing
176
+ - 🎬 Screen recording with MP4/WebM export, trimming, and audio
177
+ - 🔗 URL-based deep linking