subtitles-editor
Version:
A React component for editing SRT and VTT subtitles directly in a textarea, styled with TailwindCSS.
617 lines (607 loc) • 43.8 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SubtitlesEditor = {}, global.React));
})(this, (function (exports, React) { 'use strict';
function styleInject(css, ref) {
if (ref === void 0) ref = {};
var insertAt = ref.insertAt;
if (typeof document === 'undefined') {
return;
}
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\r\n.static {\n position: static;\n}\r\n.fixed {\n position: fixed;\n}\r\n.relative {\n position: relative;\n}\r\n.inset-0 {\n inset: 0px;\n}\r\n.z-10 {\n z-index: 10;\n}\r\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\r\n.mb-2 {\n margin-bottom: 0.5rem;\n}\r\n.mb-3 {\n margin-bottom: 0.75rem;\n}\r\n.mb-4 {\n margin-bottom: 1rem;\n}\r\n.mt-2 {\n margin-top: 0.5rem;\n}\r\n.mt-3 {\n margin-top: 0.75rem;\n}\r\n.flex {\n display: flex;\n}\r\n.inline-flex {\n display: inline-flex;\n}\r\n.grid {\n display: grid;\n}\r\n.hidden {\n display: none;\n}\r\n.size-12 {\n width: 3rem;\n height: 3rem;\n}\r\n.size-6 {\n width: 1.5rem;\n height: 1.5rem;\n}\r\n.max-h-\\[400px\\] {\n max-height: 400px;\n}\r\n.min-h-\\[400px\\] {\n min-height: 400px;\n}\r\n.min-h-full {\n min-height: 100%;\n}\r\n.w-full {\n width: 100%;\n}\r\n.w-screen {\n width: 100vw;\n}\r\n.shrink-0 {\n flex-shrink: 0;\n}\r\n.transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\r\n.grid-cols-1 {\n grid-template-columns: repeat(1, minmax(0, 1fr));\n}\r\n.items-end {\n align-items: flex-end;\n}\r\n.items-center {\n align-items: center;\n}\r\n.justify-center {\n justify-content: center;\n}\r\n.justify-between {\n justify-content: space-between;\n}\r\n.gap-4 {\n gap: 1rem;\n}\r\n.space-x-2 > :not([hidden]) ~ :not([hidden]) {\n --tw-space-x-reverse: 0;\n margin-right: calc(0.5rem * var(--tw-space-x-reverse));\n margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));\n}\r\n.space-x-4 > :not([hidden]) ~ :not([hidden]) {\n --tw-space-x-reverse: 0;\n margin-right: calc(1rem * var(--tw-space-x-reverse));\n margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));\n}\r\n.overflow-hidden {\n overflow: hidden;\n}\r\n.overflow-y-auto {\n overflow-y: auto;\n}\r\n.rounded {\n border-radius: 0.25rem;\n}\r\n.rounded-full {\n border-radius: 9999px;\n}\r\n.rounded-lg {\n border-radius: 0.5rem;\n}\r\n.rounded-md {\n border-radius: 0.375rem;\n}\r\n.border {\n border-width: 1px;\n}\r\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(107 114 128 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-500\\/75 {\n background-color: rgb(107 114 128 / 0.75);\n}\r\n.bg-green-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(34 197 94 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-600 {\n --tw-bg-opacity: 1;\n background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1));\n}\r\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\r\n.bg-yellow-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(234 179 8 / var(--tw-bg-opacity, 1));\n}\r\n.p-2 {\n padding: 0.5rem;\n}\r\n.p-3 {\n padding: 0.75rem;\n}\r\n.p-4 {\n padding: 1rem;\n}\r\n.px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\r\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\r\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\r\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\r\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\r\n.pb-4 {\n padding-bottom: 1rem;\n}\r\n.pt-5 {\n padding-top: 1.25rem;\n}\r\n.text-left {\n text-align: left;\n}\r\n.text-center {\n text-align: center;\n}\r\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\r\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\r\n.font-semibold {\n font-weight: 600;\n}\r\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\r\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(17 24 39 / var(--tw-text-opacity, 1));\n}\r\n.text-green-600 {\n --tw-text-opacity: 1;\n color: rgb(22 163 74 / var(--tw-text-opacity, 1));\n}\r\n.text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\r\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\r\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\r\n.shadow-xl {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\r\n.ring-1 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\r\n.ring-inset {\n --tw-ring-inset: inset;\n}\r\n.ring-gray-300 {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity, 1));\n}\r\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\r\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\r\n.transition-opacity {\n transition-property: opacity;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\r\n\r\n[contenteditable][placeholder]:empty:before {\r\n content: attr(placeholder);\r\n position: absolute;\r\n color: gray;\r\n background-color: transparent;\r\n}\r\n\r\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\r\n\r\n.hover\\:bg-red-500:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1));\n}\r\n\r\n@media (min-width: 640px) {\n\n .sm\\:mx-0 {\n margin-left: 0px;\n margin-right: 0px;\n }\n\n .sm\\:my-8 {\n margin-top: 2rem;\n margin-bottom: 2rem;\n }\n\n .sm\\:ml-3 {\n margin-left: 0.75rem;\n }\n\n .sm\\:ml-4 {\n margin-left: 1rem;\n }\n\n .sm\\:mt-0 {\n margin-top: 0px;\n }\n\n .sm\\:flex {\n display: flex;\n }\n\n .sm\\:size-10 {\n width: 2.5rem;\n height: 2.5rem;\n }\n\n .sm\\:w-auto {\n width: auto;\n }\n\n .sm\\:w-full {\n width: 100%;\n }\n\n .sm\\:max-w-lg {\n max-width: 32rem;\n }\n\n .sm\\:flex-row-reverse {\n flex-direction: row-reverse;\n }\n\n .sm\\:items-start {\n align-items: flex-start;\n }\n\n .sm\\:items-center {\n align-items: center;\n }\n\n .sm\\:p-0 {\n padding: 0px;\n }\n\n .sm\\:p-6 {\n padding: 1.5rem;\n }\n\n .sm\\:px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n }\n\n .sm\\:pb-4 {\n padding-bottom: 1rem;\n }\n\n .sm\\:text-left {\n text-align: left;\n }\n}\r\n\r\n@media (min-width: 1024px) {\n\n .lg\\:grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}";
styleInject(css_248z);
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _classCallCheck(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
writable: false
}), e;
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: true,
configurable: true,
writable: true
}) : e[r] = t, e;
}
function _iterableToArray(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = true,
o = false;
try {
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = true, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
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;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), true).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;
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
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);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function Dialog(_ref) {
var onConfirm = _ref.onConfirm,
onCancel = _ref.onCancel;
return /*#__PURE__*/React.createElement("div", {
className: "relative z-10",
"aria-labelledby": "modal-title",
role: "dialog",
"aria-modal": "true"
}, /*#__PURE__*/React.createElement("div", {
className: "fixed inset-0 bg-gray-500/75 transition-opacity",
"aria-hidden": "true"
}), /*#__PURE__*/React.createElement("div", {
className: "fixed inset-0 z-10 w-screen overflow-y-auto"
}, /*#__PURE__*/React.createElement("div", {
className: "flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0"
}, /*#__PURE__*/React.createElement("div", {
className: "relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg"
}, /*#__PURE__*/React.createElement("div", {
className: "bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"
}, /*#__PURE__*/React.createElement("div", {
className: "sm:flex sm:items-start"
}, /*#__PURE__*/React.createElement("div", {
className: "mx-auto flex size-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:size-10"
}, /*#__PURE__*/React.createElement("svg", {
className: "size-6 text-red-600",
fill: "none",
viewBox: "0 0 24 24",
strokeWidth: "1.5",
stroke: "currentColor",
"aria-hidden": "true",
"data-slot": "icon"
}, /*#__PURE__*/React.createElement("path", {
strokeLinecap: "round",
strokeLinejoin: "round",
d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
}))), /*#__PURE__*/React.createElement("div", {
className: "mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"
}, /*#__PURE__*/React.createElement("h3", {
className: "text-base font-semibold text-gray-900",
id: "modal-title"
}, "Confirm deletion."), /*#__PURE__*/React.createElement("div", {
className: "mt-2"
}, /*#__PURE__*/React.createElement("p", {
className: "text-sm text-gray-500"
}, "Are you sure you want to delete this subtitle? This action is irreversible."))))), /*#__PURE__*/React.createElement("div", {
className: "flex justify-between bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6"
}, /*#__PURE__*/React.createElement("button", {
onClick: onConfirm,
type: "button",
className: "inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-red-500 sm:ml-3 sm:w-auto"
}, "Delete"), /*#__PURE__*/React.createElement("button", {
onClick: onCancel,
type: "button",
className: "mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50 sm:mt-0 sm:w-auto"
}, "Cancel"))))));
}
function ContentEditable(_ref) {
var className = _ref.className,
value = _ref.value,
onChange = _ref.onChange,
placeholder = _ref.placeholder;
var contentEditableRef = React.useRef(null);
React.useEffect(function () {
if (contentEditableRef.current && contentEditableRef.current.innerText !== value) {
contentEditableRef.current.innerText = value;
}
}, [value]);
var handleInput = function handleInput(event) {
var newValue = event.currentTarget.innerText.trim();
if (newValue === '' || newValue === '<br>') {
onChange('');
} else {
onChange(newValue);
}
};
return /*#__PURE__*/React.createElement("div", {
className: className,
contentEditable: true,
ref: contentEditableRef,
onInput: handleInput,
placeholder: placeholder,
role: "textbox"
});
}
function SubtitlesEditor(_ref) {
var subtitles = _ref.subtitles,
onChange = _ref.onChange,
_ref$editable = _ref.editable,
editable = _ref$editable === void 0 ? true : _ref$editable,
_ref$placeholderSubti = _ref.placeholderSubtitle,
placeholderSubtitle = _ref$placeholderSubti === void 0 ? "Typing here..." : _ref$placeholderSubti,
_ref$addSubtitleButto = _ref.addSubtitleButton,
addSubtitleButton = _ref$addSubtitleButto === void 0 ? false : _ref$addSubtitleButto,
onAddSubtitle = _ref.onAddSubtitle,
_ref$deleteSubtitleBu = _ref.deleteSubtitleButton,
deleteSubtitleButton = _ref$deleteSubtitleBu === void 0 ? false : _ref$deleteSubtitleBu,
_ref$timingEditable = _ref.timingEditable,
timingEditable = _ref$timingEditable === void 0 ? false : _ref$timingEditable,
onSave = _ref.onSave,
_ref$downloadEnabled = _ref.downloadEnabled,
downloadEnabled = _ref$downloadEnabled === void 0 ? false : _ref$downloadEnabled,
_ref$undoEnabled = _ref.undoEnabled,
undoEnabled = _ref$undoEnabled === void 0 ? true : _ref$undoEnabled,
_ref$redoEnabled = _ref.redoEnabled,
redoEnabled = _ref$redoEnabled === void 0 ? true : _ref$redoEnabled,
_ref$buttons = _ref.buttons,
buttons = _ref$buttons === void 0 ? {
addSubtitle: {
text: "Add a subtitle.",
className: ""
},
save: {
text: "Save",
className: ""
},
download: {
text: "Download",
className: ""
},
"delete": {
text: "Delete",
className: ""
},
insertTimecode: {
text: "Insert timecode",
className: ""
},
undo: {
className: ""
},
redo: {
className: ""
}
} : _ref$buttons,
undoButton = _ref.undoButton,
redoButton = _ref.redoButton;
var _useState = React.useState(subtitles),
_useState2 = _slicedToArray(_useState, 2),
editedSubtitles = _useState2[0],
setEditedSubtitles = _useState2[1];
var _useState3 = React.useState(false),
_useState4 = _slicedToArray(_useState3, 2),
isDialogOpen = _useState4[0],
setIsDialogOpen = _useState4[1];
var _useState5 = React.useState(null),
_useState6 = _slicedToArray(_useState5, 2),
subtitleToDelete = _useState6[0],
setSubtitleToDelete = _useState6[1];
var _useState7 = React.useState(''),
_useState8 = _slicedToArray(_useState7, 2),
notification = _useState8[0],
setNotification = _useState8[1];
var _useState9 = React.useState(function () {
return subtitles.map(function () {
return {
history: [],
index: -1
};
});
}),
_useState10 = _slicedToArray(_useState9, 2),
subtitlesHistory = _useState10[0],
setSubtitlesHistory = _useState10[1];
var fileInputRef = React.useRef(null);
var defaultButtonClass = "px-4 py-2 text-white rounded";
var getButtonClass = React.useCallback(function (userClass) {
return userClass ? userClass : defaultButtonClass;
}, []);
var handleSubtitleChange = React.useCallback(function (index, field, value) {
setEditedSubtitles(function (prevSubtitles) {
var newSubtitles = _toConsumableArray(prevSubtitles);
newSubtitles[index] = _objectSpread2(_objectSpread2({}, newSubtitles[index]), {}, _defineProperty({}, field, value));
saveHistory(index, newSubtitles);
onChange === null || onChange === void 0 || onChange(newSubtitles);
return newSubtitles;
});
}, [onChange]);
var saveHistory = React.useCallback(function (index, newSubtitles) {
setSubtitlesHistory(function (prevHistory) {
var updatedHistory = _toConsumableArray(prevHistory);
if (!updatedHistory[index]) {
updatedHistory[index] = {
history: [],
index: -1
};
}
updatedHistory[index].history = [].concat(_toConsumableArray(updatedHistory[index].history.slice(0, updatedHistory[index].index + 1)), [newSubtitles[index]]);
updatedHistory[index].index += 1;
return updatedHistory;
});
}, []);
var handleHistoryNavigation = React.useCallback(function (index, direction) {
setSubtitlesHistory(function (prevHistory) {
var updatedHistory = _toConsumableArray(prevHistory);
var subtitleHistory = updatedHistory[index];
if (!subtitleHistory) return prevHistory;
var newIndex = subtitleHistory.index + direction;
if (newIndex >= 0 && newIndex < subtitleHistory.history.length) {
setEditedSubtitles(function (prev) {
var updatedSubtitles = _toConsumableArray(prev);
updatedSubtitles[index] = subtitleHistory.history[newIndex];
return updatedSubtitles;
});
subtitleHistory.index = newIndex;
}
return updatedHistory;
});
}, []);
var handleAddSubtitle = function handleAddSubtitle() {
var lastSubtitle = editedSubtitles[editedSubtitles.length - 1] || {};
var newSubtitle = {
text: "",
start: lastSubtitle.end || "",
end: ""
};
setEditedSubtitles([].concat(_toConsumableArray(editedSubtitles), [newSubtitle]));
var newSubtitlesHistory = _toConsumableArray(subtitlesHistory);
if (!newSubtitlesHistory[editedSubtitles.length]) {
newSubtitlesHistory.push({
history: [],
index: -1
});
}
saveHistory(editedSubtitles.length, [].concat(_toConsumableArray(editedSubtitles), [newSubtitle]));
setSubtitlesHistory(newSubtitlesHistory);
if (onAddSubtitle) onAddSubtitle(newSubtitle);
};
var handleSave = function handleSave() {
if (onSave) {
onSave(editedSubtitles);
showNotification('Subtitles saved successfully.');
}
};
var handleDeleteSubtitle = function handleDeleteSubtitle(index) {
setSubtitleToDelete(index);
setIsDialogOpen(true);
};
var confirmDelete = function confirmDelete() {
var newSubtitles = editedSubtitles.filter(function (_, i) {
return i !== subtitleToDelete;
});
setEditedSubtitles(newSubtitles);
saveHistory(subtitleToDelete, newSubtitles);
setIsDialogOpen(false);
setSubtitleToDelete(null);
showNotification('Subtitle deleted.');
};
var cancelDelete = function cancelDelete() {
setIsDialogOpen(false);
setSubtitleToDelete(null);
};
var formatTime = function formatTime() {
var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
var parts = time.split(":");
if (parts.length === 3) {
var _parts$2$split = parts[2].split("."),
_parts$2$split2 = _slicedToArray(_parts$2$split, 2),
seconds = _parts$2$split2[0],
_parts$2$split2$ = _parts$2$split2[1],
milliseconds = _parts$2$split2$ === void 0 ? "000" : _parts$2$split2$;
return "".concat(parts[0], ":").concat(parts[1], ":").concat(seconds, ".").concat(milliseconds.padEnd(3, "0"));
}
return "00:00:00.000";
};
var insertTimecode = function insertTimecode(index) {
setEditedSubtitles(function (prev) {
var updatedSubtitles = _toConsumableArray(prev);
var subtitle = updatedSubtitles[index];
var timecode = "\n".concat(formatTime(subtitle.start), " --> ").concat(formatTime(subtitle.end));
updatedSubtitles[index] = _objectSpread2(_objectSpread2({}, subtitle), {}, {
text: "".concat(subtitle.text, "\n").concat(timecode)
});
return updatedSubtitles;
});
};
var handleDownload = function handleDownload() {
if (!downloadEnabled) return;
editedSubtitles.forEach(function (subtitle, index) {
var subtitleText = "".concat(subtitle.text, "\n\n");
var blob = new Blob([subtitleText], {
type: 'text/plain'
});
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "subtitle_".concat(index + 1, ".vtt");
link.click();
});
};
var showNotification = function showNotification(message) {
setNotification(message);
setTimeout(function () {
return setNotification('');
}, 3000);
};
var handleFileUpload = function handleFileUpload(index, event) {
var file = event.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function () {
var fileContent = reader.result;
addContentToSubtitle(index, fileContent);
};
reader.readAsText(file);
event.target.value = null;
};
var addContentToSubtitle = function addContentToSubtitle(index, content) {
var newSubtitles = _toConsumableArray(editedSubtitles);
if (newSubtitles[index]) {
newSubtitles[index] = _objectSpread2(_objectSpread2({}, newSubtitles[index]), {}, {
text: newSubtitles[index].text + content
});
setEditedSubtitles(newSubtitles);
if (onChange) onChange(newSubtitles);
showNotification("Content of the file added to the subtitle.");
} else {
console.error("Subtitle not found at index.", index);
}
};
React.useEffect(function () {
if (!Array.isArray(subtitles)) {
console.error("Subtitles must be an array.");
return;
}
var validSubtitles = subtitles.map(function (subtitle) {
return {
text: subtitle.text || "",
start: subtitle.start || "",
end: subtitle.end || ""
};
});
setEditedSubtitles(validSubtitles);
}, [subtitles]);
return /*#__PURE__*/React.createElement("div", {
className: "p-4 bg-gray-100 rounded-lg shadow-md"
}, /*#__PURE__*/React.createElement("div", {
className: "mb-4 flex justify-between"
}, /*#__PURE__*/React.createElement("div", null, notification && /*#__PURE__*/React.createElement("div", {
className: "text-green-600"
}, notification))), /*#__PURE__*/React.createElement("div", {
className: "grid grid-cols-1 gap-4 lg:grid-cols-2"
}, editedSubtitles.map(function (subtitle, index) {
return /*#__PURE__*/React.createElement("div", {
key: index,
className: "mb-3 p-3 border rounded bg-white"
}, timingEditable && /*#__PURE__*/React.createElement("div", {
className: "flex space-x-2 mb-2 items-center"
}, /*#__PURE__*/React.createElement("input", {
type: "time",
step: "1",
className: "p-2 border rounded",
value: subtitle.start || "00:00:00",
onChange: function onChange(e) {
return handleSubtitleChange(index, 'start', e.target.value);
},
disabled: !timingEditable
}), /*#__PURE__*/React.createElement("input", {
type: "time",
step: "1",
className: "p-2 border rounded",
value: subtitle.end || "00:00:00",
onChange: function onChange(e) {
return handleSubtitleChange(index, 'end', e.target.value);
},
disabled: !timingEditable
}), /*#__PURE__*/React.createElement("button", {
onClick: function onClick() {
return insertTimecode(index);
},
className: "px-3 py-1 bg-gray-500 text-white rounded"
}, buttons.insertTimecode.text)), subtitle && /*#__PURE__*/React.createElement(ContentEditable, {
className: "editor w-full min-h-[400px] max-h-[400px] p-2 border rounded bg-white overflow-y-auto",
value: subtitle.text,
onChange: function onChange(newValue) {
return handleSubtitleChange(index, "text", newValue);
},
placeholder: placeholderSubtitle
}), deleteSubtitleButton && editable && /*#__PURE__*/React.createElement("div", {
className: "flex justify-between mt-2"
}, /*#__PURE__*/React.createElement("button", {
onClick: function onClick() {
return handleDeleteSubtitle(index);
},
className: getButtonClass(buttons["delete"].className) + " bg-red-500"
}, buttons["delete"].text), /*#__PURE__*/React.createElement("button", {
onClick: function onClick() {
return fileInputRef.current.click();
},
className: getButtonClass(buttons.insertTimecode.className) + " bg-gray-500"
}, "Upload subtitles"), /*#__PURE__*/React.createElement("input", {
key: index,
ref: fileInputRef,
type: "file",
accept: ".srt, .vtt",
onChange: function onChange(event) {
return handleFileUpload(index, event);
},
className: "hidden"
}), /*#__PURE__*/React.createElement("div", {
className: "flex space-x-4"
}, undoEnabled && undoButton && /*#__PURE__*/React.createElement("div", {
onClick: function onClick() {
return handleHistoryNavigation(index, -1);
},
className: buttons.undo.className
}, undoButton), redoEnabled && redoButton && /*#__PURE__*/React.createElement("div", {
onClick: function onClick() {
return handleHistoryNavigation(index, 1);
},
className: buttons.redo.className
}, redoButton))));
})), /*#__PURE__*/React.createElement("div", {
className: "flex justify-between"
}, addSubtitleButton && editable && /*#__PURE__*/React.createElement("button", {
onClick: handleAddSubtitle,
className: getButtonClass(buttons.addSubtitle.className) + " bg-blue-500"
}, buttons.addSubtitle.text), onSave && /*#__PURE__*/React.createElement("button", {
onClick: handleSave,
className: getButtonClass(buttons.save.className) + " bg-green-500"
}, buttons.save.text), downloadEnabled && /*#__PURE__*/React.createElement("button", {
onClick: handleDownload,
className: getButtonClass(buttons.download.className) + " bg-yellow-500"
}, buttons.download.text)), isDialogOpen && /*#__PURE__*/React.createElement(Dialog, {
onConfirm: confirmDelete,
onCancel: cancelDelete
}));
}
var Subtitle = /*#__PURE__*/function () {
function Subtitle() {
var content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
_classCallCheck(this, Subtitle);
this.content = content;
}
return _createClass(Subtitle, null, [{
key: "convertSrtToVtt",
value: function convertSrtToVtt(srtContent) {
var vttContent = "WEBVTT\n\n";
var vttText = srtContent.replace(/(\d{2}:\d{2}:\d{2}),(\d{3}) --> (\d{2}:\d{2}:\d{2}),(\d{3})/g, '$1.$2 --> $3.$4');
return vttContent + vttText.trim();
}
}, {
key: "convertVttToSrt",
value: function convertVttToSrt(vttContent) {
var srtContent = vttContent.replace(/^WEBVTT[\r\n]*\s*/g, "");
var srtText = srtContent.replace(/(\d{2}:\d{2}:\d{2})\.(\d{3}) --> (\d{2}:\d{2}:\d{2})\.(\d{3})/g, function (match, p1, p2, p3, p4) {
return "".concat(p1, ":").concat(p2, " --> ").concat(p3, ":").concat(p4);
});
var srtWithNumbers = srtText.replace(/(?:\d{2}:\d{2}:\d{2}:\d{3}) --> /g, function (match, index) {
var indexCount = 1;
return "".concat(indexCount++, "\n");
});
return srtWithNumbers.trim();
}
}, {
key: "removeHtml",
value: function removeHtml(text) {
return text.replace(/<[^>]*>/g, '');
}
}]);
}();
exports.Subtitle = Subtitle;
exports.SubtitlesEditor = SubtitlesEditor;
}));