@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,1103 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { DeesElement, customElement, html, css, property, state } from '@design.estate/dees-element';
36
+ import { RecorderService } from '../services/recorder.service.js';
37
+ let WccRecordingPanel = (() => {
38
+ let _classDecorators = [customElement('wcc-recording-panel')];
39
+ let _classDescriptor;
40
+ let _classExtraInitializers = [];
41
+ let _classThis;
42
+ let _classSuper = DeesElement;
43
+ let _dashboardRef_decorators;
44
+ let _dashboardRef_initializers = [];
45
+ let _dashboardRef_extraInitializers = [];
46
+ let _panelState_decorators;
47
+ let _panelState_initializers = [];
48
+ let _panelState_extraInitializers = [];
49
+ let _recordingMode_decorators;
50
+ let _recordingMode_initializers = [];
51
+ let _recordingMode_extraInitializers = [];
52
+ let _outputFormat_decorators;
53
+ let _outputFormat_initializers = [];
54
+ let _outputFormat_extraInitializers = [];
55
+ let _audioEnabled_decorators;
56
+ let _audioEnabled_initializers = [];
57
+ let _audioEnabled_extraInitializers = [];
58
+ let _selectedMicrophoneId_decorators;
59
+ let _selectedMicrophoneId_initializers = [];
60
+ let _selectedMicrophoneId_extraInitializers = [];
61
+ let _availableMicrophones_decorators;
62
+ let _availableMicrophones_initializers = [];
63
+ let _availableMicrophones_extraInitializers = [];
64
+ let _audioLevel_decorators;
65
+ let _audioLevel_initializers = [];
66
+ let _audioLevel_extraInitializers = [];
67
+ let _recordingDuration_decorators;
68
+ let _recordingDuration_initializers = [];
69
+ let _recordingDuration_extraInitializers = [];
70
+ let _previewVideoUrl_decorators;
71
+ let _previewVideoUrl_initializers = [];
72
+ let _previewVideoUrl_extraInitializers = [];
73
+ let _trimStart_decorators;
74
+ let _trimStart_initializers = [];
75
+ let _trimStart_extraInitializers = [];
76
+ let _trimEnd_decorators;
77
+ let _trimEnd_initializers = [];
78
+ let _trimEnd_extraInitializers = [];
79
+ let _videoDuration_decorators;
80
+ let _videoDuration_initializers = [];
81
+ let _videoDuration_extraInitializers = [];
82
+ let _isDraggingTrim_decorators;
83
+ let _isDraggingTrim_initializers = [];
84
+ let _isDraggingTrim_extraInitializers = [];
85
+ let _isExporting_decorators;
86
+ let _isExporting_initializers = [];
87
+ let _isExporting_extraInitializers = [];
88
+ var WccRecordingPanel = class extends _classSuper {
89
+ static { _classThis = this; }
90
+ static {
91
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
92
+ _dashboardRef_decorators = [property({ attribute: false })];
93
+ _panelState_decorators = [state()];
94
+ _recordingMode_decorators = [state()];
95
+ _outputFormat_decorators = [state()];
96
+ _audioEnabled_decorators = [state()];
97
+ _selectedMicrophoneId_decorators = [state()];
98
+ _availableMicrophones_decorators = [state()];
99
+ _audioLevel_decorators = [state()];
100
+ _recordingDuration_decorators = [state()];
101
+ _previewVideoUrl_decorators = [state()];
102
+ _trimStart_decorators = [state()];
103
+ _trimEnd_decorators = [state()];
104
+ _videoDuration_decorators = [state()];
105
+ _isDraggingTrim_decorators = [state()];
106
+ _isExporting_decorators = [state()];
107
+ __esDecorate(this, null, _dashboardRef_decorators, { kind: "accessor", name: "dashboardRef", static: false, private: false, access: { has: obj => "dashboardRef" in obj, get: obj => obj.dashboardRef, set: (obj, value) => { obj.dashboardRef = value; } }, metadata: _metadata }, _dashboardRef_initializers, _dashboardRef_extraInitializers);
108
+ __esDecorate(this, null, _panelState_decorators, { kind: "accessor", name: "panelState", static: false, private: false, access: { has: obj => "panelState" in obj, get: obj => obj.panelState, set: (obj, value) => { obj.panelState = value; } }, metadata: _metadata }, _panelState_initializers, _panelState_extraInitializers);
109
+ __esDecorate(this, null, _recordingMode_decorators, { kind: "accessor", name: "recordingMode", static: false, private: false, access: { has: obj => "recordingMode" in obj, get: obj => obj.recordingMode, set: (obj, value) => { obj.recordingMode = value; } }, metadata: _metadata }, _recordingMode_initializers, _recordingMode_extraInitializers);
110
+ __esDecorate(this, null, _outputFormat_decorators, { kind: "accessor", name: "outputFormat", static: false, private: false, access: { has: obj => "outputFormat" in obj, get: obj => obj.outputFormat, set: (obj, value) => { obj.outputFormat = value; } }, metadata: _metadata }, _outputFormat_initializers, _outputFormat_extraInitializers);
111
+ __esDecorate(this, null, _audioEnabled_decorators, { kind: "accessor", name: "audioEnabled", static: false, private: false, access: { has: obj => "audioEnabled" in obj, get: obj => obj.audioEnabled, set: (obj, value) => { obj.audioEnabled = value; } }, metadata: _metadata }, _audioEnabled_initializers, _audioEnabled_extraInitializers);
112
+ __esDecorate(this, null, _selectedMicrophoneId_decorators, { kind: "accessor", name: "selectedMicrophoneId", static: false, private: false, access: { has: obj => "selectedMicrophoneId" in obj, get: obj => obj.selectedMicrophoneId, set: (obj, value) => { obj.selectedMicrophoneId = value; } }, metadata: _metadata }, _selectedMicrophoneId_initializers, _selectedMicrophoneId_extraInitializers);
113
+ __esDecorate(this, null, _availableMicrophones_decorators, { kind: "accessor", name: "availableMicrophones", static: false, private: false, access: { has: obj => "availableMicrophones" in obj, get: obj => obj.availableMicrophones, set: (obj, value) => { obj.availableMicrophones = value; } }, metadata: _metadata }, _availableMicrophones_initializers, _availableMicrophones_extraInitializers);
114
+ __esDecorate(this, null, _audioLevel_decorators, { kind: "accessor", name: "audioLevel", static: false, private: false, access: { has: obj => "audioLevel" in obj, get: obj => obj.audioLevel, set: (obj, value) => { obj.audioLevel = value; } }, metadata: _metadata }, _audioLevel_initializers, _audioLevel_extraInitializers);
115
+ __esDecorate(this, null, _recordingDuration_decorators, { kind: "accessor", name: "recordingDuration", static: false, private: false, access: { has: obj => "recordingDuration" in obj, get: obj => obj.recordingDuration, set: (obj, value) => { obj.recordingDuration = value; } }, metadata: _metadata }, _recordingDuration_initializers, _recordingDuration_extraInitializers);
116
+ __esDecorate(this, null, _previewVideoUrl_decorators, { kind: "accessor", name: "previewVideoUrl", static: false, private: false, access: { has: obj => "previewVideoUrl" in obj, get: obj => obj.previewVideoUrl, set: (obj, value) => { obj.previewVideoUrl = value; } }, metadata: _metadata }, _previewVideoUrl_initializers, _previewVideoUrl_extraInitializers);
117
+ __esDecorate(this, null, _trimStart_decorators, { kind: "accessor", name: "trimStart", static: false, private: false, access: { has: obj => "trimStart" in obj, get: obj => obj.trimStart, set: (obj, value) => { obj.trimStart = value; } }, metadata: _metadata }, _trimStart_initializers, _trimStart_extraInitializers);
118
+ __esDecorate(this, null, _trimEnd_decorators, { kind: "accessor", name: "trimEnd", static: false, private: false, access: { has: obj => "trimEnd" in obj, get: obj => obj.trimEnd, set: (obj, value) => { obj.trimEnd = value; } }, metadata: _metadata }, _trimEnd_initializers, _trimEnd_extraInitializers);
119
+ __esDecorate(this, null, _videoDuration_decorators, { kind: "accessor", name: "videoDuration", static: false, private: false, access: { has: obj => "videoDuration" in obj, get: obj => obj.videoDuration, set: (obj, value) => { obj.videoDuration = value; } }, metadata: _metadata }, _videoDuration_initializers, _videoDuration_extraInitializers);
120
+ __esDecorate(this, null, _isDraggingTrim_decorators, { kind: "accessor", name: "isDraggingTrim", static: false, private: false, access: { has: obj => "isDraggingTrim" in obj, get: obj => obj.isDraggingTrim, set: (obj, value) => { obj.isDraggingTrim = value; } }, metadata: _metadata }, _isDraggingTrim_initializers, _isDraggingTrim_extraInitializers);
121
+ __esDecorate(this, null, _isExporting_decorators, { kind: "accessor", name: "isExporting", static: false, private: false, access: { has: obj => "isExporting" in obj, get: obj => obj.isExporting, set: (obj, value) => { obj.isExporting = value; } }, metadata: _metadata }, _isExporting_initializers, _isExporting_extraInitializers);
122
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
123
+ WccRecordingPanel = _classThis = _classDescriptor.value;
124
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
125
+ }
126
+ #dashboardRef_accessor_storage = __runInitializers(this, _dashboardRef_initializers, void 0);
127
+ // External configuration
128
+ get dashboardRef() { return this.#dashboardRef_accessor_storage; }
129
+ set dashboardRef(value) { this.#dashboardRef_accessor_storage = value; }
130
+ #panelState_accessor_storage = (__runInitializers(this, _dashboardRef_extraInitializers), __runInitializers(this, _panelState_initializers, 'options'));
131
+ // Panel state
132
+ get panelState() { return this.#panelState_accessor_storage; }
133
+ set panelState(value) { this.#panelState_accessor_storage = value; }
134
+ #recordingMode_accessor_storage = (__runInitializers(this, _panelState_extraInitializers), __runInitializers(this, _recordingMode_initializers, 'viewport'));
135
+ // Recording options
136
+ get recordingMode() { return this.#recordingMode_accessor_storage; }
137
+ set recordingMode(value) { this.#recordingMode_accessor_storage = value; }
138
+ #outputFormat_accessor_storage = (__runInitializers(this, _recordingMode_extraInitializers), __runInitializers(this, _outputFormat_initializers, 'mp4'));
139
+ get outputFormat() { return this.#outputFormat_accessor_storage; }
140
+ set outputFormat(value) { this.#outputFormat_accessor_storage = value; }
141
+ #audioEnabled_accessor_storage = (__runInitializers(this, _outputFormat_extraInitializers), __runInitializers(this, _audioEnabled_initializers, false));
142
+ get audioEnabled() { return this.#audioEnabled_accessor_storage; }
143
+ set audioEnabled(value) { this.#audioEnabled_accessor_storage = value; }
144
+ #selectedMicrophoneId_accessor_storage = (__runInitializers(this, _audioEnabled_extraInitializers), __runInitializers(this, _selectedMicrophoneId_initializers, ''));
145
+ get selectedMicrophoneId() { return this.#selectedMicrophoneId_accessor_storage; }
146
+ set selectedMicrophoneId(value) { this.#selectedMicrophoneId_accessor_storage = value; }
147
+ #availableMicrophones_accessor_storage = (__runInitializers(this, _selectedMicrophoneId_extraInitializers), __runInitializers(this, _availableMicrophones_initializers, []));
148
+ get availableMicrophones() { return this.#availableMicrophones_accessor_storage; }
149
+ set availableMicrophones(value) { this.#availableMicrophones_accessor_storage = value; }
150
+ #audioLevel_accessor_storage = (__runInitializers(this, _availableMicrophones_extraInitializers), __runInitializers(this, _audioLevel_initializers, 0));
151
+ get audioLevel() { return this.#audioLevel_accessor_storage; }
152
+ set audioLevel(value) { this.#audioLevel_accessor_storage = value; }
153
+ #recordingDuration_accessor_storage = (__runInitializers(this, _audioLevel_extraInitializers), __runInitializers(this, _recordingDuration_initializers, 0));
154
+ // Recording state
155
+ get recordingDuration() { return this.#recordingDuration_accessor_storage; }
156
+ set recordingDuration(value) { this.#recordingDuration_accessor_storage = value; }
157
+ #previewVideoUrl_accessor_storage = (__runInitializers(this, _recordingDuration_extraInitializers), __runInitializers(this, _previewVideoUrl_initializers, ''));
158
+ // Preview/trim state
159
+ get previewVideoUrl() { return this.#previewVideoUrl_accessor_storage; }
160
+ set previewVideoUrl(value) { this.#previewVideoUrl_accessor_storage = value; }
161
+ #trimStart_accessor_storage = (__runInitializers(this, _previewVideoUrl_extraInitializers), __runInitializers(this, _trimStart_initializers, 0));
162
+ get trimStart() { return this.#trimStart_accessor_storage; }
163
+ set trimStart(value) { this.#trimStart_accessor_storage = value; }
164
+ #trimEnd_accessor_storage = (__runInitializers(this, _trimStart_extraInitializers), __runInitializers(this, _trimEnd_initializers, 0));
165
+ get trimEnd() { return this.#trimEnd_accessor_storage; }
166
+ set trimEnd(value) { this.#trimEnd_accessor_storage = value; }
167
+ #videoDuration_accessor_storage = (__runInitializers(this, _trimEnd_extraInitializers), __runInitializers(this, _videoDuration_initializers, 0));
168
+ get videoDuration() { return this.#videoDuration_accessor_storage; }
169
+ set videoDuration(value) { this.#videoDuration_accessor_storage = value; }
170
+ #isDraggingTrim_accessor_storage = (__runInitializers(this, _videoDuration_extraInitializers), __runInitializers(this, _isDraggingTrim_initializers, null));
171
+ get isDraggingTrim() { return this.#isDraggingTrim_accessor_storage; }
172
+ set isDraggingTrim(value) { this.#isDraggingTrim_accessor_storage = value; }
173
+ #isExporting_accessor_storage = (__runInitializers(this, _isDraggingTrim_extraInitializers), __runInitializers(this, _isExporting_initializers, false));
174
+ get isExporting() { return this.#isExporting_accessor_storage; }
175
+ set isExporting(value) { this.#isExporting_accessor_storage = value; }
176
+ // Service instance
177
+ recorderService = __runInitializers(this, _isExporting_extraInitializers);
178
+ constructor() {
179
+ super();
180
+ this.recorderService = new RecorderService({
181
+ onDurationUpdate: (duration) => {
182
+ this.recordingDuration = duration;
183
+ this.dispatchEvent(new CustomEvent('duration-update', {
184
+ detail: { duration },
185
+ bubbles: true,
186
+ composed: true
187
+ }));
188
+ },
189
+ onRecordingComplete: (blob) => {
190
+ this.handleRecordingComplete(blob);
191
+ },
192
+ onAudioLevelUpdate: (level) => {
193
+ this.audioLevel = level;
194
+ },
195
+ onStreamEnded: () => {
196
+ this.stopRecording();
197
+ }
198
+ });
199
+ }
200
+ static styles = [
201
+ css `
202
+ :host {
203
+ /* CSS Variables */
204
+ --background: #0a0a0a;
205
+ --foreground: #e5e5e5;
206
+ --input: #141414;
207
+ --primary: #3b82f6;
208
+ --border: rgba(255, 255, 255, 0.06);
209
+ --radius-sm: 2px;
210
+ --radius-md: 4px;
211
+ --radius-lg: 6px;
212
+ }
213
+
214
+ /* Recording Options Panel */
215
+ .recording-options-panel {
216
+ position: fixed;
217
+ right: 16px;
218
+ bottom: 116px;
219
+ width: 360px;
220
+ background: #0c0c0c;
221
+ border: 1px solid rgba(255, 255, 255, 0.1);
222
+ border-radius: var(--radius-md);
223
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
224
+ z-index: 1000;
225
+ overflow: hidden;
226
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
227
+ }
228
+
229
+ .recording-options-header {
230
+ padding: 0.75rem 1rem;
231
+ background: rgba(255, 255, 255, 0.02);
232
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
233
+ display: flex;
234
+ justify-content: space-between;
235
+ align-items: center;
236
+ }
237
+
238
+ .recording-options-title {
239
+ font-size: 0.8rem;
240
+ font-weight: 500;
241
+ color: #ccc;
242
+ }
243
+
244
+ .recording-options-close {
245
+ width: 24px;
246
+ height: 24px;
247
+ background: transparent;
248
+ border: none;
249
+ color: #666;
250
+ cursor: pointer;
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: center;
254
+ border-radius: var(--radius-sm);
255
+ transition: all 0.15s ease;
256
+ }
257
+
258
+ .recording-options-close:hover {
259
+ background: rgba(255, 255, 255, 0.05);
260
+ color: #999;
261
+ }
262
+
263
+ .recording-options-content {
264
+ padding: 1rem;
265
+ }
266
+
267
+ .recording-option-group {
268
+ margin-bottom: 1rem;
269
+ }
270
+
271
+ .recording-option-group:last-child {
272
+ margin-bottom: 0;
273
+ }
274
+
275
+ .recording-option-label {
276
+ font-size: 0.7rem;
277
+ font-weight: 500;
278
+ color: #888;
279
+ text-transform: uppercase;
280
+ letter-spacing: 0.05em;
281
+ margin-bottom: 0.5rem;
282
+ }
283
+
284
+ .recording-mode-buttons {
285
+ display: flex;
286
+ gap: 0.5rem;
287
+ }
288
+
289
+ .recording-mode-btn {
290
+ flex: 1;
291
+ padding: 0.6rem 0.75rem;
292
+ background: var(--input);
293
+ border: 1px solid transparent;
294
+ border-radius: var(--radius-sm);
295
+ color: #999;
296
+ font-size: 0.75rem;
297
+ cursor: pointer;
298
+ transition: all 0.15s ease;
299
+ text-align: center;
300
+ }
301
+
302
+ .recording-mode-btn:hover {
303
+ border-color: var(--primary);
304
+ color: #ccc;
305
+ }
306
+
307
+ .recording-mode-btn.selected {
308
+ background: rgba(59, 130, 246, 0.15);
309
+ border-color: var(--primary);
310
+ color: var(--primary);
311
+ }
312
+
313
+ .audio-toggle {
314
+ display: flex;
315
+ align-items: center;
316
+ gap: 0.5rem;
317
+ margin-bottom: 0.75rem;
318
+ }
319
+
320
+ .audio-toggle input[type="checkbox"] {
321
+ width: 1rem;
322
+ height: 1rem;
323
+ accent-color: var(--primary);
324
+ }
325
+
326
+ .audio-toggle label {
327
+ font-size: 0.75rem;
328
+ color: #999;
329
+ cursor: pointer;
330
+ }
331
+
332
+ .microphone-select {
333
+ width: 100%;
334
+ padding: 0.5rem 0.75rem;
335
+ background: var(--input);
336
+ border: 1px solid transparent;
337
+ border-radius: var(--radius-sm);
338
+ color: var(--foreground);
339
+ font-size: 0.75rem;
340
+ outline: none;
341
+ cursor: pointer;
342
+ transition: all 0.15s ease;
343
+ }
344
+
345
+ .microphone-select:focus {
346
+ border-color: var(--primary);
347
+ }
348
+
349
+ .microphone-select:disabled {
350
+ opacity: 0.5;
351
+ cursor: not-allowed;
352
+ }
353
+
354
+ .audio-level-container {
355
+ margin-top: 0.75rem;
356
+ padding: 0.5rem;
357
+ background: rgba(255, 255, 255, 0.02);
358
+ border-radius: var(--radius-sm);
359
+ }
360
+
361
+ .audio-level-label {
362
+ font-size: 0.65rem;
363
+ color: #666;
364
+ margin-bottom: 0.25rem;
365
+ }
366
+
367
+ .audio-level-bar {
368
+ height: 8px;
369
+ background: var(--input);
370
+ border-radius: 4px;
371
+ overflow: hidden;
372
+ }
373
+
374
+ .audio-level-fill {
375
+ height: 100%;
376
+ background: linear-gradient(90deg, #22c55e, #84cc16, #eab308);
377
+ border-radius: 4px;
378
+ transition: width 0.1s ease;
379
+ }
380
+
381
+ .start-recording-btn {
382
+ width: 100%;
383
+ padding: 0.75rem;
384
+ background: #dc2626;
385
+ border: none;
386
+ border-radius: var(--radius-sm);
387
+ color: white;
388
+ font-size: 0.8rem;
389
+ font-weight: 500;
390
+ cursor: pointer;
391
+ transition: all 0.15s ease;
392
+ margin-top: 1rem;
393
+ display: flex;
394
+ align-items: center;
395
+ justify-content: center;
396
+ gap: 0.5rem;
397
+ }
398
+
399
+ .start-recording-btn:hover {
400
+ background: #b91c1c;
401
+ }
402
+
403
+ .start-recording-btn .rec-dot {
404
+ width: 10px;
405
+ height: 10px;
406
+ background: white;
407
+ border-radius: 50%;
408
+ }
409
+
410
+ /* Preview Modal */
411
+ .preview-modal-overlay {
412
+ position: fixed;
413
+ top: 0;
414
+ left: 0;
415
+ right: 0;
416
+ bottom: 0;
417
+ background: rgba(0, 0, 0, 0.8);
418
+ display: flex;
419
+ align-items: center;
420
+ justify-content: center;
421
+ z-index: 1000;
422
+ backdrop-filter: blur(4px);
423
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
424
+ }
425
+
426
+ .preview-modal {
427
+ width: 90%;
428
+ max-width: 800px;
429
+ background: #0c0c0c;
430
+ border: 1px solid rgba(255, 255, 255, 0.1);
431
+ border-radius: var(--radius-lg);
432
+ overflow: hidden;
433
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
434
+ }
435
+
436
+ .preview-modal-header {
437
+ padding: 1rem 1.25rem;
438
+ background: rgba(255, 255, 255, 0.02);
439
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
440
+ display: flex;
441
+ justify-content: space-between;
442
+ align-items: center;
443
+ }
444
+
445
+ .preview-modal-title {
446
+ font-size: 0.9rem;
447
+ font-weight: 500;
448
+ color: #ccc;
449
+ }
450
+
451
+ .preview-modal-close {
452
+ width: 28px;
453
+ height: 28px;
454
+ background: transparent;
455
+ border: none;
456
+ color: #666;
457
+ cursor: pointer;
458
+ display: flex;
459
+ align-items: center;
460
+ justify-content: center;
461
+ border-radius: var(--radius-sm);
462
+ font-size: 1.2rem;
463
+ transition: all 0.15s ease;
464
+ }
465
+
466
+ .preview-modal-close:hover {
467
+ background: rgba(255, 255, 255, 0.05);
468
+ color: #999;
469
+ }
470
+
471
+ .preview-modal-content {
472
+ padding: 1.25rem;
473
+ }
474
+
475
+ .preview-video-container {
476
+ background: #000;
477
+ border-radius: var(--radius-sm);
478
+ overflow: hidden;
479
+ aspect-ratio: 16 / 9;
480
+ }
481
+
482
+ .preview-video {
483
+ width: 100%;
484
+ height: 100%;
485
+ object-fit: contain;
486
+ }
487
+
488
+ .preview-modal-actions {
489
+ padding: 1rem 1.25rem;
490
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
491
+ display: flex;
492
+ justify-content: flex-end;
493
+ gap: 0.75rem;
494
+ }
495
+
496
+ .preview-btn {
497
+ padding: 0.6rem 1.25rem;
498
+ border-radius: var(--radius-sm);
499
+ font-size: 0.8rem;
500
+ font-weight: 500;
501
+ cursor: pointer;
502
+ transition: all 0.15s ease;
503
+ display: inline-flex;
504
+ align-items: center;
505
+ gap: 0.5rem;
506
+ }
507
+
508
+ .preview-btn.secondary {
509
+ background: transparent;
510
+ border: 1px solid rgba(255, 255, 255, 0.1);
511
+ color: #999;
512
+ }
513
+
514
+ .preview-btn.secondary:hover {
515
+ border-color: rgba(255, 255, 255, 0.2);
516
+ color: #ccc;
517
+ }
518
+
519
+ .preview-btn.primary {
520
+ background: var(--primary);
521
+ border: none;
522
+ color: white;
523
+ }
524
+
525
+ .preview-btn.primary:hover {
526
+ background: #2563eb;
527
+ }
528
+
529
+ .preview-btn.primary:disabled {
530
+ background: #1e3a5f;
531
+ cursor: not-allowed;
532
+ opacity: 0.7;
533
+ }
534
+
535
+ /* Trim Timeline Styles */
536
+ .trim-section {
537
+ margin-top: 1.25rem;
538
+ padding-top: 1.25rem;
539
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
540
+ }
541
+
542
+ .trim-section-header {
543
+ display: flex;
544
+ justify-content: space-between;
545
+ align-items: center;
546
+ margin-bottom: 0.75rem;
547
+ }
548
+
549
+ .trim-section-title {
550
+ font-size: 0.75rem;
551
+ font-weight: 500;
552
+ color: #888;
553
+ text-transform: uppercase;
554
+ letter-spacing: 0.05em;
555
+ }
556
+
557
+ .trim-duration-info {
558
+ font-size: 0.7rem;
559
+ color: #666;
560
+ font-family: 'Consolas', 'Monaco', monospace;
561
+ }
562
+
563
+ .trim-timeline {
564
+ position: relative;
565
+ height: 48px;
566
+ background: var(--input);
567
+ border-radius: var(--radius-sm);
568
+ margin-bottom: 0.75rem;
569
+ user-select: none;
570
+ }
571
+
572
+ .trim-track {
573
+ position: absolute;
574
+ top: 50%;
575
+ left: 12px;
576
+ right: 12px;
577
+ height: 6px;
578
+ background: #333;
579
+ transform: translateY(-50%);
580
+ border-radius: 3px;
581
+ }
582
+
583
+ .trim-selected {
584
+ position: absolute;
585
+ top: 50%;
586
+ height: 6px;
587
+ background: var(--primary);
588
+ transform: translateY(-50%);
589
+ border-radius: 3px;
590
+ pointer-events: none;
591
+ }
592
+
593
+ .trim-handle {
594
+ position: absolute;
595
+ top: 50%;
596
+ width: 16px;
597
+ height: 36px;
598
+ background: white;
599
+ border: 2px solid var(--primary);
600
+ border-radius: 4px;
601
+ transform: translate(-50%, -50%);
602
+ cursor: ew-resize;
603
+ z-index: 2;
604
+ display: flex;
605
+ align-items: center;
606
+ justify-content: center;
607
+ transition: background 0.15s ease, transform 0.1s ease;
608
+ }
609
+
610
+ .trim-handle:hover {
611
+ background: #e0e0e0;
612
+ }
613
+
614
+ .trim-handle:active {
615
+ background: var(--primary);
616
+ transform: translate(-50%, -50%) scale(1.05);
617
+ }
618
+
619
+ .trim-handle::before {
620
+ content: '';
621
+ width: 2px;
622
+ height: 16px;
623
+ background: #666;
624
+ border-radius: 1px;
625
+ }
626
+
627
+ .trim-handle:active::before {
628
+ background: white;
629
+ }
630
+
631
+ .trim-time-labels {
632
+ display: flex;
633
+ justify-content: space-between;
634
+ font-size: 0.65rem;
635
+ color: #666;
636
+ font-family: 'Consolas', 'Monaco', monospace;
637
+ padding: 0 12px;
638
+ }
639
+
640
+ .trim-actions {
641
+ display: flex;
642
+ gap: 0.5rem;
643
+ margin-top: 0.75rem;
644
+ }
645
+
646
+ .trim-action-btn {
647
+ flex: 1;
648
+ padding: 0.5rem 0.75rem;
649
+ background: var(--input);
650
+ border: 1px solid transparent;
651
+ border-radius: var(--radius-sm);
652
+ color: #999;
653
+ font-size: 0.75rem;
654
+ cursor: pointer;
655
+ transition: all 0.15s ease;
656
+ text-align: center;
657
+ }
658
+
659
+ .trim-action-btn:hover {
660
+ border-color: var(--primary);
661
+ color: #ccc;
662
+ }
663
+
664
+ .export-spinner {
665
+ display: inline-block;
666
+ width: 14px;
667
+ height: 14px;
668
+ border: 2px solid rgba(255, 255, 255, 0.3);
669
+ border-radius: 50%;
670
+ border-top-color: white;
671
+ animation: spin 0.8s linear infinite;
672
+ flex-shrink: 0;
673
+ }
674
+
675
+ @keyframes spin {
676
+ to { transform: rotate(360deg); }
677
+ }
678
+
679
+ `
680
+ ];
681
+ render() {
682
+ if (this.panelState === 'options') {
683
+ return this.renderOptionsPanel();
684
+ }
685
+ else if (this.panelState === 'preview') {
686
+ return this.renderPreviewModal();
687
+ }
688
+ return html ``;
689
+ }
690
+ renderOptionsPanel() {
691
+ return html `
692
+ <div class="recording-options-panel">
693
+ <div class="recording-options-header">
694
+ <span class="recording-options-title">Recording Settings</span>
695
+ <button class="recording-options-close" @click=${() => this.close()}>✕</button>
696
+ </div>
697
+ <div class="recording-options-content">
698
+ <div class="recording-option-group">
699
+ <div class="recording-option-label">Record Area</div>
700
+ <div class="recording-mode-buttons">
701
+ <button
702
+ class="recording-mode-btn ${this.recordingMode === 'viewport' ? 'selected' : ''}"
703
+ @click=${() => this.recordingMode = 'viewport'}
704
+ >
705
+ Viewport Only
706
+ </button>
707
+ <button
708
+ class="recording-mode-btn ${this.recordingMode === 'screen' ? 'selected' : ''}"
709
+ @click=${() => this.recordingMode = 'screen'}
710
+ >
711
+ Entire Screen
712
+ </button>
713
+ </div>
714
+ </div>
715
+
716
+ <div class="recording-option-group">
717
+ <div class="recording-option-label">Format</div>
718
+ <div class="recording-mode-buttons">
719
+ <button
720
+ class="recording-mode-btn ${this.outputFormat === 'mp4' ? 'selected' : ''}"
721
+ @click=${() => this.outputFormat = 'mp4'}
722
+ >
723
+ MP4 (H.264)
724
+ </button>
725
+ <button
726
+ class="recording-mode-btn ${this.outputFormat === 'webm' ? 'selected' : ''}"
727
+ @click=${() => this.outputFormat = 'webm'}
728
+ >
729
+ WebM (VP9)
730
+ </button>
731
+ </div>
732
+ </div>
733
+
734
+ <div class="recording-option-group">
735
+ <div class="recording-option-label">Audio</div>
736
+ <div class="audio-toggle">
737
+ <input
738
+ type="checkbox"
739
+ id="audioToggle"
740
+ ?checked=${this.audioEnabled}
741
+ @change=${(e) => this.handleAudioToggle(e.target.checked)}
742
+ />
743
+ <label for="audioToggle">Enable Microphone</label>
744
+ </div>
745
+
746
+ ${this.audioEnabled ? html `
747
+ <select
748
+ class="microphone-select"
749
+ .value=${this.selectedMicrophoneId}
750
+ @change=${(e) => this.handleMicrophoneChange(e.target.value)}
751
+ >
752
+ <option value="">Select Microphone...</option>
753
+ ${this.availableMicrophones.map(mic => html `
754
+ <option value=${mic.deviceId}>${mic.label || `Microphone ${mic.deviceId.slice(0, 8)}`}</option>
755
+ `)}
756
+ </select>
757
+
758
+ ${this.selectedMicrophoneId ? html `
759
+ <div class="audio-level-container">
760
+ <div class="audio-level-label">Input Level</div>
761
+ <div class="audio-level-bar">
762
+ <div class="audio-level-fill" style="width: ${this.audioLevel}%"></div>
763
+ </div>
764
+ </div>
765
+ ` : null}
766
+ ` : null}
767
+ </div>
768
+
769
+ <button class="start-recording-btn" @click=${() => this.startRecording()}>
770
+ <div class="rec-dot"></div>
771
+ Start Recording
772
+ </button>
773
+ </div>
774
+ </div>
775
+ `;
776
+ }
777
+ renderPreviewModal() {
778
+ return html `
779
+ <div class="preview-modal-overlay" @click=${(e) => {
780
+ if (e.target.classList.contains('preview-modal-overlay')) {
781
+ this.discardRecording();
782
+ }
783
+ }}>
784
+ <div class="preview-modal">
785
+ <div class="preview-modal-header">
786
+ <span class="preview-modal-title">Recording Preview</span>
787
+ <button class="preview-modal-close" @click=${() => this.discardRecording()}>✕</button>
788
+ </div>
789
+ <div class="preview-modal-content">
790
+ <div class="preview-video-container">
791
+ <video
792
+ class="preview-video"
793
+ src=${this.previewVideoUrl}
794
+ controls
795
+ @loadedmetadata=${(e) => this.handleVideoLoaded(e.target)}
796
+ ></video>
797
+ </div>
798
+
799
+ <!-- Trim Section -->
800
+ <div class="trim-section">
801
+ <div class="trim-section-header">
802
+ <span class="trim-section-title">Trim Video</span>
803
+ <span class="trim-duration-info">
804
+ ${this.formatDuration(Math.floor(this.trimEnd - this.trimStart))}
805
+ ${this.trimStart > 0 || this.trimEnd < this.videoDuration
806
+ ? `(trimmed from ${this.formatDuration(Math.floor(this.videoDuration))})`
807
+ : ''}
808
+ </span>
809
+ </div>
810
+
811
+ <div
812
+ class="trim-timeline"
813
+ @mousedown=${(e) => this.handleTimelineClick(e)}
814
+ @mousemove=${(e) => this.handleTimelineDrag(e)}
815
+ @mouseup=${() => this.handleTimelineDragEnd()}
816
+ @mouseleave=${() => this.handleTimelineDragEnd()}
817
+ >
818
+ <div class="trim-track"></div>
819
+ <div
820
+ class="trim-selected"
821
+ style="left: ${this.getHandlePositionStyle(this.trimStart)}; right: ${this.getHandlePositionFromEndStyle(this.trimEnd)};"
822
+ ></div>
823
+ <div
824
+ class="trim-handle start-handle"
825
+ style="left: ${this.getHandlePositionStyle(this.trimStart)};"
826
+ @mousedown=${(e) => { e.stopPropagation(); this.isDraggingTrim = 'start'; }}
827
+ ></div>
828
+ <div
829
+ class="trim-handle end-handle"
830
+ style="left: ${this.getHandlePositionStyle(this.trimEnd)};"
831
+ @mousedown=${(e) => { e.stopPropagation(); this.isDraggingTrim = 'end'; }}
832
+ ></div>
833
+ </div>
834
+
835
+ <div class="trim-time-labels">
836
+ <span>${this.formatDuration(Math.floor(this.trimStart))}</span>
837
+ <span>${this.formatDuration(Math.floor(this.trimEnd))}</span>
838
+ </div>
839
+
840
+ <div class="trim-actions">
841
+ <button class="trim-action-btn" @click=${() => this.resetTrim()}>
842
+ Reset Trim
843
+ </button>
844
+ <button class="trim-action-btn" @click=${() => this.previewTrimmedSection()}>
845
+ Preview Selection
846
+ </button>
847
+ </div>
848
+ </div>
849
+
850
+ </div>
851
+ <div class="preview-modal-actions">
852
+ <button class="preview-btn secondary" @click=${() => this.discardRecording()}>Discard</button>
853
+ <button
854
+ class="preview-btn primary"
855
+ ?disabled=${this.isExporting}
856
+ @click=${() => this.downloadRecording()}
857
+ >
858
+ ${this.isExporting
859
+ ? html `<span class="export-spinner"></span>${this.outputFormat === 'mp4' ? 'Converting to MP4...' : 'Exporting...'}`
860
+ : `Download ${this.outputFormat === 'mp4' ? 'MP4' : 'WebM'}`}
861
+ </button>
862
+ </div>
863
+ </div>
864
+ </div>
865
+ `;
866
+ }
867
+ // ==================== Audio Methods ====================
868
+ async handleAudioToggle(enabled) {
869
+ this.audioEnabled = enabled;
870
+ if (enabled) {
871
+ this.availableMicrophones = await this.recorderService.loadMicrophones(true);
872
+ if (this.availableMicrophones.length > 0 && !this.selectedMicrophoneId) {
873
+ this.selectedMicrophoneId = this.availableMicrophones[0].deviceId;
874
+ await this.recorderService.startAudioMonitoring(this.selectedMicrophoneId);
875
+ }
876
+ }
877
+ else {
878
+ this.recorderService.stopAudioMonitoring();
879
+ this.selectedMicrophoneId = '';
880
+ this.audioLevel = 0;
881
+ }
882
+ }
883
+ async handleMicrophoneChange(deviceId) {
884
+ this.selectedMicrophoneId = deviceId;
885
+ if (deviceId) {
886
+ await this.recorderService.startAudioMonitoring(deviceId);
887
+ }
888
+ else {
889
+ this.recorderService.stopAudioMonitoring();
890
+ this.audioLevel = 0;
891
+ }
892
+ }
893
+ // ==================== Recording Methods ====================
894
+ async startRecording() {
895
+ try {
896
+ let viewportElement;
897
+ if (this.recordingMode === 'viewport' && this.dashboardRef) {
898
+ const wccFrame = await this.dashboardRef.wccFrame;
899
+ viewportElement = await wccFrame.getViewportElement();
900
+ }
901
+ await this.recorderService.startRecording({
902
+ mode: this.recordingMode,
903
+ audioDeviceId: this.audioEnabled ? this.selectedMicrophoneId : undefined,
904
+ viewportElement,
905
+ });
906
+ this.panelState = 'recording';
907
+ this.dispatchEvent(new CustomEvent('recording-start', {
908
+ bubbles: true,
909
+ composed: true
910
+ }));
911
+ }
912
+ catch (error) {
913
+ console.error('Failed to start recording:', error);
914
+ this.panelState = 'options';
915
+ }
916
+ }
917
+ stopRecording() {
918
+ this.recorderService.stopRecording();
919
+ }
920
+ handleRecordingComplete(blob) {
921
+ if (this.previewVideoUrl) {
922
+ URL.revokeObjectURL(this.previewVideoUrl);
923
+ }
924
+ this.previewVideoUrl = URL.createObjectURL(blob);
925
+ this.panelState = 'preview';
926
+ this.dispatchEvent(new CustomEvent('recording-stop', {
927
+ bubbles: true,
928
+ composed: true
929
+ }));
930
+ }
931
+ discardRecording() {
932
+ if (this.previewVideoUrl) {
933
+ URL.revokeObjectURL(this.previewVideoUrl);
934
+ this.previewVideoUrl = '';
935
+ }
936
+ this.recorderService.reset();
937
+ this.trimStart = 0;
938
+ this.trimEnd = 0;
939
+ this.videoDuration = 0;
940
+ this.isExporting = false;
941
+ this.recordingDuration = 0;
942
+ this.close();
943
+ }
944
+ async downloadRecording() {
945
+ const recordedBlob = this.recorderService.recordedBlob;
946
+ if (!recordedBlob)
947
+ return;
948
+ this.isExporting = true;
949
+ try {
950
+ let blobToDownload;
951
+ // Handle trimming if needed — always produces WebM
952
+ const needsTrim = this.trimStart > 0.1 || this.trimEnd < this.videoDuration - 0.1;
953
+ if (needsTrim) {
954
+ const video = this.shadowRoot?.querySelector('.preview-video');
955
+ if (video) {
956
+ blobToDownload = await this.recorderService.exportTrimmedVideo(video, this.trimStart, this.trimEnd);
957
+ }
958
+ else {
959
+ blobToDownload = recordedBlob;
960
+ }
961
+ }
962
+ else {
963
+ blobToDownload = recordedBlob;
964
+ }
965
+ // Convert WebM → MP4 if MP4 format selected
966
+ if (this.outputFormat === 'mp4') {
967
+ blobToDownload = await this.recorderService.convertToMp4(blobToDownload);
968
+ }
969
+ // Trigger download
970
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
971
+ const ext = this.outputFormat === 'mp4' ? 'mp4' : 'webm';
972
+ const filename = `wcctools-recording-${timestamp}.${ext}`;
973
+ const url = URL.createObjectURL(blobToDownload);
974
+ const a = document.createElement('a');
975
+ a.href = url;
976
+ a.download = filename;
977
+ document.body.appendChild(a);
978
+ a.click();
979
+ document.body.removeChild(a);
980
+ URL.revokeObjectURL(url);
981
+ this.discardRecording();
982
+ }
983
+ catch (error) {
984
+ console.error('Error exporting video:', error);
985
+ this.isExporting = false;
986
+ }
987
+ }
988
+ // ==================== Trim Methods ====================
989
+ handleVideoLoaded(video) {
990
+ // WebM files from MediaRecorder may have Infinity/NaN duration
991
+ // Fall back to the tracked recording duration
992
+ const duration = Number.isFinite(video.duration) ? video.duration : this.recordingDuration;
993
+ this.videoDuration = duration;
994
+ this.trimStart = 0;
995
+ this.trimEnd = duration;
996
+ }
997
+ formatDuration(seconds) {
998
+ const mins = Math.floor(seconds / 60);
999
+ const secs = seconds % 60;
1000
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
1001
+ }
1002
+ getHandlePositionStyle(time) {
1003
+ if (this.videoDuration === 0)
1004
+ return '12px';
1005
+ const percentage = time / this.videoDuration;
1006
+ // Formula: 12px padding + percentage of remaining width (total - 24px padding)
1007
+ // At 0%: 12px (left edge of track)
1008
+ // At 100%: calc(100% - 12px) (right edge of track)
1009
+ return `calc(12px + ${(percentage * 100).toFixed(2)}% - ${(percentage * 24).toFixed(2)}px)`;
1010
+ }
1011
+ getHandlePositionFromEndStyle(time) {
1012
+ if (this.videoDuration === 0)
1013
+ return '12px';
1014
+ const percentage = time / this.videoDuration;
1015
+ const remainingPercentage = 1 - percentage;
1016
+ // For CSS 'right' property: distance from right edge
1017
+ // At trimEnd = 100%: right = 12px (at right edge of track)
1018
+ // At trimEnd = 0%: right = calc(100% - 12px) (at left edge of track)
1019
+ return `calc(12px + ${(remainingPercentage * 100).toFixed(2)}% - ${(remainingPercentage * 24).toFixed(2)}px)`;
1020
+ }
1021
+ handleTimelineClick(e) {
1022
+ if (this.isDraggingTrim)
1023
+ return;
1024
+ const timeline = e.currentTarget;
1025
+ const rect = timeline.getBoundingClientRect();
1026
+ const x = e.clientX - rect.left;
1027
+ const percentage = Math.max(0, Math.min(1, (x - 12) / (rect.width - 24)));
1028
+ const time = percentage * this.videoDuration;
1029
+ const video = this.shadowRoot?.querySelector('.preview-video');
1030
+ if (video) {
1031
+ video.currentTime = time;
1032
+ }
1033
+ }
1034
+ handleTimelineDrag(e) {
1035
+ if (!this.isDraggingTrim)
1036
+ return;
1037
+ const timeline = e.currentTarget;
1038
+ const rect = timeline.getBoundingClientRect();
1039
+ const x = e.clientX - rect.left;
1040
+ const percentage = Math.max(0, Math.min(1, (x - 12) / (rect.width - 24)));
1041
+ const time = percentage * this.videoDuration;
1042
+ const minDuration = 1;
1043
+ if (this.isDraggingTrim === 'start') {
1044
+ this.trimStart = Math.min(time, this.trimEnd - minDuration);
1045
+ this.trimStart = Math.max(0, this.trimStart);
1046
+ }
1047
+ else if (this.isDraggingTrim === 'end') {
1048
+ this.trimEnd = Math.max(time, this.trimStart + minDuration);
1049
+ this.trimEnd = Math.min(this.videoDuration, this.trimEnd);
1050
+ }
1051
+ const video = this.shadowRoot?.querySelector('.preview-video');
1052
+ if (video) {
1053
+ video.currentTime = this.isDraggingTrim === 'start' ? this.trimStart : this.trimEnd;
1054
+ }
1055
+ }
1056
+ handleTimelineDragEnd() {
1057
+ this.isDraggingTrim = null;
1058
+ }
1059
+ resetTrim() {
1060
+ this.trimStart = 0;
1061
+ this.trimEnd = this.videoDuration;
1062
+ const video = this.shadowRoot?.querySelector('.preview-video');
1063
+ if (video) {
1064
+ video.currentTime = 0;
1065
+ }
1066
+ }
1067
+ previewTrimmedSection() {
1068
+ const video = this.shadowRoot?.querySelector('.preview-video');
1069
+ if (!video)
1070
+ return;
1071
+ video.currentTime = this.trimStart;
1072
+ video.play();
1073
+ const checkTime = () => {
1074
+ if (video.currentTime >= this.trimEnd) {
1075
+ video.pause();
1076
+ video.removeEventListener('timeupdate', checkTime);
1077
+ }
1078
+ };
1079
+ video.addEventListener('timeupdate', checkTime);
1080
+ }
1081
+ // ==================== Lifecycle ====================
1082
+ close() {
1083
+ this.recorderService.stopAudioMonitoring();
1084
+ this.dispatchEvent(new CustomEvent('close', {
1085
+ bubbles: true,
1086
+ composed: true
1087
+ }));
1088
+ }
1089
+ async disconnectedCallback() {
1090
+ await super.disconnectedCallback();
1091
+ this.recorderService.dispose();
1092
+ if (this.previewVideoUrl) {
1093
+ URL.revokeObjectURL(this.previewVideoUrl);
1094
+ }
1095
+ }
1096
+ static {
1097
+ __runInitializers(_classThis, _classExtraInitializers);
1098
+ }
1099
+ };
1100
+ return WccRecordingPanel = _classThis;
1101
+ })();
1102
+ export { WccRecordingPanel };
1103
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2NjLXJlY29yZGluZy1wYW5lbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3dlYi9lbGVtZW50cy93Y2MtcmVjb3JkaW5nLXBhbmVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQXVCLE1BQU0sNkJBQTZCLENBQUM7QUFDMUgsT0FBTyxFQUFFLGVBQWUsRUFBc0IsTUFBTSxpQ0FBaUMsQ0FBQztJQUl6RSxpQkFBaUI7NEJBRDdCLGFBQWEsQ0FBQyxxQkFBcUIsQ0FBQzs7OztzQkFDRSxXQUFXOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O2lDQUFuQixTQUFRLFdBQVc7Ozs7d0NBRS9DLFFBQVEsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBQztzQ0FJOUIsS0FBSyxFQUFFO3lDQUlQLEtBQUssRUFBRTt3Q0FHUCxLQUFLLEVBQUU7d0NBR1AsS0FBSyxFQUFFO2dEQUdQLEtBQUssRUFBRTtnREFHUCxLQUFLLEVBQUU7c0NBR1AsS0FBSyxFQUFFOzZDQUlQLEtBQUssRUFBRTsyQ0FJUCxLQUFLLEVBQUU7cUNBR1AsS0FBSyxFQUFFO21DQUdQLEtBQUssRUFBRTt5Q0FHUCxLQUFLLEVBQUU7MENBR1AsS0FBSyxFQUFFO3VDQUdQLEtBQUssRUFBRTtZQTdDUix5TEFBUyxZQUFZLDZCQUFaLFlBQVksbUdBQWU7WUFJcEMsbUxBQVMsVUFBVSw2QkFBVixVQUFVLCtGQUFrRDtZQUlyRSw0TEFBUyxhQUFhLDZCQUFiLGFBQWEscUdBQXFDO1lBRzNELHlMQUFTLFlBQVksNkJBQVosWUFBWSxtR0FBd0I7WUFHN0MseUxBQVMsWUFBWSw2QkFBWixZQUFZLG1HQUFrQjtZQUd2QyxpTkFBUyxvQkFBb0Is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