@instructure/canvas-rce 7.3.0 → 8.0.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 (162) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/{es/rce/plugins/shared/ai_tools/index.js → __mocks__/@instructure/ui-media-player/_mockUiMediaPlayer.js} +4 -4
  3. package/__tests__/common/mimeClass.test.js +25 -1
  4. package/__tests__/rcs/api.test.js +280 -251
  5. package/es/canvasFileBrowser/FileBrowser.d.ts +2 -2
  6. package/es/canvasFileBrowser/FileBrowser.js +8 -7
  7. package/es/common/mimeClass.js +3 -1
  8. package/es/defaultTinymceConfig.js +47 -49
  9. package/es/enhance-user-content/doc_previews.js +5 -0
  10. package/es/enhance-user-content/enhance_user_content.js +6 -8
  11. package/es/enhance-user-content/index.d.ts +3 -1
  12. package/es/enhance-user-content/index.js +3 -1
  13. package/es/enhance-user-content/instructure_helper.js +1 -0
  14. package/es/enhance-user-content/youtube_overlay.js +18 -0
  15. package/es/getThemeVars.d.ts +1 -1
  16. package/es/getThemeVars.js +23 -26
  17. package/es/rce/AlertMessageArea.d.ts +2 -2
  18. package/es/rce/AlertMessageArea.js +3 -3
  19. package/es/rce/KeyboardShortcutModal.js +2 -2
  20. package/es/rce/RCE.d.ts +9 -0
  21. package/es/rce/RCE.js +4 -0
  22. package/es/rce/RCEGlobals.d.ts +2 -0
  23. package/es/rce/RCEGlobals.js +1 -0
  24. package/es/rce/RCEVariants.d.ts +1 -2
  25. package/es/rce/RCEVariants.js +1 -2
  26. package/es/rce/RCEWrapper.d.ts +16 -26
  27. package/es/rce/RCEWrapper.js +227 -271
  28. package/es/rce/RCEWrapper.utils.d.ts +1 -1
  29. package/es/rce/RCEWrapperProps.d.ts +2 -1
  30. package/es/rce/RCEWrapperProps.js +2 -1
  31. package/es/rce/StatusBar.d.ts +0 -1
  32. package/es/rce/StatusBar.js +3 -28
  33. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.d.ts +2 -1
  34. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +3 -1
  35. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -0
  36. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +12 -2
  37. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +2 -2
  38. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +3 -3
  39. package/es/rce/plugins/instructure_icon_maker/svg/constants.d.ts +20 -5
  40. package/es/rce/plugins/instructure_icon_maker/svg/utils.d.ts +1 -1
  41. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +2 -2
  42. package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +0 -2
  43. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +2 -9
  44. package/es/rce/plugins/instructure_paste/plugin.js +18 -12
  45. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -1
  46. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.d.ts +1 -1
  47. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +25 -25
  48. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.d.ts +4 -0
  49. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +4 -0
  50. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +11 -2
  51. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +92 -10
  52. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.d.ts +13 -1
  53. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +216 -24
  54. package/es/rce/plugins/instructure_record/MediaPanel/index.js +16 -5
  55. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +14 -13
  56. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +110 -39
  57. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.d.ts +11 -1
  58. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +242 -67
  59. package/es/rce/plugins/instructure_record/clickCallback.js +19 -4
  60. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
  61. package/es/rce/plugins/instructure_record/playerLayoutOptions.d.ts +25 -0
  62. package/es/rce/plugins/instructure_record/playerLayoutOptions.js +91 -0
  63. package/es/rce/plugins/instructure_record/plugin.js +2 -5
  64. package/es/rce/plugins/instructure_record/utils.d.ts +3 -0
  65. package/es/rce/plugins/instructure_record/utils.js +31 -0
  66. package/es/rce/plugins/instructure_studio_media_options/plugin.js +82 -24
  67. package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +1 -0
  68. package/es/rce/plugins/shared/ContentSelection.d.ts +6 -1
  69. package/es/rce/plugins/shared/ContentSelection.js +15 -6
  70. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +1 -2
  71. package/es/rce/plugins/shared/DimensionsInput/index.js +11 -12
  72. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.d.ts +1 -1
  73. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +4 -3
  74. package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +27 -5
  75. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +94 -9
  76. package/es/rce/plugins/shared/Upload/UploadFile.js +1 -8
  77. package/es/rce/style.d.ts +2 -1
  78. package/es/rce/style.js +4 -2
  79. package/es/rcs/api.d.ts +5 -10
  80. package/es/rcs/api.js +15 -21
  81. package/es/rcs/fake.d.ts +1 -7
  82. package/es/rcs/fake.js +1 -47
  83. package/es/sidebar/actions/media.d.ts +19 -6
  84. package/es/sidebar/actions/media.js +17 -4
  85. package/es/sidebar/actions/upload.d.ts +3 -3
  86. package/es/sidebar/actions/upload.js +9 -9
  87. package/es/sidebar/containers/Sidebar.js +0 -2
  88. package/es/sidebar/containers/sidebarHandlers.d.ts +2 -4
  89. package/es/sidebar/containers/sidebarHandlers.js +2 -5
  90. package/es/sidebar/reducers/index.d.ts +0 -1
  91. package/es/sidebar/reducers/index.js +0 -2
  92. package/es/sidebar/store/initialState.d.ts +0 -1
  93. package/es/sidebar/store/initialState.js +0 -5
  94. package/es/translations/locales/ar.js +77 -77
  95. package/es/translations/locales/ca.js +77 -77
  96. package/es/translations/locales/cy.js +77 -77
  97. package/es/translations/locales/da-x-k12.js +77 -77
  98. package/es/translations/locales/da.js +77 -77
  99. package/es/translations/locales/de.js +77 -77
  100. package/es/translations/locales/el.js +0 -9
  101. package/es/translations/locales/en-AU-x-unimelb.js +77 -77
  102. package/es/translations/locales/en-GB-x-ukhe.js +77 -77
  103. package/es/translations/locales/en.js +67 -79
  104. package/es/translations/locales/en_AU.js +77 -77
  105. package/es/translations/locales/en_CA.js +77 -77
  106. package/es/translations/locales/en_CY.js +77 -77
  107. package/es/translations/locales/en_GB.js +77 -77
  108. package/es/translations/locales/es.js +77 -77
  109. package/es/translations/locales/es_ES.js +77 -77
  110. package/es/translations/locales/fa_IR.js +0 -9
  111. package/es/translations/locales/fi.js +77 -77
  112. package/es/translations/locales/fr.js +77 -77
  113. package/es/translations/locales/fr_CA.js +77 -77
  114. package/es/translations/locales/ga.js +77 -77
  115. package/es/translations/locales/he.js +0 -9
  116. package/es/translations/locales/hi.js +77 -77
  117. package/es/translations/locales/ht.js +77 -77
  118. package/es/translations/locales/hu.js +0 -36
  119. package/es/translations/locales/hy.js +0 -9
  120. package/es/translations/locales/id.js +77 -77
  121. package/es/translations/locales/is.js +77 -77
  122. package/es/translations/locales/it.js +77 -77
  123. package/es/translations/locales/ja.js +77 -77
  124. package/es/translations/locales/ko.js +2455 -133
  125. package/es/translations/locales/mi.js +77 -77
  126. package/es/translations/locales/ms.js +77 -77
  127. package/es/translations/locales/nb-x-k12.js +77 -77
  128. package/es/translations/locales/nb.js +77 -77
  129. package/es/translations/locales/nl.js +78 -78
  130. package/es/translations/locales/nn.js +0 -36
  131. package/es/translations/locales/pl.js +77 -77
  132. package/es/translations/locales/pt.js +77 -77
  133. package/es/translations/locales/pt_BR.js +77 -77
  134. package/es/translations/locales/ru.js +77 -77
  135. package/es/translations/locales/sl.js +77 -77
  136. package/es/translations/locales/sv-x-k12.js +77 -77
  137. package/es/translations/locales/sv.js +77 -77
  138. package/es/translations/locales/th.js +77 -77
  139. package/es/translations/locales/tr.js +1962 -18
  140. package/es/translations/locales/uk_UA.js +0 -9
  141. package/es/translations/locales/vi.js +77 -77
  142. package/es/translations/locales/zh-Hans.js +77 -77
  143. package/es/translations/locales/zh-Hant.js +77 -77
  144. package/es/translations/locales/zh.js +77 -77
  145. package/es/translations/locales/zh_HK.js +77 -77
  146. package/eslint.config.js +16 -147
  147. package/jest/jest-setup.js +1 -0
  148. package/jest.config.js +2 -0
  149. package/oxlint.json +84 -0
  150. package/package.json +86 -62
  151. package/tsconfig.json +3 -2
  152. package/es/rce/plugins/shared/ai_tools/AIResponseModal.d.ts +0 -10
  153. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +0 -67
  154. package/es/rce/plugins/shared/ai_tools/AIToolsTray.d.ts +0 -18
  155. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +0 -489
  156. package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +0 -7
  157. package/es/rce/plugins/shared/ai_tools/aiicons.js +0 -60
  158. package/es/rce/plugins/shared/ai_tools/index.d.ts +0 -3
  159. package/es/sidebar/actions/flickr.d.ts +0 -20
  160. package/es/sidebar/actions/flickr.js +0 -60
  161. package/es/sidebar/reducers/flickr.d.ts +0 -1
  162. package/es/sidebar/reducers/flickr.js +0 -49
