@scaleflex/widget-screen-capture 0.0.1

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.
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Submit recorded video to filerobot. Enabled when file is available
4
+ */
5
+ export default function SubmitButton(_ref) {
6
+ var recordedVideo = _ref.recordedVideo,
7
+ recording = _ref.recording,
8
+ onSubmit = _ref.onSubmit,
9
+ i18n = _ref.i18n;
10
+ if (recordedVideo && !recording) {
11
+ return /*#__PURE__*/_jsx("button", {
12
+ className: "filerobot-u-reset filerobot-c-btn filerobot-ScreenCapture-button filerobot-ScreenCapture-button--submit",
13
+ type: "button",
14
+ title: i18n('screenCaptureSubmitRecordedFileTitle'),
15
+ "aria-label": i18n('screenCaptureSubmitRecordedFileTitle'),
16
+ onClick: onSubmit,
17
+ "data-filerobot-super-focusable": true,
18
+ children: /*#__PURE__*/_jsxs("svg", {
19
+ "aria-hidden": "true",
20
+ focusable: "false",
21
+ className: "filerobot-c-icon",
22
+ width: "48",
23
+ height: "48",
24
+ viewBox: "0 0 48 48",
25
+ children: [/*#__PURE__*/_jsx("path", {
26
+ d: "M0 0h48v48h-48z",
27
+ fill: "none"
28
+ }), /*#__PURE__*/_jsx("path", {
29
+ d: "M38.71 20.07c-1.36-6.88-7.43-12.07-14.71-12.07-5.78 0-10.79 3.28-13.3 8.07-6.01.65-10.7 5.74-10.7 11.93 0 6.63 5.37 12 12 12h26c5.52 0 10-4.48 10-10 0-5.28-4.11-9.56-9.29-9.93zm-10.71 5.93v8h-8v-8h-6l10-10 10 10h-6z"
30
+ })]
31
+ })
32
+ });
33
+ } else {
34
+ return null;
35
+ }
36
+ }
@@ -0,0 +1,29 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
6
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ import { createSlice } from '@reduxjs/toolkit';
8
+ import { PLUGINS_IDS } from '@scaleflex/widget-utils/lib/constants';
9
+ var initialState = {
10
+ streamActive: false,
11
+ audioStreamActive: false,
12
+ recording: false,
13
+ recordedVideo: null
14
+ };
15
+ var commonSlice = createSlice({
16
+ name: PLUGINS_IDS.SCREEN_CAPTURE,
17
+ initialState: initialState,
18
+ reducers: {
19
+ screenCaptureCommonStateUpdated: function screenCaptureCommonStateUpdated(state, action) {
20
+ return _objectSpread(_objectSpread({}, state), action.payload);
21
+ }
22
+ }
23
+ });
24
+ var screenCaptureCommonStateUpdated = commonSlice.actions.screenCaptureCommonStateUpdated;
25
+ export { screenCaptureCommonStateUpdated };
26
+ export var selectScreenCaptureCommonState = function selectScreenCaptureCommonState(state) {
27
+ return state[PLUGINS_IDS.SCREEN_CAPTURE];
28
+ };
29
+ export default commonSlice.reducer;
@@ -0,0 +1,9 @@
1
+ export default {
2
+ screenCaptureStartCapturingTitle: 'Begin screen capturing',
3
+ screenCaptureStopCapturingLabel: 'Stop screen capturing',
4
+ screenCaptureSubmitRecordedFileTitle: 'Submit captured video',
5
+ screenCaptureStreamActive: 'Stream active',
6
+ screenCaptureStreamPassiveTitle: 'Stream passive',
7
+ screenCaptureMicDisabledInfo: 'Microphone access denied by user',
8
+ screenCaptureRecordingText: 'Recording'
9
+ };
package/lib/index.js ADDED
@@ -0,0 +1,438 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
6
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
7
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
8
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
9
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
+ function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
11
+ function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
12
+ function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
13
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
14
+ function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
15
+ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
16
+ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
17
+ import { createElement } from 'react';
18
+ import { Plugin } from '@scaleflex/widget-core';
19
+ import Explorer from '@scaleflex/widget-explorer';
20
+ import Translator from '@scaleflex/widget-utils/lib/Translator';
21
+ import getFileTypeExtension from '@scaleflex/widget-utils/lib/getFileTypeExtension';
22
+ import { PLUGINS_IDS } from '@scaleflex/widget-utils/lib/constants';
23
+ import defaultLocale from './defaultLocale';
24
+ import ScreenRecIcon from './ScreenRecIcon';
25
+ import CaptureScreen from './CaptureScreen';
26
+ import screenCaptureReducer, { screenCaptureCommonStateUpdated } from './common.slice';
27
+ // TODO: find a way to show version of the current plugin
28
+ // why solution below isn't good?
29
+ // first import doesn't work with webpack 5 as it was deprecated
30
+ // second import fixes webpack 5 issue as it was mentioned in their docs
31
+ // but it exposes our package.json to the client and it is mentioned as security rist in mutiple places
32
+ // https://github.com/axelpale/genversion
33
+ // https://stackoverflow.com/questions/64993118/error-should-not-import-the-named-export-version-imported-as-version
34
+ // https://stackoverflow.com/questions/9153571/is-there-a-way-to-get-version-from-package-json-in-nodejs-code/10855054#10855054
35
+ // import { version } from '../package.json'
36
+ // import packageInfo from '../package.json'
37
+
38
+ // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
39
+ function getMediaDevices() {
40
+ // check if screen capturing is supported
41
+ /* eslint-disable */
42
+ if (navigator && navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia && window && window.MediaRecorder) {
43
+ return navigator.mediaDevices;
44
+ }
45
+ /* eslint-enable */
46
+
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Screen capture
52
+ */
53
+ var ScreenCapture = /*#__PURE__*/function (_Plugin) {
54
+ // static VERSION = packageInfo.version
55
+
56
+ function ScreenCapture(filerobot) {
57
+ var _this;
58
+ var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
59
+ _classCallCheck(this, ScreenCapture);
60
+ _this = _callSuper(this, ScreenCapture, [filerobot, opts]);
61
+ _this.mediaDevices = getMediaDevices();
62
+ _this.protocol = location.protocol.match(/https/i) ? 'https' : 'http';
63
+ _this.id = PLUGINS_IDS.SCREEN_CAPTURE;
64
+ _this.title = _this.opts.title || 'Screen cast';
65
+ _this.type = 'acquirer';
66
+ _this.icon = ScreenRecIcon;
67
+ _this.defaultLocale = {
68
+ strings: defaultLocale
69
+ };
70
+
71
+ // set default options
72
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints
73
+ var defaultOptions = {
74
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#Properties_of_shared_screen_tracks
75
+ displayMediaConstraints: {
76
+ video: {
77
+ width: 1280,
78
+ height: 720,
79
+ frameRate: {
80
+ ideal: 3,
81
+ max: 5
82
+ },
83
+ cursor: 'motion',
84
+ displaySurface: 'monitor'
85
+ }
86
+ },
87
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints/audio
88
+ userMediaConstraints: {
89
+ audio: true
90
+ },
91
+ preferredVideoMimeType: 'video/webm'
92
+ };
93
+
94
+ // merge default options with the ones set by user
95
+ _this.opts = _objectSpread(_objectSpread({}, defaultOptions), opts);
96
+
97
+ // i18n
98
+ _this.translator = new Translator([_this.defaultLocale, _this.filerobot.locale, _this.opts.locale]);
99
+ _this.i18n = _this.translator.translate.bind(_this.translator);
100
+ _this.i18nArray = _this.translator.translateArray.bind(_this.translator);
101
+
102
+ // filerobot plugin class related
103
+ _this.install = _this.install.bind(_this);
104
+ _this.render = _this.render.bind(_this);
105
+
106
+ // screen capturer related
107
+ _this.start = _this.start.bind(_this);
108
+ _this.stop = _this.stop.bind(_this);
109
+ _this.startRecording = _this.startRecording.bind(_this);
110
+ _this.stopRecording = _this.stopRecording.bind(_this);
111
+ _this.submit = _this.submit.bind(_this);
112
+ _this.streamInterrupted = _this.streamInactivated.bind(_this);
113
+
114
+ // initialize
115
+ _this.captureActive = false;
116
+ _this.capturedMediaFile = null;
117
+ return _this;
118
+ }
119
+ _inherits(ScreenCapture, _Plugin);
120
+ return _createClass(ScreenCapture, [{
121
+ key: "install",
122
+ value: function install() {
123
+ // Return if browser doesn’t support getDisplayMedia and
124
+ if (!this.mediaDevices) {
125
+ this.filerobot.log('Screen recorder access is not supported', 'error');
126
+ return null;
127
+ }
128
+ if (Explorer) {
129
+ this.mount(Explorer, this);
130
+ }
131
+ }
132
+ }, {
133
+ key: "uninstall",
134
+ value: function uninstall() {
135
+ if (this.videoStream) {
136
+ this.stop();
137
+ }
138
+ this.unmount();
139
+ }
140
+ }, {
141
+ key: "setPluginCommonState",
142
+ value: function setPluginCommonState(update) {
143
+ this.dispatch(screenCaptureCommonStateUpdated(update));
144
+ }
145
+ }, {
146
+ key: "getPluginReducer",
147
+ value: function getPluginReducer() {
148
+ return screenCaptureReducer;
149
+ }
150
+ }, {
151
+ key: "start",
152
+ value: function start() {
153
+ var _this2 = this;
154
+ if (!this.mediaDevices) {
155
+ return Promise.reject(new Error('Screen recorder access not supported'));
156
+ }
157
+ this.captureActive = true;
158
+ this.selectAudioStreamSource();
159
+ this.selectVideoStreamSource().then(function (res) {
160
+ // something happened in start -> return
161
+ if (res === false) {
162
+ // Close the Explorer panel if plugin is installed
163
+ // into Explorer (could be other parent UI plugin)
164
+ if (_this2.parent && _this2.parent.hideAllPanelsAndShowAddFilesPanel) {
165
+ _this2.parent.hideAllPanelsAndShowAddFilesPanel();
166
+ _this2.captureActive = false;
167
+ }
168
+ }
169
+ });
170
+ }
171
+ }, {
172
+ key: "selectVideoStreamSource",
173
+ value: function selectVideoStreamSource() {
174
+ var _this3 = this;
175
+ // if active stream available, return it
176
+ if (this.videoStream) {
177
+ return new Promise(function (resolve) {
178
+ return resolve(_this3.videoStream);
179
+ });
180
+ }
181
+
182
+ // ask user to select source to record and get mediastream from that
183
+ return this.mediaDevices.getDisplayMedia(this.opts.displayMediaConstraints).then(function (videoStream) {
184
+ _this3.videoStream = videoStream;
185
+
186
+ // add event listener to stop recording if stream is interrupted
187
+ _this3.videoStream.addEventListener('inactive', function (event) {
188
+ _this3.streamInactivated();
189
+ });
190
+ _this3.setPluginCommonState({
191
+ streamActive: true
192
+ });
193
+ return videoStream;
194
+ })["catch"](function () {
195
+ var err = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
196
+ _this3.filerobot.info(err, 'error', 5000);
197
+ _this3.userDenied = true;
198
+ setTimeout(function () {
199
+ _this3.userDenied = false;
200
+ }, 1000);
201
+ return false;
202
+ });
203
+ }
204
+ }, {
205
+ key: "selectAudioStreamSource",
206
+ value: function selectAudioStreamSource() {
207
+ var _this4 = this;
208
+ // if active stream available, return it
209
+ if (this.audioStream) {
210
+ return new Promise(function (resolve) {
211
+ return resolve(_this4.audioStream);
212
+ });
213
+ }
214
+
215
+ // ask user to select source to record and get mediastream from that
216
+ return this.mediaDevices.getUserMedia(this.opts.userMediaConstraints).then(function (audioStream) {
217
+ _this4.audioStream = audioStream;
218
+ _this4.setPluginCommonState({
219
+ audioStreamActive: true
220
+ });
221
+ return audioStream;
222
+ })["catch"](function (err) {
223
+ if (err.name === 'NotAllowedError') {
224
+ _this4.filerobot.info(_this4.i18n('screenCaptureMicDisabledInfo'), 'error', 5000);
225
+ }
226
+ return false;
227
+ });
228
+ }
229
+ }, {
230
+ key: "startRecording",
231
+ value: function startRecording() {
232
+ var _this5 = this;
233
+ var options = {};
234
+ this.capturedMediaFile = null;
235
+ this.recordingChunks = [];
236
+ var preferredVideoMimeType = this.opts.preferredVideoMimeType;
237
+ this.selectVideoStreamSource().then(function (videoStream) {
238
+ // Attempt to use the passed preferredVideoMimeType (if any) during recording.
239
+ // If the browser doesn't support it, we'll fall back to the browser default instead
240
+ if (preferredVideoMimeType && MediaRecorder.isTypeSupported(preferredVideoMimeType) && getFileTypeExtension(preferredVideoMimeType)) {
241
+ options.mimeType = preferredVideoMimeType;
242
+ }
243
+
244
+ // prepare tracks
245
+ var tracks = [videoStream.getVideoTracks()[0]];
246
+
247
+ // merge audio if exits
248
+ if (_this5.audioStream) {
249
+ tracks.push(_this5.audioStream.getAudioTracks()[0]);
250
+ }
251
+
252
+ // create new stream from video and audio
253
+ _this5.outputStream = new MediaStream(tracks);
254
+
255
+ // initialize mediarecorder
256
+ _this5.recorder = new MediaRecorder(_this5.outputStream, options);
257
+
258
+ // push data to buffer when data available
259
+ _this5.recorder.addEventListener('dataavailable', function (event) {
260
+ _this5.recordingChunks.push(event.data);
261
+ });
262
+
263
+ // start recording
264
+ _this5.recorder.start();
265
+
266
+ // set plugin state to recording
267
+ _this5.setPluginCommonState({
268
+ recording: true
269
+ });
270
+ })["catch"](function (err) {
271
+ _this5.filerobot.log(err, 'error');
272
+ });
273
+ }
274
+ }, {
275
+ key: "streamInactivated",
276
+ value: function streamInactivated() {
277
+ // get screen recorder state
278
+ var recording = this.getPluginCommonState().recording; // recordedVideo
279
+
280
+ // commented because It is generating this bug https://scaleflexhq.atlassian.net/browse/FRA-1038
281
+ // TODO: delete this comment if a long time passed without issues related to this fix
282
+ // if (!recordedVideo && !recording) {
283
+ // // Close the Explorer panel if plugin is installed
284
+ // // into Explorer (could be other parent UI plugin)
285
+ // if (this.parent && this.parent.hideAllPanelsAndShowAddFilesPanel) {
286
+ // this.parent.hideAllPanelsAndShowAddFilesPanel()
287
+ // }
288
+ // }
289
+ if (recording) {
290
+ // stop recorder if it is active
291
+ this.filerobot.log('Capture stream inactive — stop recording');
292
+ this.stopRecording();
293
+ }
294
+ this.videoStream = null;
295
+ this.audioStream = null;
296
+ this.setPluginCommonState({
297
+ streamActive: false,
298
+ audioStreamActive: false
299
+ });
300
+ }
301
+ }, {
302
+ key: "stopRecording",
303
+ value: function stopRecording() {
304
+ var _this6 = this;
305
+ var stopped = new Promise(function (resolve, reject) {
306
+ _this6.recorder.addEventListener('stop', function () {
307
+ resolve();
308
+ });
309
+ _this6.recorder.stop();
310
+ });
311
+ return stopped.then(function () {
312
+ // recording stopped
313
+ _this6.setPluginCommonState({
314
+ recording: false
315
+ });
316
+ // get video file after recorder stopped
317
+ return _this6.getVideo();
318
+ }).then(function (file) {
319
+ // store media file
320
+ _this6.capturedMediaFile = file;
321
+
322
+ // create object url for capture result preview
323
+ _this6.setPluginCommonState({
324
+ recordedVideo: URL.createObjectURL(file.data)
325
+ });
326
+ }).then(function () {
327
+ _this6.recordingChunks = null;
328
+ _this6.recorder = null;
329
+ }, function (error) {
330
+ _this6.recordingChunks = null;
331
+ _this6.recorder = null;
332
+ throw error;
333
+ });
334
+ }
335
+ }, {
336
+ key: "submit",
337
+ value: function submit() {
338
+ try {
339
+ // add recorded file to filerobot
340
+ if (this.capturedMediaFile) {
341
+ this.filerobot.addFile(this.capturedMediaFile);
342
+ }
343
+ } catch (err) {
344
+ // Logging the error, exept restrictions, which is handled in Core
345
+ if (!err.isRestriction) {
346
+ this.filerobot.log(err, 'error');
347
+ }
348
+ }
349
+ }
350
+ }, {
351
+ key: "stop",
352
+ value: function stop() {
353
+ // flush video stream
354
+ if (this.videoStream) {
355
+ this.videoStream.getVideoTracks().forEach(function (track) {
356
+ track.stop();
357
+ });
358
+ this.videoStream.getAudioTracks().forEach(function (track) {
359
+ track.stop();
360
+ });
361
+ this.videoStream = null;
362
+ }
363
+
364
+ // flush audio stream
365
+ if (this.audioStream) {
366
+ this.audioStream.getAudioTracks().forEach(function (track) {
367
+ track.stop();
368
+ });
369
+ this.audioStream.getVideoTracks().forEach(function (track) {
370
+ track.stop();
371
+ });
372
+ this.audioStream = null;
373
+ }
374
+
375
+ // flush output stream
376
+ if (this.outputStream) {
377
+ this.outputStream.getAudioTracks().forEach(function (track) {
378
+ track.stop();
379
+ });
380
+ this.outputStream.getVideoTracks().forEach(function (track) {
381
+ track.stop();
382
+ });
383
+ this.outputStream = null;
384
+ }
385
+
386
+ // remove preview video
387
+ this.setPluginCommonState({
388
+ recordedVideo: null
389
+ });
390
+ if (this.getPluginCommonState().recording) {
391
+ this.setPluginCommonState({
392
+ recording: false
393
+ });
394
+ }
395
+ this.captureActive = false;
396
+ }
397
+ }, {
398
+ key: "getVideo",
399
+ value: function getVideo() {
400
+ var mimeType = this.recordingChunks[0].type;
401
+ var fileExtension = getFileTypeExtension(mimeType);
402
+ if (!fileExtension) {
403
+ return Promise.reject(new Error("Could not retrieve recording: Unsupported media type \"".concat(mimeType, "\"")));
404
+ }
405
+ var name = "screencap-".concat(Date.now(), ".").concat(fileExtension);
406
+ var blob = new Blob(this.recordingChunks, {
407
+ type: mimeType
408
+ });
409
+ var file = {
410
+ source: this.id,
411
+ name: name,
412
+ data: new Blob([blob], {
413
+ type: mimeType
414
+ }),
415
+ type: mimeType
416
+ };
417
+ return Promise.resolve(file);
418
+ }
419
+ }, {
420
+ key: "render",
421
+ value: function render() {
422
+ // get screen recorder state
423
+ var streamActive = this.getPluginCommonState().streamActive;
424
+ if (!streamActive && !this.captureActive && !this.userDenied) {
425
+ this.start();
426
+ }
427
+ return /*#__PURE__*/createElement(CaptureScreen, {
428
+ onStartRecording: this.startRecording,
429
+ onStopRecording: this.stopRecording,
430
+ onStop: this.stop,
431
+ onSubmit: this.submit,
432
+ i18n: this.i18n,
433
+ stream: this.videoStream
434
+ });
435
+ }
436
+ }]);
437
+ }(Plugin);
438
+ export { ScreenCapture as default };
package/lib/style.scss ADDED
@@ -0,0 +1,149 @@
1
+ // packages/@scaleflex/widget-screen-capture/src/CaptureScreen.jsx
2
+ @import '@scaleflex/widget-core/lib/_utils.scss';
3
+ @import '@scaleflex/widget-core/lib/_variables.scss';
4
+
5
+ .filerobot-ScreenCapture-container {
6
+ width: 100%;
7
+ height: 100%;
8
+ display: flex;
9
+ justify-content: center;
10
+ align-items: center;
11
+ flex-direction: column;
12
+ }
13
+
14
+ .filerobot-ScreenCapture-videoContainer {
15
+ width: 100%;
16
+ flex: 1;
17
+ flex-grow: 1;
18
+ overflow: hidden;
19
+ background-color: $darkgrey;
20
+ text-align: center;
21
+ position: relative;
22
+
23
+ .filerobot-size--md & {
24
+ max-width: 100%;
25
+ }
26
+ }
27
+
28
+ .filerobot-ScreenCapture-video {
29
+ max-width: 100%;
30
+ max-height: 100%;
31
+ position: absolute;
32
+ top: 0;
33
+ right: 0;
34
+ bottom: 0;
35
+ left: 0;
36
+ margin: auto;
37
+ outline: none;
38
+ }
39
+
40
+ .filerobot-ScreenCapture-buttonContainer {
41
+ width: 100%;
42
+ height: 75px;
43
+ border-top: 1px solid $gray-200;
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ padding: 0 20px;
48
+ background-color: $white;
49
+
50
+ [data-filerobot-theme="dark"] & {
51
+ background-color: $gray-900;
52
+ border-top: 1px solid $gray-800;
53
+ }
54
+ }
55
+
56
+ .filerobot-ScreenCapture-button {
57
+ @include blue-border-focus;
58
+ width: 45px;
59
+ height: 45px;
60
+ border-radius: 50%;
61
+ color: $white;
62
+ cursor: pointer;
63
+ transition: all 0.3s;
64
+
65
+ [data-filerobot-theme="dark"] & {
66
+ @include blue-border-focus--dark;
67
+ }
68
+
69
+ .filerobot-size--md & {
70
+ width: 60px;
71
+ height: 60px;
72
+ }
73
+
74
+ &:hover {
75
+ background-color: darken($red, 5%);
76
+ }
77
+ }
78
+
79
+ .filerobot-ScreenCapture-button svg {
80
+ width: 30px;
81
+ height: 30px;
82
+ max-width: 100%;
83
+ max-height: 100%;
84
+ display: inline-block;
85
+ vertical-align: text-top;
86
+ overflow: hidden;
87
+ fill: currentColor;
88
+ }
89
+
90
+ .filerobot-ScreenCapture-button--submit {
91
+ background-color: $blue;
92
+ margin-left: 12px;
93
+
94
+ &:hover {
95
+ background-color: darken($blue, 5%);
96
+ }
97
+
98
+ &:disabled {
99
+ background-color: $gray-500;
100
+ cursor: default;
101
+ }
102
+
103
+ &:disabled:hover {
104
+ background-color: $gray-200;
105
+ }
106
+ }
107
+
108
+ .filerobot-ScreenCapture-title {
109
+ font-size: 22px;
110
+ line-height: 1.35;
111
+ font-weight: 400;
112
+ margin: 0;
113
+ margin-bottom: 5px;
114
+ padding: 0 15px;
115
+ max-width: 500px;
116
+ text-align: center;
117
+ color: $gray-800;
118
+ }
119
+
120
+ .filerobot-ScreenCapture-icon--stream {
121
+ position: absolute;
122
+ right: 0;
123
+ top: 0;
124
+ margin: 1rem;
125
+ z-index:1;
126
+
127
+ svg {
128
+ fill: $gray-500;
129
+ }
130
+ }
131
+
132
+ .filerobot-ScreenCapture-icon--streamActive svg {
133
+ animation: filerobot-ScreenCapture-icon--blink 1s cubic-bezier(0.47, 0, 0.75, 0.72) infinite;
134
+ }
135
+
136
+ @keyframes filerobot-ScreenCapture-icon--blink {
137
+ 0% { fill: $blue;}
138
+ 50% { fill: $gray-500; }
139
+ 100% { fill: $blue; }
140
+ }
141
+
142
+ .filerobot-ScreenCapture-button--video {
143
+ color: $white;
144
+ background: $red;
145
+
146
+ &:hover {
147
+ background-color: darken($red, 10%);
148
+ }
149
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@scaleflex/widget-screen-capture",
3
+ "description": "Scaleflex plugin that captures video from display or application.",
4
+ "version": "0.0.1",
5
+ "license": "MIT",
6
+ "main": "lib/index.js",
7
+ "style": "dist/style.min.css",
8
+ "types": "types/index.d.ts",
9
+ "dependencies": {
10
+ "@scaleflex/widget-utils": "^0.0.1"
11
+ },
12
+ "devDependencies": {
13
+ "react": "^19.0.0",
14
+ "react-dom": "^19.0.0"
15
+ },
16
+ "peerDependencies": {
17
+ "@scaleflex/widget-core": "^0.0.0",
18
+ "@scaleflex/widget-explorer": "^0.0.0",
19
+ "react": ">=19.0.0",
20
+ "react-dom": ">=19.0.0"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "files": [
26
+ "/dist",
27
+ "/lib",
28
+ "/types"
29
+ ],
30
+ "gitHead": "64ea82e745b7deda36d6794863350e6671e9010d"
31
+ }