@@ -21,4 +21,4 @@ export declare function focusToolbar(el: HTMLElement): void;
21
21
  export declare function focusFirstMenuButton(el: HTMLElement): void;
22
22
  export declare function isElementWithinTable(node: Element | null): boolean;
23
23
  export declare function parsePluginsToExclude(plugins: string[]): string[];
24
- export declare function patchAutosavedContent(content: string, asText?: boolean): string | null;
24
+ export declare function patchAutosavedContent(content: string, asText?: boolean): string;
@@ -154,6 +154,8 @@ export declare const rceWrapperPropTypes: {
154
154
  }>;
155
155
  instRecordDisabled: PropTypes.Requireable<boolean>;
156
156
  highContrastCSS: PropTypes.Requireable<(string | null | undefined)[]>;
157
+ useHighContrast: PropTypes.Requireable<boolean>;
158
+ fontFamily: PropTypes.Requireable<string>;
157
159
  maxInitRenderedRCEs: PropTypes.Requireable<number>;
158
160
  use_rce_icon_maker: PropTypes.Requireable<boolean>;
159
161
  features: PropTypes.Requireable<{
@@ -169,7 +171,6 @@ export declare const rceWrapperPropTypes: {
169
171
  isA2StudentView: PropTypes.Requireable<boolean>;
170
172
  maxMruTools: PropTypes.Requireable<number>;
171
173
  }>>;
172
- ai_text_tools: PropTypes.Requireable<boolean>;
173
174
  variant: PropTypes.Requireable<"full" | "lite" | "text-only" | "text-block" | "block-content-editor">;
174
175
  };
175
176
  export type RCEWrapperProps = PropTypes.InferProps<typeof rceWrapperPropTypes>;
@@ -126,6 +126,8 @@ export const rceWrapperPropTypes = {
126
126
  menu: menuPropType,
127
127
  instRecordDisabled: PropTypes.bool,
128
128
  highContrastCSS: PropTypes.arrayOf(PropTypes.string),
129
+ useHighContrast: PropTypes.bool,
130
+ fontFamily: PropTypes.string,
129
131
  maxInitRenderedRCEs: PropTypes.number,
130
132
  use_rce_icon_maker: PropTypes.bool,
131
133
  features: PropTypes.objectOf(PropTypes.bool),
@@ -133,6 +135,5 @@ export const rceWrapperPropTypes = {
133
135
  timezone: PropTypes.string,
134
136
  userCacheKey: PropTypes.string,
135
137
  externalToolsConfig: externalToolsConfigPropType,
136
- ai_text_tools: PropTypes.bool,
137
138
  variant: PropTypes.oneOf(RCEVariantValues)
138
139
  };
@@ -18,7 +18,6 @@ declare namespace StatusBar {
18
18
  export let onWordcountModalOpen: import("prop-types").Validator<(...args: any[]) => any>;
19
19
  export let disabledPlugins: import("prop-types").Requireable<(string | null | undefined)[]>;
20
20
  export let features: import("prop-types").Requireable<(string | null | undefined)[]>;
21
- export { func as onAI };
22
21
  }
23
22
  namespace defaultProps {
24
23
  export let a11yBadgeColor: string;
@@ -32,7 +32,6 @@ import { IconA11yLine, IconKeyboardShortcutsLine, IconMiniArrowEndLine, IconFull
32
32
  import formatMessage from '../format-message';
33
33
  import ResizeHandle from './ResizeHandle';
34
34
  import { FS_ENABLED } from '../util/fullscreenHelpers';
35
- import { AIWandSVG } from './plugins/shared/ai_tools';
36
35
  export const WYSIWYG_VIEW = 'WYSIWYG';
37
36
  export const PRETTY_HTML_EDITOR_VIEW = 'PRETTY';
38
37
  export const RAW_HTML_EDITOR_VIEW = 'RAW';
@@ -57,9 +56,7 @@ StatusBar.propTypes = {
57
56
  a11yErrorsCount: number,
58
57
  onWordcountModalOpen: func.isRequired,
59
58
  disabledPlugins: arrayOf(string),
60
- features: arrayOf(string),
61
- // StatusBarFeature[]
62
- onAI: func
59
+ features: arrayOf(string) // StatusBarFeature[]
63
60
  };
64
61
  StatusBar.defaultProps = {
65
62
  a11yBadgeColor: '#2B7ABC',
@@ -218,36 +215,14 @@ export default function StatusBar(props) {
218
215
  }
219
216
  function renderIconButtons() {
220
217
  if (isHtmlView()) return null;
221
- const ai_tools = isFeature('ai_tools');
222
218
  const kb_shortcuts = isFeature('keyboard_shortcuts');
223
219
  const a11y_checker = isFeature('a11y_checker');
224
- if (!(ai_tools || kb_shortcuts || a11y_checker)) return null;
220
+ if (!(kb_shortcuts || a11y_checker)) return null;
225
221
  const kbshortcut = formatMessage('View keyboard shortcuts');
226
222
  return /*#__PURE__*/React.createElement(View, {
227
223
  display: "inline-block",
228
224
  padding: "0 x-small"
229
- }, ai_tools && props.onAI && /*#__PURE__*/React.createElement(IconButton, {
230
- "data-btn-id": "rce-ai-btn",
231
- color: "secondary",
232
- "aria-haspopup": "dialog",
233
- title: formatMessage('AI Tools'),
234
- tabIndex: tabIndexForBtn('rce-ai-btn'),
235
- onClick: event => {
236
- event.target.focus(); // FF doesn't focus buttons on click
237
- props.onAI();
238
- },
239
- onFocus: () => setFocusedBtnId('rce-ai-btn'),
240
- screenReaderLabel: formatMessage('AI Tools'),
241
- withBackground: false,
242
- withBorder: false
243
- }, /*#__PURE__*/React.createElement("span", {
244
- style: {
245
- color: 'dodgerBlue'
246
- }
247
- }, /*#__PURE__*/React.createElement(SVGIcon, {
248
- src: AIWandSVG,
249
- size: "x-small"
250
- }))), kb_shortcuts && /*#__PURE__*/React.createElement(IconButton, {
225
+ }, kb_shortcuts && /*#__PURE__*/React.createElement(IconButton, {
251
226
  "data-btn-id": "rce-kbshortcut-btn",
252
227
  color: "secondary",
253
228
  "aria-haspopup": "dialog",
@@ -1,2 +1,3 @@
1
1
  export const advancedOnlyCommands: string[];
2
- export const containsAdvancedSyntax: ((latex: any) => boolean) & import("lodash").MemoizedFunction;
2
+ /** @type {(latex: string) => boolean} */
3
+ export const containsAdvancedSyntax: (latex: string) => boolean;
@@ -16,12 +16,14 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import { memoize } from 'lodash';
19
+ import { memoize } from 'es-toolkit/compat';
20
20
 
21
21
  // These commands all work fine with MathJax but either don't work, don't work well
22
22
  // (bad UX for editing), or look strange when rendered by Mathlive. Add new ones
23
23
  // here if you discover anything else or customers report unexpected experiences.
24
24
  const advancedOnlyCommands = ['begin', 'end', 'cases', 'cr', 'rm', 'text', 'hbox', 'mbox', 'unicode', 'cal', 'frak', 'it', 'scr', 'sf', '#', 'def', 'newcommand', 'operatorname', 'DeclareMathOperator', 'displaystyle', 'textstyle', 'scriptstyle', 'scriptscriptstyle', 'displaylines', 'abovewithdelims', 'array', 'bmatrix', 'buildrel', 'ddddot', 'dddot', 'eqalign', 'eqalignno', 'gcd', 'genfrac', 'hdashline', 'hfil', 'hfill', 'hfilll', 'hline', 'idotsint', 'iiiint', 'injlim', 'kern', 'label', 'LaTeX', 'leftroot', 'lgroup', 'lower', 'mathchoice', 'mathfrak', 'matrix', 'mit', 'mkern', 'mspace', 'negmedspace', 'negthickspace', 'negthinspace', 'newline', 'nobreakspace', 'oldstyle', 'overset', 'pmatrix', 'raise', 'rgroup', 'rule', 'Rule', 'skew', 'space', 'tag', 'TeX', 'underbrace', 'uproot', 'varinjlim', 'varliminf', 'varlimsup', 'varprojlim', 'vcenter', 'vmatrix'];
25
25
  const advancedOnlyRegex = new RegExp(advancedOnlyCommands.join('|'), 'm');
26
+
27
+ /** @type {(latex: string) => boolean} */
26
28
  const containsAdvancedSyntax = memoize(latex => advancedOnlyRegex.test(latex));
27
29
  export { advancedOnlyCommands, containsAdvancedSyntax };
@@ -11,6 +11,7 @@ declare class EquationEditorModal extends React.Component<any, any, any> {
11
11
  insertNewRange(): void;
12
12
  advancedModeOnly(latex: any): boolean;
13
13
  executeCommand: (cmd: any, advancedCmd: any) => void;
14
+ clearMathFieldBeforeDismiss: () => void;
14
15
  handleModalCancel: () => void;
15
16
  handleModalDone: () => void;
16
17
  renderMathInAdvancedPreview: import("@instructure/debounce").Debounced<() => void>;
@@ -68,7 +68,16 @@ export default class EquationEditorModal extends Component {
68
68
  });
69
69
  }
70
70
  };
71
+ // Clear the math-field before the modal unmounts to prevent XSS.
72
+ // Mathlive's dispose method sets element.innerHTML = this.model.getValue(),
73
+ // which would parse any HTML in the formula as real DOM elements.
74
+ this.clearMathFieldBeforeDismiss = () => {
75
+ if (this.mathField) {
76
+ this.setMathField('');
77
+ }
78
+ };
71
79
  this.handleModalCancel = () => {
80
+ this.clearMathFieldBeforeDismiss();
72
81
  this.props.onModalDismiss();
73
82
  };
74
83
  this.handleModalDone = () => {
@@ -80,11 +89,12 @@ export default class EquationEditorModal extends Component {
80
89
  if (output) {
81
90
  onEquationSubmit(output);
82
91
  }
92
+ this.clearMathFieldBeforeDismiss();
83
93
  onModalDismiss();
84
94
  };
85
95
  this.renderMathInAdvancedPreview = debounce(() => {
86
96
  if (this.previewElement.current) {
87
- this.previewElement.current.innerHTML = String.raw`\(${this.state.workingFormula}\)`;
97
+ this.previewElement.current.textContent = String.raw`\(${this.state.workingFormula}\)`;
88
98
  this.mathml.processNewMathInElem(this.previewElement.current);
89
99
  }
90
100
  }, EquationEditorModal.debounceRate, {
@@ -257,7 +267,7 @@ export default class EquationEditorModal extends Component {
257
267
  if (this.state.workingFormula) {
258
268
  this.renderMathInAdvancedPreview();
259
269
  } else {
260
- this.previewElement.current.innerHTML = '';
270
+ this.previewElement.current.textContent = '';
261
271
  }
262
272
  }
263
273
  componentDidMount() {
@@ -35,7 +35,7 @@ import { MAX_IMAGE_SIZE_BYTES } from '../../../../shared/compressionUtils';
35
35
  import { createCroppedImageSvg } from '../../../../shared/ImageCropper/imageCropUtils';
36
36
  import { convertFileToBase64 } from '../../../../shared/fileUtils';
37
37
  import { ImageSettingsPropTypes } from './propTypes';
38
- import _ from 'lodash';
38
+ import { isEqual } from 'es-toolkit/compat';
39
39
  const getCompressionMessage = () => formatMessage('Your image has been compressed for Icon Maker. Images less than {size} KB will not be compressed.', {
40
40
  size: MAX_IMAGE_SIZE_BYTES / 1024
41
41
  });
@@ -99,7 +99,7 @@ export const ImageOptions = ({
99
99
 
100
100
  // After submitting cropper modal a new embedded image should be generated
101
101
  useEffect(() => {
102
- if (state.cropperSettings && settings.imageSettings && !_.isEqual(state.cropperSettings, settings.imageSettings?.cropperSettings)) {
102
+ if (state.cropperSettings && settings.imageSettings && !isEqual(state.cropperSettings, settings.imageSettings?.cropperSettings)) {
103
103
  if (state.cropperSettings.shape !== settings.shape) {
104
104
  trayDispatch({
105
105
  shape: state.cropperSettings.shape
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import React, { useReducer, useEffect, useRef, Suspense } from 'react';
20
- import _ from 'lodash';
20
+ import { isEqual } from 'es-toolkit/compat';
21
21
  import PropTypes from 'prop-types';
22
22
  import formatMessage from '../../../../../../format-message';
23
23
  import reducer, { actions, initialState, modes } from '../../../reducers/imageSection';
@@ -161,7 +161,7 @@ export const ImageSection = ({
161
161
  // eslint-disable-next-line react-hooks/exhaustive-deps
162
162
  }, [settings.shape]);
163
163
  useEffect(() => {
164
- if (settings.imageSettings && !isMetadataLoaded.current && !_.isEqual(settings.imageSettings, metadata)) {
164
+ if (settings.imageSettings && !isMetadataLoaded.current && !isEqual(settings.imageSettings, metadata)) {
165
165
  isMetadataLoaded.current = true;
166
166
  dispatch({
167
167
  type: actions.UPDATE_SETTINGS.type,
@@ -171,7 +171,7 @@ export const ImageSection = ({
171
171
  // eslint-disable-next-line react-hooks/exhaustive-deps
172
172
  }, [settings.imageSettings]);
173
173
  useEffect(() => {
174
- if (!_.isEqual(metadata, settings.imageSettings)) {
174
+ if (!isEqual(metadata, settings.imageSettings)) {
175
175
  onChange({
176
176
  type: svgActions.SET_IMAGE_SETTINGS,
177
177
  payload: metadata
@@ -47,19 +47,34 @@ export namespace DEFAULT_SETTINGS {
47
47
  export let error: null;
48
48
  }
49
49
  export const BASE_SIZE: {
50
- [x: string]: number;
50
+ [Size.ExtraSmall]: number;
51
+ [Size.Small]: number;
52
+ [Size.Medium]: number;
53
+ [Size.Large]: number;
51
54
  };
52
55
  export const STROKE_WIDTH: {
53
- [x: string]: number;
56
+ [Size.None]: number;
57
+ [Size.Small]: number;
58
+ [Size.Medium]: number;
59
+ [Size.Large]: number;
54
60
  };
55
61
  export const TEXT_SIZE: {
56
- [x: string]: number;
62
+ [Size.Small]: number;
63
+ [Size.Medium]: number;
64
+ [Size.Large]: number;
65
+ [Size.ExtraLarge]: number;
57
66
  };
58
67
  export const TEXT_SIZE_FONT_DIFF: {
59
- [x: string]: number;
68
+ [Size.Small]: number;
69
+ [Size.Medium]: number;
70
+ [Size.Large]: number;
71
+ [Size.ExtraLarge]: number;
60
72
  };
61
73
  export const MAX_CHAR_COUNT: {
62
- [x: string]: number;
74
+ [Size.Small]: number;
75
+ [Size.Medium]: number;
76
+ [Size.Large]: number;
77
+ [Size.ExtraLarge]: number;
63
78
  };
64
79
  export const MAX_TOTAL_TEXT_CHARS: 32;
65
80
  export const TEXT_BACKGROUND_PADDING: 4;
@@ -1,3 +1,3 @@
1
1
  export function createSvgElement(tag: any, attributes?: {}): any;
2
2
  export function splitTextIntoLines(text: any, maxChars: any): string[];
3
- export function decode(input: any): string | null;
3
+ export function decode(input: any): string;
@@ -16,7 +16,7 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import _ from 'lodash';
19
+ import { isEqual } from 'es-toolkit/compat';
20
20
  export class IconMakerFormHasChanges {
21
21
  constructor(initSettings, currSettings) {
22
22
  this.initialSettings = void 0;
@@ -61,7 +61,7 @@ export class IconMakerFormHasChanges {
61
61
  return 'textPosition' in this.currentSettings && this.initialSettings.textPosition !== this.currentSettings.textPosition;
62
62
  }
63
63
  hasImageSettingsChange() {
64
- return 'imageSettings' in this.currentSettings && !_.isEqual(this.initialSettings.imageSettings, this.currentSettings.imageSettings);
64
+ return 'imageSettings' in this.currentSettings && !isEqual(this.initialSettings.imageSettings, this.currentSettings.imageSettings);
65
65
  }
66
66
  hasChanges() {
67
67
  return this.hasNameChange() || this.hasAltChange() || this.hasShapeNameChange() || this.hasShapeSizeChange() || this.hasColorNameChange() || this.hasOutlineColorChange() || this.hasOutlineSizeChange() || this.hasTextChange() || this.hasTextSizeChange() || this.hasTextColorChange() || this.hasTextBackgroundColorChange() || this.hasTextPositionChange() || this.hasImageSettingsChange();
@@ -42,7 +42,6 @@ export function scaleVideoSize(videoSize: any, naturalWidth: any, naturalHeight:
42
42
  export function labelForImageSize(imageSize: any): string;
43
43
  export const MIN_HEIGHT: 10;
44
44
  export const MIN_WIDTH: 10;
45
- export const MIN_WIDTH_VIDEO: 320;
46
45
  export const MIN_PERCENTAGE: 10;
47
46
  export const SMALL: "small";
48
47
  export const MEDIUM: "medium";
@@ -50,7 +49,6 @@ export const LARGE: "large";
50
49
  export const EXTRA_LARGE: "extra-large";
51
50
  export const CUSTOM: "custom";
52
51
  export const imageSizes: string[];
53
- export const videoSizes: string[];
54
52
  export const studioPlayerSizes: string[];
55
53
  export const MIN_WIDTH_STUDIO_PLAYER: number;
56
54
  export const MIN_HEIGHT_STUDIO_PLAYER: number;
@@ -18,10 +18,8 @@
18
18
 
19
19
  import formatMessage from '../../../format-message';
20
20
  import { scaleForHeight, scaleForWidth } from '../shared/DimensionUtils';
21
- import RCEGlobals from '../../../rce/RCEGlobals';
22
21
  export const MIN_HEIGHT = 10;
23
22
  export const MIN_WIDTH = 10;
24
- export const MIN_WIDTH_VIDEO = 320;
25
23
  export const MIN_PERCENTAGE = 10;
26
24
  export const SMALL = 'small';
27
25
  export const MEDIUM = 'medium';
@@ -29,7 +27,6 @@ export const LARGE = 'large';
29
27
  export const EXTRA_LARGE = 'extra-large';
30
28
  export const CUSTOM = 'custom';
31
29
  export const imageSizes = [SMALL, MEDIUM, LARGE, EXTRA_LARGE, CUSTOM];
32
- export const videoSizes = [MEDIUM, LARGE, EXTRA_LARGE, CUSTOM];
33
30
  export const studioPlayerSizes = [SMALL, MEDIUM, LARGE, CUSTOM];
34
31
  const sizeByMaximumDimension = {
35
32
  200: SMALL,
@@ -137,12 +134,8 @@ export function fromVideoEmbed($element) {
137
134
  } catch (_ignore) {
138
135
  // bad json?
139
136
  }
140
- if (RCEGlobals.getFeatures()?.consolidated_media_player) {
141
- const width = videoOptions.appliedWidth || videoOptions.naturalWidth;
142
- videoOptions.videoSize = [SMALL, MEDIUM, LARGE].find(size => width === studioPlayerDimensions[size].width) || CUSTOM;
143
- } else {
144
- videoOptions.videoSize = imageSizeFromKnownOptions(videoOptions);
145
- }
137
+ const width = videoOptions.appliedWidth || videoOptions.naturalWidth;
138
+ videoOptions.videoSize = [SMALL, MEDIUM, LARGE].find(size => width === studioPlayerDimensions[size].width) || CUSTOM;
146
139
  const source = $videoIframe.getAttribute('src');
147
140
  const matches = source?.match(/\/media_attachments_iframe\/(\d+)/);
148
141
  if (matches) {
@@ -16,16 +16,19 @@
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
+ import { trackPendoEvent } from '@instructure/canvas-media';
19
20
  import tinymce from 'tinymce';
20
21
  import bridge from '../../../bridge';
21
- import configureStore from '../../../sidebar/store/configureStore';
22
+ import { showFlashAlert } from '../../../common/FlashAlert';
23
+ import formatMessage from '../../../format-message';
22
24
  import { get as getSession } from '../../../sidebar/actions/session';
23
25
  import { uploadToMediaFolder } from '../../../sidebar/actions/upload';
24
- import doFileUpload from '../shared/Upload/doFileUpload';
25
- import formatMessage from '../../../format-message';
26
- import { isAudioOrVideo, isImage } from '../shared/fileTypeUtils';
27
- import { showFlashAlert } from '../../../common/FlashAlert';
26
+ import configureStore from '../../../sidebar/store/configureStore';
27
+ import RCEGlobals from '../../RCEGlobals';
28
28
  import { isMicrosoftWordContentInEvent } from '../shared/EventUtils';
29
+ import { isAudioOrVideo } from '../shared/fileTypeUtils';
30
+ import doFileUpload from '../shared/Upload/doFileUpload';
31
+
29
32
  // assume that if there are multiple RCEs on the page,
30
33
  // they all talk to the same canvas
31
34
  const config = {
@@ -110,13 +113,7 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
110
113
  size: file.size,
111
114
  domObject: file
112
115
  };
113
- let tabContext = 'documents';
114
- if (isImage(file.type)) {
115
- tabContext = 'images';
116
- } else if (isAudioOrVideo(file.type)) {
117
- tabContext = 'media';
118
- }
119
- store.dispatch(uploadToMediaFolder(tabContext, fileMetaProps));
116
+ store.dispatch(uploadToMediaFolder(fileMetaProps));
120
117
  return 'submitted';
121
118
  }
122
119
  }
@@ -158,6 +155,15 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
158
155
  // Skip audio and video files when disabled
159
156
  continue;
160
157
  }
158
+ if (isAudioOrVideo(file.type) && RCEGlobals.getFeatures()?.rce_asr_captioning_improvements) {
159
+ const trayProps = bridge.trayProps.get(editor);
160
+ trackPendoEvent('canvas_native_media_embedded', {
161
+ insertion_method: isPaste ? 'paste_file' : 'drag_drop',
162
+ media_kind: file.type.startsWith('audio/') ? 'audio' : 'video',
163
+ resourceType: trayProps?.contextType,
164
+ resourceId: trayProps?.contextId
165
+ });
166
+ }
161
167
 
162
168
  // This will finish once the dialog is closed, if one was created, putting this in a loop allows us
163
169
  // to show a dialog for each file without them conflicting.
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ModalProps } from '@instructure/ui-modal/types';
2
+ import { type ModalProps } from '@instructure/ui-modal';
3
3
  export declare function ExternalToolDialogModal(props: Pick<ModalProps, 'label' | 'open' | 'onOpen' | 'onClose' | 'mountNode'> & {
4
4
  onCloseButton: () => void;
5
5
  name: string;
@@ -1,4 +1,4 @@
1
- import { RceToolWrapper } from "../../RceToolWrapper";
1
+ import { RceToolWrapper } from '../../RceToolWrapper';
2
2
  /**
3
3
  * Returns a filtered list of items based on the term.
4
4
  *
@@ -17,22 +17,22 @@ import _pt from "prop-types";
17
17
  * with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  */
19
19
 
20
- import React, { useState } from "react";
21
- import { Modal } from "@instructure/ui-modal";
22
- import { Button, CloseButton } from "@instructure/ui-buttons";
23
- import { Heading } from "@instructure/ui-heading";
24
- import { View } from "@instructure/ui-view";
25
- import { ScreenReaderContent } from "@instructure/ui-a11y-content";
26
- import { List } from "@instructure/ui-list";
27
- import { Flex } from "@instructure/ui-flex";
28
- import { TextInput } from "@instructure/ui-text-input";
29
- import { IconSearchLine } from "@instructure/ui-icons";
30
- import { Alert } from "@instructure/ui-alerts";
31
- import formatMessage from "../../../../../format-message";
32
- import ExternalToolSelectionItem from "./ExternalToolSelectionItem";
33
- import { instuiPopupMountNodeFn } from "../../../../../util/fullscreenHelpers";
20
+ import React, { useState } from 'react';
21
+ import { Modal } from '@instructure/ui-modal';
22
+ import { Button, CloseButton } from '@instructure/ui-buttons';
23
+ import { Heading } from '@instructure/ui-heading';
24
+ import { View } from '@instructure/ui-view';
25
+ import { ScreenReaderContent } from '@instructure/ui-a11y-content';
26
+ import { List } from '@instructure/ui-list';
27
+ import { Flex } from '@instructure/ui-flex';
28
+ import { TextInput } from '@instructure/ui-text-input';
29
+ import { IconSearchLine } from '@instructure/ui-icons';
30
+ import { Alert } from '@instructure/ui-alerts';
31
+ import formatMessage from '../../../../../format-message';
32
+ import ExternalToolSelectionItem from './ExternalToolSelectionItem';
33
+ import { instuiPopupMountNodeFn } from '../../../../../util/fullscreenHelpers';
34
34
  // TODO: we really need a way for the client to pass this to the RCE
35
- const getLiveRegion = () => document.getElementById("flash_screenreader_holder");
35
+ const getLiveRegion = () => document.getElementById('flash_screenreader_holder');
36
36
 
37
37
  /**
38
38
  * Returns a filtered list of items based on the term.
@@ -51,7 +51,7 @@ export function filterItemsByTitleSubstring(searchString, items) {
51
51
  return items.filter(item => item.title.toLocaleLowerCase().includes(lowerTerm));
52
52
  }
53
53
  export function ExternalToolSelectionDialog(props) {
54
- const [filterTerm, setFilterTerm] = useState("");
54
+ const [filterTerm, setFilterTerm] = useState('');
55
55
  const [filteredResults, setFilteredResults] = useState(props.ltiButtons);
56
56
  const handleFilterChange = e => {
57
57
  setFilterTerm(e.target?.value);
@@ -63,31 +63,31 @@ export function ExternalToolSelectionDialog(props) {
63
63
  liveRegion: getLiveRegion,
64
64
  size: "medium",
65
65
  themeOverride: {
66
- mediumMaxWidth: "42rem"
66
+ mediumMaxWidth: '42rem'
67
67
  },
68
- label: formatMessage("Apps"),
68
+ label: formatMessage('Apps'),
69
69
  mountNode: instuiPopupMountNodeFn(),
70
70
  onDismiss: props.onDismiss,
71
71
  open: true,
72
72
  shouldCloseOnDocumentClick: false
73
73
  }, /*#__PURE__*/React.createElement(Modal.Header, {
74
74
  themeOverride: {
75
- padding: "0.5rem"
75
+ padding: '0.5rem'
76
76
  }
77
77
  }, /*#__PURE__*/React.createElement(CloseButton, {
78
78
  placement: "end",
79
79
  offset: "medium",
80
80
  onClick: props.onDismiss,
81
- screenReaderLabel: formatMessage("Close")
81
+ screenReaderLabel: formatMessage('Close')
82
82
  }), /*#__PURE__*/React.createElement(Heading, {
83
83
  margin: "small"
84
- }, formatMessage("All Apps")), /*#__PURE__*/React.createElement(View, {
84
+ }, formatMessage('All Apps')), /*#__PURE__*/React.createElement(View, {
85
85
  as: "div",
86
86
  padding: "x-small none x-small medium"
87
87
  }, /*#__PURE__*/React.createElement(TextInput, {
88
88
  type: "search",
89
- renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage("Search")),
90
- placeholder: formatMessage("Search"),
89
+ renderLabel: /*#__PURE__*/React.createElement(ScreenReaderContent, null, formatMessage('Search')),
90
+ placeholder: formatMessage('Search'),
91
91
  renderAfterInput: /*#__PURE__*/React.createElement(IconSearchLine, {
92
92
  inline: false
93
93
  }),
@@ -105,7 +105,7 @@ export function ExternalToolSelectionDialog(props) {
105
105
  liveRegion: getLiveRegion,
106
106
  variant: "info",
107
107
  screenReaderOnly: !filterEmpty
108
- }, filterEmpty && formatMessage("No results found for {filterTerm}", {
108
+ }, filterEmpty && formatMessage('No results found for {filterTerm}', {
109
109
  filterTerm
110
110
  }), !filterEmpty && formatMessage(`Found { count, plural,
111
111
  =0 {# results}
@@ -116,7 +116,7 @@ export function ExternalToolSelectionDialog(props) {
116
116
  })), renderTools(filteredResults)))), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement(Button, {
117
117
  onClick: props.onDismiss,
118
118
  color: "primary"
119
- }, formatMessage("Done"))));
119
+ }, formatMessage('Done'))));
120
120
  function renderTools(ltiButtons) {
121
121
  return /*#__PURE__*/React.createElement(List, {
122
122
  isUnstyled: true
@@ -3,6 +3,10 @@ import React from 'react';
3
3
  * Provide an iframe for launching an LTI tool directly from the frontend.
4
4
  * Works just like all existing usages of the LTI <iframe> element, including
5
5
  * extracting a ref of the <iframe> directly and setting things on it later.
6
+ *
7
+ * TODO: consider moving allow stuff here instead of having it be the
8
+ * responsibility of the caller, need to check if RCE uses same
9
+ * iframeAllowances() as elsewhere
6
10
  */
7
11
  declare const ToolLaunchIframe: React.ForwardRefExoticComponent<React.IframeHTMLAttributes<HTMLIFrameElement> & {
8
12
  children?: React.ReactNode | undefined;
@@ -23,6 +23,10 @@ import formatMessage from '../../../../../format-message';
23
23
  * Provide an iframe for launching an LTI tool directly from the frontend.
24
24
  * Works just like all existing usages of the LTI <iframe> element, including
25
25
  * extracting a ref of the <iframe> directly and setting things on it later.
26
+ *
27
+ * TODO: consider moving allow stuff here instead of having it be the
28
+ * responsibility of the caller, need to check if RCE uses same
29
+ * iframeAllowances() as elsewhere
26
30
  */
27
31
  const ToolLaunchIframe = /*#__PURE__*/React.forwardRef((props, ref) => {
28
32
  return /*#__PURE__*/React.createElement("iframe", Object.assign({
@@ -4,14 +4,23 @@ export default class TrayController {
4
4
  _shouldOpen: boolean;
5
5
  _editor: any;
6
6
  _audioContainer: Element | null;
7
+ _captionsModified: boolean;
8
+ requestSubtitlesFromIframe(cb: any): void;
7
9
  get container(): HTMLElement;
8
10
  get isOpen(): boolean;
9
11
  showTrayForEditor(editor: any): void;
12
+ _isPlayerReady: boolean | undefined;
10
13
  hideTrayForEditor(editor: any): void;
14
+ _listenForPlayerIframeToLoad(currentMediaId: any): void;
15
+ _iframeLoadingListener: AbortController | undefined;
16
+ _reloadAudioPlayer(): void;
11
17
  _dismissTray(): void;
12
18
  _resetController(): Node;
19
+ _resizeContainer({ height, width }: {
20
+ height: any;
21
+ width: any;
22
+ }): void;
13
23
  _applyAudioOptions(audioOptions: any): any;
14
- requestSubtitlesFromIframe(cb: any): void;
15
24
  _subtitleListener: AbortController | undefined;
16
- _renderTray(trayProps: any): void;
25
+ _renderTray(): void;
17
26
  }