@lizardbyte/shared-web 2026.811.210538 → 2026.920.12131

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 (35) hide show
  1. package/README.md +43 -0
  2. package/dist/crowdin-dockle-css.css +3 -0
  3. package/dist/crowdin-dockle-css.css.map +1 -0
  4. package/dist/crowdin.js +1 -1
  5. package/dist/crowdin.js.map +1 -1
  6. package/dist/format-number.js.map +1 -1
  7. package/dist/levenshtein-distance.js.map +1 -1
  8. package/dist/load-script.js.map +1 -1
  9. package/dist/ranking-sorter.js.map +1 -1
  10. package/dist/sleep.js.map +1 -1
  11. package/package.json +21 -18
  12. package/src/css/crowdin-dockle.scss +42 -0
  13. package/src/js/crowdin-dockle-css.js +1 -0
  14. package/src/js/crowdin.js +62 -48
  15. package/tests/crowdin.test.js +45 -99
  16. package/dist/crowdin-clean-jsdoc-css.css +0 -3
  17. package/dist/crowdin-clean-jsdoc-css.css.map +0 -1
  18. package/dist/crowdin-doxygen-css.css +0 -3
  19. package/dist/crowdin-doxygen-css.css.map +0 -1
  20. package/dist/crowdin-doxygen-css.js +0 -0
  21. package/dist/crowdin-furo-css.css +0 -3
  22. package/dist/crowdin-furo-css.css.map +0 -1
  23. package/dist/crowdin-furo-css.js +0 -0
  24. package/dist/crowdin-rustdoc-css.css +0 -3
  25. package/dist/crowdin-rustdoc-css.css.map +0 -1
  26. package/dist/crowdin-rustdoc-css.js +0 -0
  27. package/src/css/crowdin-clean-jsdoc.scss +0 -36
  28. package/src/css/crowdin-doxygen.scss +0 -20
  29. package/src/css/crowdin-furo.scss +0 -20
  30. package/src/css/crowdin-rustdoc.scss +0 -30
  31. package/src/js/crowdin-clean-jsdoc-css.js +0 -1
  32. package/src/js/crowdin-doxygen-css.js +0 -1
  33. package/src/js/crowdin-furo-css.js +0 -1
  34. package/src/js/crowdin-rustdoc-css.js +0 -1
  35. /package/dist/{crowdin-clean-jsdoc-css.js → crowdin-dockle-css.js} +0 -0
package/README.md CHANGED
@@ -65,3 +65,46 @@ icon filenames that contain spaces or other reserved characters.
65
65
  ```bash
66
66
  npm install @lizardbyte/shared-web --ignore-scripts
67
67
  ```
68
+
69
+ ## CrowdIn in JSDoc
70
+
71
+ Create an initializer such as `docs/crowdin-init.js`:
72
+
73
+ ```javascript
74
+ globalThis.initCrowdIn('LizardByte-docs', 'dockle');
75
+ ```
76
+
77
+ Then configure Dockle to load the CrowdIn assets from either jsDelivr or the installed npm package.
78
+
79
+ ### jsDelivr
80
+
81
+ ```toml
82
+ [targets.jsdoc]
83
+ extra_files = ["docs/crowdin-init.js"]
84
+ extra_stylesheets = ["https://cdn.jsdelivr.net/npm/@lizardbyte/shared-web@latest/dist/crowdin-dockle-css.css"]
85
+ extra_javascript = [
86
+ "https://cdn.jsdelivr.net/npm/@lizardbyte/shared-web@latest/dist/crowdin.js",
87
+ "crowdin-init.js",
88
+ ]
89
+ ```
90
+
91
+ Replace `latest` with a published package version for immutable URLs.
92
+
93
+ ### npm
94
+
95
+ Install the package, then configure Dockle to copy and load its assets:
96
+
97
+ ```bash
98
+ npm install --save-dev @lizardbyte/shared-web --ignore-scripts
99
+ ```
100
+
101
+ ```toml
102
+ [targets.jsdoc]
103
+ extra_files = [
104
+ "node_modules/@lizardbyte/shared-web/dist/crowdin.js",
105
+ "node_modules/@lizardbyte/shared-web/dist/crowdin-dockle-css.css",
106
+ "docs/crowdin-init.js",
107
+ ]
108
+ extra_stylesheets = ["crowdin-dockle-css.css"]
109
+ extra_javascript = ["crowdin.js", "crowdin-init.js"]
110
+ ```
@@ -0,0 +1,3 @@
1
+ #crowdin-language-picker.dockle-crowdin-picker{bottom:1rem !important;left:1.25rem !important;left:var(--dockle-sidebar-padding, 1.25rem) !important;position:fixed !important;width:calc(18rem - 2*1.25rem) !important;width:calc(var(--dockle-sidebar-width, 18rem) - 2*var(--dockle-sidebar-padding, 1.25rem)) !important;z-index:200}#crowdin-language-picker.dockle-crowdin-picker .cr-picker-button{box-sizing:border-box;width:100%}#crowdin-language-picker .cr-picker-button,#crowdin-language-picker .cr-picker-submenu{background-color:var(--dockle-background-secondary) !important;border:1px solid var(--dockle-border) !important;border-radius:var(--dockle-radius) !important;color:var(--dockle-content) !important}#crowdin-language-picker .cr-picker-button:hover,#crowdin-language-picker .cr-picker-submenu>a:hover{background-color:var(--dockle-primary) !important;color:#fff !important}#crowdin-language-picker .cr-picker-submenu>a{color:var(--dockle-content) !important}#crowdin-language-picker .cr-picker-submenu>a.cr-selected{color:var(--dockle-primary) !important}@media(max-width: 50rem){#crowdin-language-picker.dockle-crowdin-picker{bottom:1rem !important;left:1rem !important;width:min(18rem,100vw - 2rem) !important}}
2
+
3
+ /*# sourceMappingURL=crowdin-dockle-css.css.map*/
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crowdin-dockle-css.css","mappings":"AAAA,+CACE,uBACA,+EACA,0BACA,8IACA,YAGF,iEACE,sBACA,WAGF,uFAEE,+DACA,iDACA,8CACA,uCAGF,qGAEE,kDACA,sBAGF,8CACE,uCAGF,0DACE,uCAGF,yBACE,+CACE,uBACA,qBACA,0C","sources":["webpack://@lizardbyte/shared-web/./src/css/crowdin-dockle.scss"],"sourcesContent":["#crowdin-language-picker.dockle-crowdin-picker {\n bottom: 1rem !important;\n left: var(--dockle-sidebar-padding, 1.25rem) !important;\n position: fixed !important;\n width: calc(var(--dockle-sidebar-width, 18rem) - (2 * var(--dockle-sidebar-padding, 1.25rem))) !important;\n z-index: 200;\n}\n\n#crowdin-language-picker.dockle-crowdin-picker .cr-picker-button {\n box-sizing: border-box;\n width: 100%;\n}\n\n#crowdin-language-picker .cr-picker-button,\n#crowdin-language-picker .cr-picker-submenu {\n background-color: var(--dockle-background-secondary) !important;\n border: 1px solid var(--dockle-border) !important;\n border-radius: var(--dockle-radius) !important;\n color: var(--dockle-content) !important;\n}\n\n#crowdin-language-picker .cr-picker-button:hover,\n#crowdin-language-picker .cr-picker-submenu > a:hover {\n background-color: var(--dockle-primary) !important;\n color: white !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a {\n color: var(--dockle-content) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a.cr-selected {\n color: var(--dockle-primary) !important;\n}\n\n@media (max-width: 50rem) {\n #crowdin-language-picker.dockle-crowdin-picker {\n bottom: 1rem !important;\n left: 1rem !important;\n width: min(18rem, calc(100vw - 2rem)) !important;\n }\n}\n"],"names":[],"sourceRoot":""}
package/dist/crowdin.js CHANGED
@@ -1,2 +1,2 @@
1
- (()=>{var e={714(e,t,n){const i=n(836),o=["a","abbr","b","cite","code","del","em","i","ins","kbd","mark","q","s","samp","small","span","strong","sub","sup","time","u","var"].join(",");function s(e){const t=new globalThis.MutationObserver(n);function n(){t.disconnect(),function(e){e.forEach(e=>{const t=function(e){if(e.node.isConnected)return e.node;const t=e.previousInline?.nextSibling;if(t instanceof globalThis.Text&&t.isConnected)return e.node=t,e.node;const n=e.nextInline?.previousSibling;if(n instanceof globalThis.Text&&n.isConnected)return e.node=n,e.node;if(!e.whitespaceOnly)return null;const i=document.createTextNode("");if(e.previousInline?.isConnected)e.previousInline.after(i);else{if(!e.nextInline?.isConnected)return null;e.nextInline.before(i)}return e.node=i,e.node}(e);null!==t&&(e.leading&&!/^\s/.test(t.data)&&(t.data=" "+t.data),e.trailing&&!/\s$/.test(t.data)&&(t.data+=" "))})}(e),t.observe(document.body,{characterData:!0,childList:!0,subtree:!0})}return n}function l(e,t){t>=100||globalThis.setTimeout(function(){r(e,t+1)},50)}function r(e,t=0){const n=document.getElementById("crowdin-language-picker");if("sphinx"===e){const i=document.getElementsByClassName("cr-picker-button")[0],o=document.getElementsByClassName("sidebar-sticky")[0];return null===n||void 0===i||void 0===o?void l(e,t):(n.classList.remove("cr-position-bottom-left"),n.style.width=i.offsetWidth+10+"px",n.style.position="relative",n.style.left="10px",n.style.bottom="10px",void o.appendChild(n))}const i=document.querySelector(".sidebar .sidebar-elems")||document.querySelector(".sidebar");null!==n&&null!==i?(n.classList.remove("cr-position-bottom-left"),n.classList.add("rustdoc-crowdin-picker"),n.style.position="static",n.style.left="auto",n.style.bottom="auto",i.appendChild(n)):l(e,t)}function a(e="LizardByte",t=null){["LizardByte","LizardByte-docs"].includes(e)?["sphinx","rustdoc",null].includes(t)?(function(){if("function"!=typeof globalThis.fetch)return;if(globalThis._crowdinMirrorInstalled)return;globalThis._crowdinMirrorInstalled=!0;const e=globalThis.fetch.bind(globalThis);globalThis.fetch=function(t,n){if("string"==typeof t){let i;try{i=new URL(t)}catch{}if("https:"===i?.protocol&&"distributions.crowdin.net"===i.hostname){const t="https://cdn.jsdelivr.net/gh/LizardByte/i18n@dist"+i.pathname+i.search+i.hash;return e(t,n)}}return e(t,n)}}(),i("https://website-translator.app.crowdin.net/assets/proxy-translator.js",function(){let n={bg:"Български (Bulgarian)",cs:"Čeština (Czech)",de:"Deutsch (German)",en:"English","en-GB":"English, United Kingdom","en-US":"English, United States","es-ES":"Español (Spanish)",fr:"Français (French)",hu:"Magyar (Hungarian)",it:"Italiano (Italian)",ja:"日本語 (Japanese)",ko:"한국어 (Korean)",pl:"Polski (Polish)","pt-BR":"Português, Brasileiro (Portuguese, Brazilian)","pt-PT":"Português (Portuguese)",ru:"Русский (Russian)","sv-SE":"svenska (Swedish)",tr:"Türkçe (Turkish)",uk:"Українська (Ukranian)",vi:"Tiếng Việt (Vietnamese)","zh-CN":"简体中文 (Chinese Simplified)","zh-TW":"繁體中文 (Chinese Traditional)"};n=Object.fromEntries(Object.entries(n).sort((e,t)=>e[1].localeCompare(t[1])));let i=globalThis.location.origin;const l=function(){const e=[],t=document.createTreeWalker(document.body,globalThis.NodeFilter.SHOW_TEXT);let n=t.nextNode();for(;null!==n;){const i=n.previousSibling instanceof globalThis.Element&&n.previousSibling.matches(o),s=n.nextSibling instanceof globalThis.Element&&n.nextSibling.matches(o),l=i&&/^\s/.test(n.data),r=s&&/\s$/.test(n.data);(l||r)&&e.push({node:n,leading:l,trailing:r,previousInline:i?n.previousSibling:null,nextInline:s?n.nextSibling:null,whitespaceOnly:/^\s*$/.test(n.data)}),n=t.nextNode()}return e}();globalThis.proxyTranslator.init({baseUrl:i,callback:s(l),distribution:{LizardByte:{baseUrl:"https://app.lizardbyte.dev",distribution:"458f881791aebba1d4dde491bw4"},"LizardByte-docs":{baseUrl:"https://docs.lizardbyte.dev",distribution:"d6c830ba4b41106fefe5d391bw4"}}[e].distribution,defaultLanguage:"en",languageTitles:n,showDefaultLanguageInUrl:!1,languageRoutingMethod:"query",position:"bottom-left",submenuPosition:"top-left",poweredBy:!1}),null!==t&&r(t)})):console.error('Invalid UI. Must be "sphinx", "rustdoc", or null'):console.error('Invalid project. Must be "LizardByte" or "LizardByte-docs"')}void 0!==globalThis.window&&(globalThis.initCrowdIn=a),e.exports=a},836(e){function t(e,t){const n=document.createElement("script");n.src=e,n.async=!0,n.onload=()=>{t&&t(null,n)},n.onerror=()=>{t&&t(new Error(`Failed to load script: ${e}`))},document.head.appendChild(n)}void 0!==globalThis.window&&(globalThis.loadScript=t),e.exports=t}};const t={};!function n(i){const o=t[i];if(void 0!==o)return o.exports;const s=t[i]={exports:{}};return e[i](s,s.exports,n),s.exports}(714)})();
1
+ (()=>{var e={714(e,n,t){const i=t(836),o=["a","abbr","b","cite","code","del","em","i","ins","kbd","mark","q","s","samp","small","span","strong","sub","sup","time","u","var"].join(",");function s(e){return e instanceof globalThis.Element&&e.matches(o)?e:null}function r(e){const n=e.trimStart();return e.slice(0,e.length-n.length)}function a(e){const n=e.trimEnd();return e.slice(n.length)}function l(e){const n=new globalThis.MutationObserver(t);function t(){n.disconnect(),function(e){e.forEach(e=>{const n=function(e){if(e.node.isConnected)return e.node;const n=e.previousInline?.nextSibling;if(n instanceof globalThis.Text&&n.isConnected)return e.node=n,e.node;const t=e.nextInline?.previousSibling;if(t instanceof globalThis.Text&&t.isConnected)return e.node=t,e.node;if(!e.whitespaceOnly)return null;const i=document.createTextNode("");if(e.previousInline?.isConnected)e.previousInline.after(i);else{if(!e.nextInline?.isConnected)return null;e.nextInline.before(i)}return e.node=i,e.node}(e);if(null===n)return;const t=r(n.data);e.leading&&t!==e.leadingWhitespace&&(n.data=e.leadingWhitespace+n.data.slice(t.length));const i=a(n.data);if(e.trailing&&i!==e.trailingWhitespace){const t=n.data.length-i.length;n.data=n.data.slice(0,t)+e.trailingWhitespace}})}(e),n.observe(document.body,{characterData:!0,childList:!0,subtree:!0})}return t}function c(e,n=0){if("jekyll"===e)return;const t=document.getElementById("crowdin-language-picker");null!==t?(t.classList.remove("cr-position-bottom-left"),t.classList.add("dockle-crowdin-picker"),t.style.position="fixed",t.style.left="auto",t.style.bottom="auto",document.body.appendChild(t)):function(e,n){n>=100||globalThis.setTimeout(function(){c(e,n+1)},50)}(e,n)}function d(e="LizardByte",n="jekyll"){["LizardByte","LizardByte-docs"].includes(e)?["dockle","jekyll"].includes(n)?(function(){if("function"!=typeof globalThis.fetch)return;if(globalThis._crowdinMirrorInstalled)return;globalThis._crowdinMirrorInstalled=!0;const e=globalThis.fetch.bind(globalThis);globalThis.fetch=function(n,t){if("string"==typeof n){let i;try{i=new URL(n)}catch{}if("https:"===i?.protocol&&"distributions.crowdin.net"===i.hostname){const n="https://cdn.jsdelivr.net/gh/LizardByte/i18n@dist"+i.pathname+i.search+i.hash;return e(n,t)}}return e(n,t)}}(),i("https://website-translator.app.crowdin.net/assets/proxy-translator.js",function(){let t={bg:"Български (Bulgarian)",cs:"Čeština (Czech)",de:"Deutsch (German)",en:"English","en-GB":"English, United Kingdom","en-US":"English, United States","es-ES":"Español (Spanish)",fr:"Français (French)",hu:"Magyar (Hungarian)",it:"Italiano (Italian)",ja:"日本語 (Japanese)",ko:"한국어 (Korean)",pl:"Polski (Polish)","pt-BR":"Português, Brasileiro (Portuguese, Brazilian)","pt-PT":"Português (Portuguese)",ru:"Русский (Russian)","sv-SE":"svenska (Swedish)",tr:"Türkçe (Turkish)",uk:"Українська (Ukranian)",vi:"Tiếng Việt (Vietnamese)","zh-CN":"简体中文 (Chinese Simplified)","zh-TW":"繁體中文 (Chinese Traditional)"};t=Object.fromEntries(Object.entries(t).sort((e,n)=>e[1].localeCompare(n[1])));let i=globalThis.location.origin;const o=function(){const e=[],n=document.createTreeWalker(document.body,globalThis.NodeFilter.SHOW_TEXT);let t=n.nextNode();for(;null!==t;){const i=s(t.previousSibling),o=s(t.nextSibling),l=null===i?"":r(t.data),c=null===o?"":a(t.data),d=""!==l,u=""!==c;(d||u)&&e.push({node:t,leading:d,leadingWhitespace:l,trailing:u,trailingWhitespace:c,previousInline:i,nextInline:o,whitespaceOnly:""===t.data.trim()}),t=n.nextNode()}return e}();globalThis.proxyTranslator.init({baseUrl:i,callback:l(o),distribution:{LizardByte:{baseUrl:"https://app.lizardbyte.dev",distribution:"458f881791aebba1d4dde491bw4"},"LizardByte-docs":{baseUrl:"https://docs.lizardbyte.dev",distribution:"d6c830ba4b41106fefe5d391bw4"}}[e].distribution,defaultLanguage:"en",languageTitles:t,showDefaultLanguageInUrl:!1,languageRoutingMethod:"query",position:"bottom-left",submenuPosition:"top-left",poweredBy:!1}),c(n)})):console.error('Invalid UI. Must be "dockle" or "jekyll"'):console.error('Invalid project. Must be "LizardByte" or "LizardByte-docs"')}void 0!==globalThis.window&&(globalThis.initCrowdIn=d),e.exports=d},836(e){function n(e,n){const t=document.createElement("script");t.src=e,t.async=!0,t.onload=()=>{n&&n(null,t)},t.onerror=()=>{n&&n(new Error(`Failed to load script: ${e}`))},document.head.appendChild(t)}void 0!==globalThis.window&&(globalThis.loadScript=n),e.exports=n}};const n={};!function t(i){const o=n[i];if(void 0!==o)return o.exports;const s=n[i]={exports:{}};return e[i](s,s.exports,t),s.exports}(714)})();
2
2
  //# sourceMappingURL=crowdin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crowdin.js","mappings":"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","sources":["webpack://@lizardbyte/shared-web/./src/js/crowdin.js","webpack://@lizardbyte/shared-web/./src/js/load-script.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["const loadScript = require('./load-script');\n\n/**\n * jsDelivr CDN URL serving Crowdin distribution files from the crowdin-dist\n * git branch. jsDelivr unconditionally sets Access-Control-Allow-Origin: *,\n * so cross-origin fetch() calls succeed without any browser plugin.\n * The branch is refreshed daily by the \"Sync Crowdin Distribution\" workflow.\n * Structure mirrors https://distributions.crowdin.net/<hash>/… exactly.\n * @type {string}\n */\nconst CROWDIN_DIST_MIRROR = 'https://cdn.jsdelivr.net/gh/LizardByte/i18n@dist';\nconst CROWDIN_PLATFORM_STYLING_MAX_ATTEMPTS = 100;\nconst CROWDIN_PLATFORM_STYLING_RETRY_DELAY_MS = 50;\nconst CROWDIN_INLINE_ELEMENT_SELECTOR = [\n 'a',\n 'abbr',\n 'b',\n 'cite',\n 'code',\n 'del',\n 'em',\n 'i',\n 'ins',\n 'kbd',\n 'mark',\n 'q',\n 's',\n 'samp',\n 'small',\n 'span',\n 'strong',\n 'sub',\n 'sup',\n 'time',\n 'u',\n 'var',\n].join(',');\n\n/**\n * Records whitespace that separates text from inline elements before Crowdin translates the page.\n * @returns {Array<{\n * node: Text,\n * leading: boolean,\n * trailing: boolean,\n * previousInline: Element|null,\n * nextInline: Element|null,\n * whitespaceOnly: boolean\n * }>} Recorded text-node boundaries.\n */\nfunction _captureCrowdinWhitespaceBoundaries() {\n const boundaries = [];\n const walker = document.createTreeWalker(document.body, globalThis.NodeFilter.SHOW_TEXT);\n let node = walker.nextNode();\n\n while (node !== null) {\n const previousIsInline = node.previousSibling instanceof globalThis.Element &&\n node.previousSibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR);\n const nextIsInline = node.nextSibling instanceof globalThis.Element &&\n node.nextSibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR);\n const leading = previousIsInline && /^\\s/.test(node.data);\n const trailing = nextIsInline && /\\s$/.test(node.data);\n\n if (leading || trailing) {\n boundaries.push({\n node,\n leading,\n trailing,\n previousInline: previousIsInline ? node.previousSibling : null,\n nextInline: nextIsInline ? node.nextSibling : null,\n whitespaceOnly: /^\\s*$/.test(node.data),\n });\n }\n\n node = walker.nextNode();\n }\n\n return boundaries;\n}\n\n/**\n * Finds the current text node for a boundary after Crowdin changes the DOM.\n * @param {Object} boundary Recorded whitespace boundary.\n * @returns {Text|null} Current or recreated text node.\n */\nfunction _resolveCrowdinWhitespaceNode(boundary) {\n if (boundary.node.isConnected) return boundary.node;\n\n const previousReplacement = boundary.previousInline?.nextSibling;\n if (previousReplacement instanceof globalThis.Text && previousReplacement.isConnected) {\n boundary.node = previousReplacement;\n return boundary.node;\n }\n const nextReplacement = boundary.nextInline?.previousSibling;\n if (nextReplacement instanceof globalThis.Text && nextReplacement.isConnected) {\n boundary.node = nextReplacement;\n return boundary.node;\n }\n if (!boundary.whitespaceOnly) return null;\n\n const replacement = document.createTextNode('');\n if (boundary.previousInline?.isConnected) {\n boundary.previousInline.after(replacement);\n } else if (boundary.nextInline?.isConnected) {\n boundary.nextInline.before(replacement);\n } else {\n return null;\n }\n\n boundary.node = replacement;\n return boundary.node;\n}\n\n/**\n * Restores whitespace that Crowdin removed from translated text-node boundaries.\n * @param {Array<Object>} boundaries Recorded text-node boundaries.\n */\nfunction _restoreCrowdinWhitespaceBoundaries(boundaries) {\n boundaries.forEach((boundary) => {\n const node = _resolveCrowdinWhitespaceNode(boundary);\n if (node === null) return;\n\n if (boundary.leading && !/^\\s/.test(node.data)) {\n node.data = ' ' + node.data;\n }\n if (boundary.trailing && !/\\s$/.test(node.data)) {\n node.data += ' ';\n }\n });\n}\n\n/**\n * Creates the Website Translator callback and observes later translation mutations.\n * @param {Array<Object>} boundaries Recorded text-node boundaries.\n * @returns {Function} Website Translator callback.\n */\nfunction _createCrowdinTranslationCallback(boundaries) {\n const translationObserver = new globalThis.MutationObserver(restoreAndObserve);\n\n function restoreAndObserve() {\n translationObserver.disconnect();\n _restoreCrowdinWhitespaceBoundaries(boundaries);\n translationObserver.observe(document.body, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n }\n\n return restoreAndObserve;\n}\n\n/**\n * Monkey-patches globalThis.fetch to redirect Crowdin distribution requests to\n * the self-hosted GitHub Pages mirror.\n *\n * Must be called BEFORE proxy-translator.js is loaded so that every fetch()\n * the script makes is already intercepted.\n *\n * Idempotent – installs the interceptor at most once per page.\n */\nfunction _installCrowdinFetchInterceptor() {\n if (typeof globalThis.fetch !== 'function') return;\n if (globalThis._crowdinMirrorInstalled) return;\n globalThis._crowdinMirrorInstalled = true;\n\n const _origFetch = globalThis.fetch.bind(globalThis);\n\n globalThis.fetch = function crowdinMirrorFetch(url, options) {\n if (typeof url === 'string') {\n let parsed;\n try {\n parsed = new URL(url);\n } catch {\n // Not a valid absolute URL – pass through unchanged.\n }\n // Use exact hostname comparison to avoid prefix-match bypasses\n // (e.g. distributions.crowdin.net.evil.com) that would be flagged\n // by incomplete URL sanitisation checks.\n if (parsed?.protocol === 'https:' && parsed.hostname === 'distributions.crowdin.net') {\n const mirroredUrl = CROWDIN_DIST_MIRROR + parsed.pathname + parsed.search + parsed.hash;\n return _origFetch(mirroredUrl, options);\n }\n }\n return _origFetch(url, options);\n };\n}\n\n/**\n * Re-attempts platform styling while Crowdin inserts the language picker.\n * @param {string} platform - UI platform ('sphinx' or 'rustdoc').\n * @param {number} attempt - Current retry count.\n */\nfunction _retryCrowdinPlatformStyling(platform, attempt) {\n if (attempt >= CROWDIN_PLATFORM_STYLING_MAX_ATTEMPTS) {\n return;\n }\n\n globalThis.setTimeout(function() {\n _applyCrowdinPlatformStyling(platform, attempt + 1);\n }, CROWDIN_PLATFORM_STYLING_RETRY_DELAY_MS);\n}\n\n/**\n * Applies platform-specific placement after the Crowdin picker exists.\n * @param {string} platform - UI platform ('sphinx' or 'rustdoc').\n * @param {number} attempt - Current retry count.\n */\nfunction _applyCrowdinPlatformStyling(platform, attempt = 0) {\n const container = document.getElementById('crowdin-language-picker');\n\n if (platform === 'sphinx') {\n const button = document.getElementsByClassName('cr-picker-button')[0];\n const sidebar = document.getElementsByClassName('sidebar-sticky')[0];\n\n if (container === null || button === undefined || sidebar === undefined) {\n _retryCrowdinPlatformStyling(platform, attempt);\n return;\n }\n\n container.classList.remove('cr-position-bottom-left');\n container.style.width = button.offsetWidth + 10 + 'px';\n container.style.position = 'relative';\n container.style.left = '10px';\n container.style.bottom = '10px';\n\n // move button to related pages\n sidebar.appendChild(container);\n return;\n }\n\n const sidebar = document.querySelector('.sidebar .sidebar-elems') || document.querySelector('.sidebar');\n\n if (container === null || sidebar === null) {\n _retryCrowdinPlatformStyling(platform, attempt);\n return;\n }\n\n container.classList.remove('cr-position-bottom-left');\n container.classList.add('rustdoc-crowdin-picker');\n container.style.position = 'static';\n container.style.left = 'auto';\n container.style.bottom = 'auto';\n\n sidebar.appendChild(container);\n}\n\n/**\n * Initializes Crowdin translation widget based on project and UI platform.\n * @param {string} project - Project name ('LizardByte' or 'LizardByte-docs').\n * @param {string|null} platform - UI platform ('sphinx', 'rustdoc', or null).\n */\nfunction initCrowdIn(project = 'LizardByte', platform = null) {\n // Input validation\n if (!['LizardByte', 'LizardByte-docs'].includes(project)) {\n console.error('Invalid project. Must be \"LizardByte\" or \"LizardByte-docs\"');\n return;\n }\n if (!['sphinx', 'rustdoc', null].includes(platform)) {\n console.error('Invalid UI. Must be \"sphinx\", \"rustdoc\", or null');\n return;\n }\n\n // Redirect distribution CDN requests to our self-hosted GitHub Pages mirror\n // before the script is even loaded so every fetch() it makes is intercepted.\n _installCrowdinFetchInterceptor();\n\n loadScript('https://website-translator.app.crowdin.net/assets/proxy-translator.js', function() {\n // Configure base settings based on project\n const projectSettings = {\n 'LizardByte': {\n baseUrl: \"https://app.lizardbyte.dev\",\n distribution: \"458f881791aebba1d4dde491bw4\",\n },\n 'LizardByte-docs': {\n baseUrl: \"https://docs.lizardbyte.dev\",\n distribution: \"d6c830ba4b41106fefe5d391bw4\",\n }\n };\n\n let languageTitles = {\n \"bg\": \"Български (Bulgarian)\",\n \"cs\": \"Čeština (Czech)\",\n \"de\": \"Deutsch (German)\",\n \"en\": \"English\",\n \"en-GB\": \"English, United Kingdom\",\n \"en-US\": \"English, United States\",\n \"es-ES\": \"Español (Spanish)\",\n \"fr\": \"Français (French)\",\n \"hu\": \"Magyar (Hungarian)\",\n \"it\": \"Italiano (Italian)\",\n \"ja\": \"日本語 (Japanese)\",\n \"ko\": \"한국어 (Korean)\",\n \"pl\": \"Polski (Polish)\",\n \"pt-BR\": \"Português, Brasileiro (Portuguese, Brazilian)\",\n \"pt-PT\": \"Português (Portuguese)\",\n \"ru\": \"Русский (Russian)\",\n \"sv-SE\": \"svenska (Swedish)\",\n \"tr\": \"Türkçe (Turkish)\",\n \"uk\": \"Українська (Ukranian)\",\n \"vi\": \"Tiếng Việt (Vietnamese)\",\n \"zh-CN\": \"简体中文 (Chinese Simplified)\",\n \"zh-TW\": \"繁體中文 (Chinese Traditional)\",\n };\n // sort languages by name\n languageTitles = Object.fromEntries(Object.entries(languageTitles).sort((a, b) => a[1].localeCompare(b[1])));\n\n // use this to allow translations to work on PR preview builds\n let currentBaseUrl = globalThis.location.origin;\n\n // Initialize Crowdin translator\n const whitespaceBoundaries = _captureCrowdinWhitespaceBoundaries();\n\n globalThis.proxyTranslator.init({\n baseUrl: currentBaseUrl,\n callback: _createCrowdinTranslationCallback(whitespaceBoundaries),\n distribution: projectSettings[project].distribution,\n defaultLanguage: \"en\",\n languageTitles: languageTitles,\n showDefaultLanguageInUrl: false,\n languageRoutingMethod: \"query\",\n position: \"bottom-left\",\n submenuPosition: \"top-left\",\n poweredBy: false,\n });\n\n // Apply styling based on UI framework\n if (platform === null) {\n return;\n }\n\n _applyCrowdinPlatformStyling(platform);\n });\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.initCrowdIn = initCrowdIn;\n}\n\nmodule.exports = initCrowdIn;\n","/**\n * Load a script asynchronously, and add it to the DOM. Optionally, call a callback when the script has loaded.\n * @param {string} url The URL of the script to load.\n * @param {Function} callback An optional callback to call when the script has loaded.\n */\nfunction loadScript(url, callback) {\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n if (callback) callback(null, script);\n };\n\n script.onerror = () => {\n if (callback) callback(new Error(`Failed to load script: ${url}`));\n };\n\n document.head.appendChild(script);\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.loadScript = loadScript;\n}\n\nmodule.exports = loadScript;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(714);\n"],"names":["loadScript","__webpack_require__","CROWDIN_INLINE_ELEMENT_SELECTOR","join","_createCrowdinTranslationCallback","boundaries","translationObserver","globalThis","MutationObserver","restoreAndObserve","disconnect","forEach","boundary","node","isConnected","previousReplacement","previousInline","nextSibling","Text","nextReplacement","nextInline","previousSibling","whitespaceOnly","replacement","document","createTextNode","after","before","_resolveCrowdinWhitespaceNode","leading","test","data","trailing","_restoreCrowdinWhitespaceBoundaries","observe","body","characterData","childList","subtree","_retryCrowdinPlatformStyling","platform","attempt","setTimeout","_applyCrowdinPlatformStyling","container","getElementById","button","getElementsByClassName","sidebar","undefined","classList","remove","style","width","offsetWidth","position","left","bottom","appendChild","querySelector","add","initCrowdIn","project","includes","fetch","_crowdinMirrorInstalled","_origFetch","bind","url","options","parsed","URL","protocol","hostname","mirroredUrl","pathname","search","hash","_installCrowdinFetchInterceptor","languageTitles","bg","cs","de","en","fr","hu","it","ja","ko","pl","ru","tr","uk","vi","Object","fromEntries","entries","sort","a","b","localeCompare","currentBaseUrl","location","origin","whitespaceBoundaries","walker","createTreeWalker","NodeFilter","SHOW_TEXT","nextNode","previousIsInline","Element","matches","nextIsInline","push","_captureCrowdinWhitespaceBoundaries","proxyTranslator","init","baseUrl","callback","distribution","LizardByte","defaultLanguage","showDefaultLanguageInUrl","languageRoutingMethod","submenuPosition","poweredBy","console","error","window","module","exports","script","createElement","src","async","onload","onerror","Error","head","__webpack_module_cache__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"crowdin.js","mappings":"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","sources":["webpack://@lizardbyte/shared-web/./src/js/crowdin.js","webpack://@lizardbyte/shared-web/./src/js/load-script.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["const loadScript = require('./load-script');\n\n/**\n * jsDelivr CDN URL serving Crowdin distribution files from the crowdin-dist\n * git branch. jsDelivr unconditionally sets Access-Control-Allow-Origin: *,\n * so cross-origin fetch() calls succeed without any browser plugin.\n * The branch is refreshed daily by the \"Sync Crowdin Distribution\" workflow.\n * Structure mirrors https://distributions.crowdin.net/<hash>/… exactly.\n * @type {string}\n */\nconst CROWDIN_DIST_MIRROR = 'https://cdn.jsdelivr.net/gh/LizardByte/i18n@dist';\nconst CROWDIN_PLATFORM_STYLING_MAX_ATTEMPTS = 100;\nconst CROWDIN_PLATFORM_STYLING_RETRY_DELAY_MS = 50;\nconst CROWDIN_INLINE_ELEMENT_SELECTOR = [\n 'a',\n 'abbr',\n 'b',\n 'cite',\n 'code',\n 'del',\n 'em',\n 'i',\n 'ins',\n 'kbd',\n 'mark',\n 'q',\n 's',\n 'samp',\n 'small',\n 'span',\n 'strong',\n 'sub',\n 'sup',\n 'time',\n 'u',\n 'var',\n].join(',');\n\n/**\n * Returns a sibling when it is an inline element whose boundary Crowdin may rewrite.\n * @param {Node|null} sibling Candidate sibling.\n * @returns {Element|null} Matching inline element.\n */\nfunction _getCrowdinInlineSibling(sibling) {\n if (!(sibling instanceof globalThis.Element)) return null;\n return sibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR) ? sibling : null;\n}\n\n/**\n * Returns the exact whitespace at the start of a string.\n * @param {string} text Text to inspect.\n * @returns {string} Leading whitespace.\n */\nfunction _getLeadingWhitespace(text) {\n const trimmedText = text.trimStart();\n return text.slice(0, text.length - trimmedText.length);\n}\n\n/**\n * Returns the exact whitespace at the end of a string.\n * @param {string} text Text to inspect.\n * @returns {string} Trailing whitespace.\n */\nfunction _getTrailingWhitespace(text) {\n const trimmedText = text.trimEnd();\n return text.slice(trimmedText.length);\n}\n\n/**\n * Records whitespace that separates text from inline elements before Crowdin translates the page.\n * @returns {Array<{\n * node: Text,\n * leading: boolean,\n * leadingWhitespace: string,\n * trailing: boolean,\n * trailingWhitespace: string,\n * previousInline: Element|null,\n * nextInline: Element|null,\n * whitespaceOnly: boolean\n * }>} Recorded text-node boundaries.\n */\nfunction _captureCrowdinWhitespaceBoundaries() {\n const boundaries = [];\n const walker = document.createTreeWalker(document.body, globalThis.NodeFilter.SHOW_TEXT);\n let node = walker.nextNode();\n\n while (node !== null) {\n const previousInline = _getCrowdinInlineSibling(node.previousSibling);\n const nextInline = _getCrowdinInlineSibling(node.nextSibling);\n const leadingWhitespace = previousInline === null ? '' : _getLeadingWhitespace(node.data);\n const trailingWhitespace = nextInline === null ? '' : _getTrailingWhitespace(node.data);\n const leading = leadingWhitespace !== '';\n const trailing = trailingWhitespace !== '';\n\n if (leading || trailing) {\n boundaries.push({\n node,\n leading,\n leadingWhitespace,\n trailing,\n trailingWhitespace,\n previousInline,\n nextInline,\n whitespaceOnly: node.data.trim() === '',\n });\n }\n\n node = walker.nextNode();\n }\n\n return boundaries;\n}\n\n/**\n * Finds the current text node for a boundary after Crowdin changes the DOM.\n * @param {Object} boundary Recorded whitespace boundary.\n * @returns {Text|null} Current or recreated text node.\n */\nfunction _resolveCrowdinWhitespaceNode(boundary) {\n if (boundary.node.isConnected) return boundary.node;\n\n const previousReplacement = boundary.previousInline?.nextSibling;\n if (previousReplacement instanceof globalThis.Text && previousReplacement.isConnected) {\n boundary.node = previousReplacement;\n return boundary.node;\n }\n const nextReplacement = boundary.nextInline?.previousSibling;\n if (nextReplacement instanceof globalThis.Text && nextReplacement.isConnected) {\n boundary.node = nextReplacement;\n return boundary.node;\n }\n if (!boundary.whitespaceOnly) return null;\n\n const replacement = document.createTextNode('');\n if (boundary.previousInline?.isConnected) {\n boundary.previousInline.after(replacement);\n } else if (boundary.nextInline?.isConnected) {\n boundary.nextInline.before(replacement);\n } else {\n return null;\n }\n\n boundary.node = replacement;\n return boundary.node;\n}\n\n/**\n * Restores whitespace that Crowdin removed from translated text-node boundaries.\n * @param {Array<Object>} boundaries Recorded text-node boundaries.\n */\nfunction _restoreCrowdinWhitespaceBoundaries(boundaries) {\n boundaries.forEach((boundary) => {\n const node = _resolveCrowdinWhitespaceNode(boundary);\n if (node === null) return;\n\n const currentLeadingWhitespace = _getLeadingWhitespace(node.data);\n if (boundary.leading && currentLeadingWhitespace !== boundary.leadingWhitespace) {\n node.data = boundary.leadingWhitespace + node.data.slice(currentLeadingWhitespace.length);\n }\n const currentTrailingWhitespace = _getTrailingWhitespace(node.data);\n if (boundary.trailing && currentTrailingWhitespace !== boundary.trailingWhitespace) {\n const translatedTextEnd = node.data.length - currentTrailingWhitespace.length;\n node.data = node.data.slice(0, translatedTextEnd) + boundary.trailingWhitespace;\n }\n });\n}\n\n/**\n * Creates the Website Translator callback and observes later translation mutations.\n * @param {Array<Object>} boundaries Recorded text-node boundaries.\n * @returns {Function} Website Translator callback.\n */\nfunction _createCrowdinTranslationCallback(boundaries) {\n const translationObserver = new globalThis.MutationObserver(restoreAndObserve);\n\n function restoreAndObserve() {\n translationObserver.disconnect();\n _restoreCrowdinWhitespaceBoundaries(boundaries);\n translationObserver.observe(document.body, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n }\n\n return restoreAndObserve;\n}\n\n/**\n * Monkey-patches globalThis.fetch to redirect Crowdin distribution requests to\n * the self-hosted GitHub Pages mirror.\n *\n * Must be called BEFORE proxy-translator.js is loaded so that every fetch()\n * the script makes is already intercepted.\n *\n * Idempotent – installs the interceptor at most once per page.\n */\nfunction _installCrowdinFetchInterceptor() {\n if (typeof globalThis.fetch !== 'function') return;\n if (globalThis._crowdinMirrorInstalled) return;\n globalThis._crowdinMirrorInstalled = true;\n\n const _origFetch = globalThis.fetch.bind(globalThis);\n\n globalThis.fetch = function crowdinMirrorFetch(url, options) {\n if (typeof url === 'string') {\n let parsed;\n try {\n parsed = new URL(url);\n } catch {\n // Not a valid absolute URL – pass through unchanged.\n }\n // Use exact hostname comparison to avoid prefix-match bypasses\n // (e.g. distributions.crowdin.net.evil.com) that would be flagged\n // by incomplete URL sanitisation checks.\n if (parsed?.protocol === 'https:' && parsed.hostname === 'distributions.crowdin.net') {\n const mirroredUrl = CROWDIN_DIST_MIRROR + parsed.pathname + parsed.search + parsed.hash;\n return _origFetch(mirroredUrl, options);\n }\n }\n return _origFetch(url, options);\n };\n}\n\n/**\n * Re-attempts platform styling while Crowdin inserts the language picker.\n * @param {string} platform - UI platform ('dockle' or 'jekyll').\n * @param {number} attempt - Current retry count.\n */\nfunction _retryCrowdinPlatformStyling(platform, attempt) {\n if (attempt >= CROWDIN_PLATFORM_STYLING_MAX_ATTEMPTS) {\n return;\n }\n\n globalThis.setTimeout(function() {\n _applyCrowdinPlatformStyling(platform, attempt + 1);\n }, CROWDIN_PLATFORM_STYLING_RETRY_DELAY_MS);\n}\n\n/**\n * Applies platform-specific placement after the Crowdin picker exists.\n * @param {string} platform - UI platform ('dockle' or 'jekyll').\n * @param {number} attempt - Current retry count.\n */\nfunction _applyCrowdinPlatformStyling(platform, attempt = 0) {\n if (platform === 'jekyll') {\n return;\n }\n\n const container = document.getElementById('crowdin-language-picker');\n\n if (container === null) {\n _retryCrowdinPlatformStyling(platform, attempt);\n return;\n }\n\n container.classList.remove('cr-position-bottom-left');\n container.classList.add('dockle-crowdin-picker');\n container.style.position = 'fixed';\n container.style.left = 'auto';\n container.style.bottom = 'auto';\n document.body.appendChild(container);\n}\n\n/**\n * Initializes Crowdin translation widget based on project and UI platform.\n * @param {string} project - Project name ('LizardByte' or 'LizardByte-docs').\n * @param {string} platform - UI platform ('dockle' or 'jekyll').\n */\nfunction initCrowdIn(project = 'LizardByte', platform = 'jekyll') {\n // Input validation\n if (!['LizardByte', 'LizardByte-docs'].includes(project)) {\n console.error('Invalid project. Must be \"LizardByte\" or \"LizardByte-docs\"');\n return;\n }\n if (!['dockle', 'jekyll'].includes(platform)) {\n console.error('Invalid UI. Must be \"dockle\" or \"jekyll\"');\n return;\n }\n\n // Redirect distribution CDN requests to our self-hosted GitHub Pages mirror\n // before the script is even loaded so every fetch() it makes is intercepted.\n _installCrowdinFetchInterceptor();\n\n loadScript('https://website-translator.app.crowdin.net/assets/proxy-translator.js', function() {\n // Configure base settings based on project\n const projectSettings = {\n 'LizardByte': {\n baseUrl: \"https://app.lizardbyte.dev\",\n distribution: \"458f881791aebba1d4dde491bw4\",\n },\n 'LizardByte-docs': {\n baseUrl: \"https://docs.lizardbyte.dev\",\n distribution: \"d6c830ba4b41106fefe5d391bw4\",\n }\n };\n\n let languageTitles = {\n \"bg\": \"Български (Bulgarian)\",\n \"cs\": \"Čeština (Czech)\",\n \"de\": \"Deutsch (German)\",\n \"en\": \"English\",\n \"en-GB\": \"English, United Kingdom\",\n \"en-US\": \"English, United States\",\n \"es-ES\": \"Español (Spanish)\",\n \"fr\": \"Français (French)\",\n \"hu\": \"Magyar (Hungarian)\",\n \"it\": \"Italiano (Italian)\",\n \"ja\": \"日本語 (Japanese)\",\n \"ko\": \"한국어 (Korean)\",\n \"pl\": \"Polski (Polish)\",\n \"pt-BR\": \"Português, Brasileiro (Portuguese, Brazilian)\",\n \"pt-PT\": \"Português (Portuguese)\",\n \"ru\": \"Русский (Russian)\",\n \"sv-SE\": \"svenska (Swedish)\",\n \"tr\": \"Türkçe (Turkish)\",\n \"uk\": \"Українська (Ukranian)\",\n \"vi\": \"Tiếng Việt (Vietnamese)\",\n \"zh-CN\": \"简体中文 (Chinese Simplified)\",\n \"zh-TW\": \"繁體中文 (Chinese Traditional)\",\n };\n // sort languages by name\n languageTitles = Object.fromEntries(Object.entries(languageTitles).sort((a, b) => a[1].localeCompare(b[1])));\n\n // use this to allow translations to work on PR preview builds\n let currentBaseUrl = globalThis.location.origin;\n\n // Initialize Crowdin translator\n const whitespaceBoundaries = _captureCrowdinWhitespaceBoundaries();\n\n globalThis.proxyTranslator.init({\n baseUrl: currentBaseUrl,\n callback: _createCrowdinTranslationCallback(whitespaceBoundaries),\n distribution: projectSettings[project].distribution,\n defaultLanguage: \"en\",\n languageTitles: languageTitles,\n showDefaultLanguageInUrl: false,\n languageRoutingMethod: \"query\",\n position: \"bottom-left\",\n submenuPosition: \"top-left\",\n poweredBy: false,\n });\n\n // Apply styling based on UI framework\n _applyCrowdinPlatformStyling(platform);\n });\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.initCrowdIn = initCrowdIn;\n}\n\nmodule.exports = initCrowdIn;\n","/**\n * Load a script asynchronously, and add it to the DOM. Optionally, call a callback when the script has loaded.\n * @param {string} url The URL of the script to load.\n * @param {Function} callback An optional callback to call when the script has loaded.\n */\nfunction loadScript(url, callback) {\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n if (callback) callback(null, script);\n };\n\n script.onerror = () => {\n if (callback) callback(new Error(`Failed to load script: ${url}`));\n };\n\n document.head.appendChild(script);\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.loadScript = loadScript;\n}\n\nmodule.exports = loadScript;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(714);\n"],"names":["loadScript","__webpack_require__","CROWDIN_INLINE_ELEMENT_SELECTOR","join","_getCrowdinInlineSibling","sibling","globalThis","Element","matches","_getLeadingWhitespace","text","trimmedText","trimStart","slice","length","_getTrailingWhitespace","trimEnd","_createCrowdinTranslationCallback","boundaries","translationObserver","MutationObserver","restoreAndObserve","disconnect","forEach","boundary","node","isConnected","previousReplacement","previousInline","nextSibling","Text","nextReplacement","nextInline","previousSibling","whitespaceOnly","replacement","document","createTextNode","after","before","_resolveCrowdinWhitespaceNode","currentLeadingWhitespace","data","leading","leadingWhitespace","currentTrailingWhitespace","trailing","trailingWhitespace","translatedTextEnd","_restoreCrowdinWhitespaceBoundaries","observe","body","characterData","childList","subtree","_applyCrowdinPlatformStyling","platform","attempt","container","getElementById","classList","remove","add","style","position","left","bottom","appendChild","setTimeout","_retryCrowdinPlatformStyling","initCrowdIn","project","includes","fetch","_crowdinMirrorInstalled","_origFetch","bind","url","options","parsed","URL","protocol","hostname","mirroredUrl","pathname","search","hash","_installCrowdinFetchInterceptor","languageTitles","bg","cs","de","en","fr","hu","it","ja","ko","pl","ru","tr","uk","vi","Object","fromEntries","entries","sort","a","b","localeCompare","currentBaseUrl","location","origin","whitespaceBoundaries","walker","createTreeWalker","NodeFilter","SHOW_TEXT","nextNode","push","trim","_captureCrowdinWhitespaceBoundaries","proxyTranslator","init","baseUrl","callback","distribution","LizardByte","defaultLanguage","showDefaultLanguageInUrl","languageRoutingMethod","submenuPosition","poweredBy","console","error","undefined","window","module","exports","script","createElement","src","async","onload","onerror","Error","head","__webpack_module_cache__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
@@ -1 +1 @@
1
- {"version":3,"file":"format-number.js","mappings":"oBAMA,SAASA,EAAaC,EAAKC,EAAgB,GACvC,OAAID,GAAO,KACCA,EAAM,KAASE,QAAQD,GAAiB,IACzCD,GAAO,KACNA,EAAM,KAAME,QAAQD,GAAiB,IAEtCD,EAAIG,UAEnB,MAG0BC,IAAtBC,WAAWC,SACXD,WAAWN,aAAeA,GAG9BQ,EAAOC,QAAUT,ICpBjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA","sources":["webpack://@lizardbyte/shared-web/./src/js/format-number.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Format a number to a human-readable string.\n * @param {number} num The number to format.\n * @param {number} decimalPlaces The number of decimal places to include in the formatted string.\n * @returns {string} The formatted number as a string with suffix (k, M, etc.).\n */\nfunction formatNumber(num, decimalPlaces = 1) {\n if (num >= 1000000) {\n return (num / 1000000).toFixed(decimalPlaces) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(decimalPlaces) + 'k';\n } else {\n return num.toString()\n }\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.formatNumber = formatNumber;\n}\n\nmodule.exports = formatNumber;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(415);\n"],"names":["formatNumber","num","decimalPlaces","toFixed","toString","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"format-number.js","mappings":"oBAMA,SAASA,EAAaC,EAAKC,EAAgB,GACvC,OAAID,GAAO,KACCA,EAAM,KAASE,QAAQD,GAAiB,IACzCD,GAAO,KACNA,EAAM,KAAME,QAAQD,GAAiB,IAEtCD,EAAIG,UAEnB,MAG0BC,IAAtBC,WAAWC,SACXD,WAAWN,aAAeA,GAG9BQ,EAAOC,QAAUT,C,GCpBjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA,I","sources":["webpack://@lizardbyte/shared-web/./src/js/format-number.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Format a number to a human-readable string.\n * @param {number} num The number to format.\n * @param {number} decimalPlaces The number of decimal places to include in the formatted string.\n * @returns {string} The formatted number as a string with suffix (k, M, etc.).\n */\nfunction formatNumber(num, decimalPlaces = 1) {\n if (num >= 1000000) {\n return (num / 1000000).toFixed(decimalPlaces) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(decimalPlaces) + 'k';\n } else {\n return num.toString()\n }\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.formatNumber = formatNumber;\n}\n\nmodule.exports = formatNumber;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(415);\n"],"names":["formatNumber","num","decimalPlaces","toFixed","toString","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
@@ -1 +1 @@
1
- {"version":3,"file":"levenshtein-distance.js","mappings":"mBAMA,SAASA,EAAoBC,EAAGC,GAC5B,GAAiB,IAAbD,EAAEE,OAAc,OAAOD,EAAEC,OAC7B,GAAiB,IAAbD,EAAEC,OAAc,OAAOF,EAAEE,OAE7B,IAGIC,EAMAC,EATAC,EAAS,GAIb,IAAKF,EAAI,EAAGA,GAAKF,EAAEC,OAAQC,IACvBE,EAAOF,GAAK,CAACA,GAKjB,IAAKC,EAAI,EAAGA,GAAKJ,EAAEE,OAAQE,IACvBC,EAAO,GAAGD,GAAKA,EAInB,IAAKD,EAAI,EAAGA,GAAKF,EAAEC,OAAQC,IACvB,IAAKC,EAAI,EAAGA,GAAKJ,EAAEE,OAAQE,IACnBH,EAAEK,OAAOH,EAAI,KAAOH,EAAEM,OAAOF,EAAI,GACjCC,EAAOF,GAAGC,GAAKC,EAAOF,EAAI,GAAGC,EAAI,GAEjCC,EAAOF,GAAGC,GAAKG,KAAKC,IAAIH,EAAOF,EAAI,GAAGC,EAAI,GAAK,EAC3CG,KAAKC,IAAIH,EAAOF,GAAGC,EAAI,GAAK,EACxBC,EAAOF,EAAI,GAAGC,GAAK,IAMvC,OAA2E,KAAnE,EAAKC,EAAOJ,EAAEC,QAAQF,EAAEE,QAAUK,KAAKE,IAAIT,EAAEE,OAAQD,EAAEC,QACnE,MAG0BQ,IAAtBC,WAAWC,SACXD,WAAWZ,oBAAsBA,GAGrCc,EAAOC,QAAUf,IC7CjB,MAAAgB,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA","sources":["webpack://@lizardbyte/shared-web/./src/js/levenshtein-distance.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Calculate the levenshtein distance between two strings.\n * @param {string} a - The first string to compare.\n * @param {string} b - The second string to compare.\n * @returns {number|*} - The percentage of the levenshtein distance.\n */\nfunction levenshteinDistance(a, b) {\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let matrix = [];\n\n // increment along the first column of each row\n let i;\n for (i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n // increment each column in the first row\n let j;\n for (j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n // Fill in the rest of the matrix\n for (i = 1; i <= b.length; i++) {\n for (j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution\n Math.min(matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j] + 1)); // deletion\n }\n }\n }\n\n // return the percentage of the levenshtein distance\n return (1 - (matrix[b.length][a.length] / Math.max(a.length, b.length))) * 100\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.levenshteinDistance = levenshteinDistance;\n}\n\nmodule.exports = levenshteinDistance;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(83);\n"],"names":["levenshteinDistance","a","b","length","i","j","matrix","charAt","Math","min","max","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"levenshtein-distance.js","mappings":"mBAMA,SAASA,EAAoBC,EAAGC,GAC5B,GAAiB,IAAbD,EAAEE,OAAc,OAAOD,EAAEC,OAC7B,GAAiB,IAAbD,EAAEC,OAAc,OAAOF,EAAEE,OAE7B,IAGIC,EAMAC,EATAC,EAAS,GAIb,IAAKF,EAAI,EAAGA,GAAKF,EAAEC,OAAQC,IACvBE,EAAOF,GAAK,CAACA,GAKjB,IAAKC,EAAI,EAAGA,GAAKJ,EAAEE,OAAQE,IACvBC,EAAO,GAAGD,GAAKA,EAInB,IAAKD,EAAI,EAAGA,GAAKF,EAAEC,OAAQC,IACvB,IAAKC,EAAI,EAAGA,GAAKJ,EAAEE,OAAQE,IACnBH,EAAEK,OAAOH,EAAI,KAAOH,EAAEM,OAAOF,EAAI,GACjCC,EAAOF,GAAGC,GAAKC,EAAOF,EAAI,GAAGC,EAAI,GAEjCC,EAAOF,GAAGC,GAAKG,KAAKC,IAAIH,EAAOF,EAAI,GAAGC,EAAI,GAAK,EAC3CG,KAAKC,IAAIH,EAAOF,GAAGC,EAAI,GAAK,EACxBC,EAAOF,EAAI,GAAGC,GAAK,IAMvC,OAA2E,KAAnE,EAAKC,EAAOJ,EAAEC,QAAQF,EAAEE,QAAUK,KAAKE,IAAIT,EAAEE,OAAQD,EAAEC,QACnE,MAG0BQ,IAAtBC,WAAWC,SACXD,WAAWZ,oBAAsBA,GAGrCc,EAAOC,QAAUf,C,GC7CjB,MAAAgB,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA,G","sources":["webpack://@lizardbyte/shared-web/./src/js/levenshtein-distance.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Calculate the levenshtein distance between two strings.\n * @param {string} a - The first string to compare.\n * @param {string} b - The second string to compare.\n * @returns {number|*} - The percentage of the levenshtein distance.\n */\nfunction levenshteinDistance(a, b) {\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let matrix = [];\n\n // increment along the first column of each row\n let i;\n for (i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n // increment each column in the first row\n let j;\n for (j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n // Fill in the rest of the matrix\n for (i = 1; i <= b.length; i++) {\n for (j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution\n Math.min(matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j] + 1)); // deletion\n }\n }\n }\n\n // return the percentage of the levenshtein distance\n return (1 - (matrix[b.length][a.length] / Math.max(a.length, b.length))) * 100\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.levenshteinDistance = levenshteinDistance;\n}\n\nmodule.exports = levenshteinDistance;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(83);\n"],"names":["levenshteinDistance","a","b","length","i","j","matrix","charAt","Math","min","max","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
@@ -1 +1 @@
1
- {"version":3,"file":"load-script.js","mappings":"oBAKA,SAASA,EAAWC,EAAKC,GACrB,MAAMC,EAASC,SAASC,cAAc,UACtCF,EAAOG,IAAML,EACbE,EAAOI,OAAQ,EAEfJ,EAAOK,OAAS,KACRN,GAAUA,EAAS,KAAMC,IAGjCA,EAAOM,QAAU,KACTP,GAAUA,EAAS,IAAIQ,MAAM,0BAA0BT,OAG/DG,SAASO,KAAKC,YAAYT,EAC9B,MAG0BU,IAAtBC,WAAWC,SACXD,WAAWd,WAAaA,GAG5BgB,EAAOC,QAAUjB,ICzBjB,MAAAkB,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA","sources":["webpack://@lizardbyte/shared-web/./src/js/load-script.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Load a script asynchronously, and add it to the DOM. Optionally, call a callback when the script has loaded.\n * @param {string} url The URL of the script to load.\n * @param {Function} callback An optional callback to call when the script has loaded.\n */\nfunction loadScript(url, callback) {\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n if (callback) callback(null, script);\n };\n\n script.onerror = () => {\n if (callback) callback(new Error(`Failed to load script: ${url}`));\n };\n\n document.head.appendChild(script);\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.loadScript = loadScript;\n}\n\nmodule.exports = loadScript;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(836);\n"],"names":["loadScript","url","callback","script","document","createElement","src","async","onload","onerror","Error","head","appendChild","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"load-script.js","mappings":"oBAKA,SAASA,EAAWC,EAAKC,GACrB,MAAMC,EAASC,SAASC,cAAc,UACtCF,EAAOG,IAAML,EACbE,EAAOI,OAAQ,EAEfJ,EAAOK,OAAS,KACRN,GAAUA,EAAS,KAAMC,IAGjCA,EAAOM,QAAU,KACTP,GAAUA,EAAS,IAAIQ,MAAM,0BAA0BT,OAG/DG,SAASO,KAAKC,YAAYT,EAC9B,MAG0BU,IAAtBC,WAAWC,SACXD,WAAWd,WAAaA,GAG5BgB,EAAOC,QAAUjB,C,GCzBjB,MAAAkB,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA,I","sources":["webpack://@lizardbyte/shared-web/./src/js/load-script.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Load a script asynchronously, and add it to the DOM. Optionally, call a callback when the script has loaded.\n * @param {string} url The URL of the script to load.\n * @param {Function} callback An optional callback to call when the script has loaded.\n */\nfunction loadScript(url, callback) {\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n\n script.onload = () => {\n if (callback) callback(null, script);\n };\n\n script.onerror = () => {\n if (callback) callback(new Error(`Failed to load script: ${url}`));\n };\n\n document.head.appendChild(script);\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.loadScript = loadScript;\n}\n\nmodule.exports = loadScript;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(836);\n"],"names":["loadScript","url","callback","script","document","createElement","src","async","onload","onerror","Error","head","appendChild","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
@@ -1 +1 @@
1
- {"version":3,"file":"ranking-sorter.js","mappings":"mBAMA,SAASA,EAAcC,EAAUC,GAC7B,OAAO,SAASC,EAAGC,GACf,OAAID,EAAEF,GAAYG,EAAEH,IACR,EACDE,EAAEF,GAAYG,EAAEH,IAEhBE,EAAED,GAAaE,EAAEF,GADjB,EAGAC,EAAED,GAAaE,EAAEF,IAChB,EAED,CAEf,CACJ,MAG0BG,IAAtBC,WAAWC,SACXD,WAAWN,cAAgBA,GAG/BQ,EAAOC,QAAUT,IC1BjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA","sources":["webpack://@lizardbyte/shared-web/./src/js/ranking-sorter.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Sorts an array of objects by two keys in descending order.\n * @param {string} firstKey The primary key to sort by.\n * @param {string} secondKey The secondary key to sort by, in case the first key is equal.\n * @returns {Function} The sorting function that returns a number.\n */\nfunction rankingSorter(firstKey, secondKey) {\n return function(a, b) {\n if (a[firstKey] > b[firstKey]) {\n return -1;\n } else if (a[firstKey] < b[firstKey]) {\n return 1;\n } else if (a[secondKey] > b[secondKey]) {\n return 1;\n } else if (a[secondKey] < b[secondKey]) {\n return -1;\n } else {\n return 0;\n }\n }\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.rankingSorter = rankingSorter;\n}\n\nmodule.exports = rankingSorter;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(76);\n"],"names":["rankingSorter","firstKey","secondKey","a","b","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"ranking-sorter.js","mappings":"mBAMA,SAASA,EAAcC,EAAUC,GAC7B,OAAO,SAASC,EAAGC,GACf,OAAID,EAAEF,GAAYG,EAAEH,IACR,EACDE,EAAEF,GAAYG,EAAEH,IAEhBE,EAAED,GAAaE,EAAEF,GADjB,EAGAC,EAAED,GAAaE,EAAEF,IAChB,EAED,CAEf,CACJ,MAG0BG,IAAtBC,WAAWC,SACXD,WAAWN,cAAgBA,GAG/BQ,EAAOC,QAAUT,C,GC1BjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA,G","sources":["webpack://@lizardbyte/shared-web/./src/js/ranking-sorter.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Sorts an array of objects by two keys in descending order.\n * @param {string} firstKey The primary key to sort by.\n * @param {string} secondKey The secondary key to sort by, in case the first key is equal.\n * @returns {Function} The sorting function that returns a number.\n */\nfunction rankingSorter(firstKey, secondKey) {\n return function(a, b) {\n if (a[firstKey] > b[firstKey]) {\n return -1;\n } else if (a[firstKey] < b[firstKey]) {\n return 1;\n } else if (a[secondKey] > b[secondKey]) {\n return 1;\n } else if (a[secondKey] < b[secondKey]) {\n return -1;\n } else {\n return 0;\n }\n }\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.rankingSorter = rankingSorter;\n}\n\nmodule.exports = rankingSorter;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(76);\n"],"names":["rankingSorter","firstKey","secondKey","a","b","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
package/dist/sleep.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.js","mappings":"oBAKA,SAASA,EAAMC,GACX,OAAO,IAAIC,QAAQC,GAAWC,WAAWD,EAASF,GACtD,MAG0BI,IAAtBC,WAAWC,SACXD,WAAWN,MAAQA,GAGvBQ,EAAOC,QAAUT,ICbjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA","sources":["webpack://@lizardbyte/shared-web/./src/js/sleep.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Sleep for a given amount of time.\n * @param {number} ms The time to sleep in milliseconds.\n * @returns {Promise<unknown>} A promise that resolves after the given time.\n */\nfunction sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.sleep = sleep;\n}\n\nmodule.exports = sleep;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(399);\n"],"names":["sleep","ms","Promise","resolve","setTimeout","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
1
+ {"version":3,"file":"sleep.js","mappings":"oBAKA,SAASA,EAAMC,GACX,OAAO,IAAIC,QAAQC,GAAWC,WAAWD,EAASF,GACtD,MAG0BI,IAAtBC,WAAWC,SACXD,WAAWN,MAAQA,GAGvBQ,EAAOC,QAAUT,C,GCbjB,MAAAU,EAAA,IAGA,SAAAC,EAAAC,GAEA,MAAAC,EAAAH,EAAAE,GACA,QAAAP,IAAAQ,EACA,OAAAA,EAAAJ,QAGA,MAAAD,EAAAE,EAAAE,GAAA,CAGAH,QAAA,IAOA,OAHAK,EAAAF,GAAAJ,EAAAA,EAAAC,QAAAE,GAGAH,EAAAC,OACA,CCnBAE,CAAA,I","sources":["webpack://@lizardbyte/shared-web/./src/js/sleep.js","webpack://@lizardbyte/shared-web/webpack/bootstrap","webpack://@lizardbyte/shared-web/webpack/startup"],"sourcesContent":["/**\n * Sleep for a given amount of time.\n * @param {number} ms The time to sleep in milliseconds.\n * @returns {Promise<unknown>} A promise that resolves after the given time.\n */\nfunction sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// Expose to the global scope\nif (globalThis.window !== undefined) {\n globalThis.sleep = sleep;\n}\n\nmodule.exports = sleep;\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(399);\n"],"names":["sleep","ms","Promise","resolve","setTimeout","undefined","globalThis","window","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/LizardByte/shared-web.git"
6
6
  },
7
- "version": "2026.811.210538",
7
+ "version": "2026.920.12131",
8
8
  "description": "Shared web assets for LizardByte projects",
9
9
  "license": "AGPL-3.0-only",
10
10
  "funding": "https://app.lizardbyte.dev",
@@ -23,32 +23,31 @@
23
23
  "bootstrap": "5.3.8"
24
24
  },
25
25
  "devDependencies": {
26
- "@babel/core": "8.0.1",
27
- "@babel/preset-env": "8.0.2",
26
+ "@babel/core": "8.0.5",
27
+ "@babel/preset-env": "8.0.5",
28
28
  "@codecov/webpack-plugin": "2.0.1",
29
29
  "@eslint/js": "10.0.1",
30
- "@jest/globals": "30.4.1",
30
+ "@jest/globals": "30.5.1",
31
31
  "babel-loader": "10.1.1",
32
- "clean-jsdoc-theme": "5.1.1",
33
32
  "cross-env": "10.1.0",
34
- "css-loader": "7.1.4",
35
- "eslint": "10.8.1",
36
- "eslint-plugin-jest": "29.16.0",
37
- "globals": "17.9.0",
38
- "jest": "30.4.2",
39
- "jest-environment-jsdom": "30.4.1",
33
+ "css-loader": "7.1.5",
34
+ "eslint": "10.10.0",
35
+ "eslint-plugin-jest": "29.16.6",
36
+ "globals": "17.12.0",
37
+ "jest": "30.5.1",
38
+ "jest-environment-jsdom": "30.5.1",
40
39
  "jest-junit": "17.0.0",
41
40
  "jsdoc": "4.0.5",
42
41
  "mini-css-extract-plugin": "2.10.2",
43
42
  "node-fetch": "3.3.2",
44
43
  "npm-run-all2": "9.0.3",
45
- "postcss": "8.5.26",
44
+ "postcss": "8.5.28",
46
45
  "postcss-loader": "8.2.1",
47
- "postcss-preset-env": "11.3.2",
48
- "sass": "1.102.0",
49
- "sass-loader": "17.0.0",
50
- "webpack": "5.109.2",
51
- "webpack-cli": "7.2.2",
46
+ "postcss-preset-env": "11.5.3",
47
+ "sass": "1.104.1",
48
+ "sass-loader": "17.0.1",
49
+ "webpack": "5.111.0",
50
+ "webpack-cli": "7.2.3",
52
51
  "webpack-dev-server": "6.0.0"
53
52
  },
54
53
  "jest": {
@@ -64,11 +63,15 @@
64
63
  "statements": 100
65
64
  }
66
65
  },
66
+ "testPathIgnorePatterns": [
67
+ "<rootDir>/examples/jekyll/build/",
68
+ "<rootDir>/third-party/"
69
+ ],
67
70
  "testEnvironment": "jsdom"
68
71
  },
69
72
  "scripts": {
70
73
  "build": "cross-env NODE_ENV=production webpack",
71
- "generate-docs": "jsdoc --configure jsdoc.config.js --verbose",
74
+ "generate-docs": "dockle build",
72
75
  "start": "webpack serve",
73
76
  "test": "npm-run-all test:unit test:report test:lint",
74
77
  "test:unit": "jest --coverage",
@@ -0,0 +1,42 @@
1
+ #crowdin-language-picker.dockle-crowdin-picker {
2
+ bottom: 1rem !important;
3
+ left: var(--dockle-sidebar-padding, 1.25rem) !important;
4
+ position: fixed !important;
5
+ width: calc(var(--dockle-sidebar-width, 18rem) - (2 * var(--dockle-sidebar-padding, 1.25rem))) !important;
6
+ z-index: 200;
7
+ }
8
+
9
+ #crowdin-language-picker.dockle-crowdin-picker .cr-picker-button {
10
+ box-sizing: border-box;
11
+ width: 100%;
12
+ }
13
+
14
+ #crowdin-language-picker .cr-picker-button,
15
+ #crowdin-language-picker .cr-picker-submenu {
16
+ background-color: var(--dockle-background-secondary) !important;
17
+ border: 1px solid var(--dockle-border) !important;
18
+ border-radius: var(--dockle-radius) !important;
19
+ color: var(--dockle-content) !important;
20
+ }
21
+
22
+ #crowdin-language-picker .cr-picker-button:hover,
23
+ #crowdin-language-picker .cr-picker-submenu > a:hover {
24
+ background-color: var(--dockle-primary) !important;
25
+ color: white !important;
26
+ }
27
+
28
+ #crowdin-language-picker .cr-picker-submenu > a {
29
+ color: var(--dockle-content) !important;
30
+ }
31
+
32
+ #crowdin-language-picker .cr-picker-submenu > a.cr-selected {
33
+ color: var(--dockle-primary) !important;
34
+ }
35
+
36
+ @media (max-width: 50rem) {
37
+ #crowdin-language-picker.dockle-crowdin-picker {
38
+ bottom: 1rem !important;
39
+ left: 1rem !important;
40
+ width: min(18rem, calc(100vw - 2rem)) !important;
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ import "../css/crowdin-dockle.scss";
package/src/js/crowdin.js CHANGED
@@ -36,12 +36,44 @@ const CROWDIN_INLINE_ELEMENT_SELECTOR = [
36
36
  'var',
37
37
  ].join(',');
38
38
 
39
+ /**
40
+ * Returns a sibling when it is an inline element whose boundary Crowdin may rewrite.
41
+ * @param {Node|null} sibling Candidate sibling.
42
+ * @returns {Element|null} Matching inline element.
43
+ */
44
+ function _getCrowdinInlineSibling(sibling) {
45
+ if (!(sibling instanceof globalThis.Element)) return null;
46
+ return sibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR) ? sibling : null;
47
+ }
48
+
49
+ /**
50
+ * Returns the exact whitespace at the start of a string.
51
+ * @param {string} text Text to inspect.
52
+ * @returns {string} Leading whitespace.
53
+ */
54
+ function _getLeadingWhitespace(text) {
55
+ const trimmedText = text.trimStart();
56
+ return text.slice(0, text.length - trimmedText.length);
57
+ }
58
+
59
+ /**
60
+ * Returns the exact whitespace at the end of a string.
61
+ * @param {string} text Text to inspect.
62
+ * @returns {string} Trailing whitespace.
63
+ */
64
+ function _getTrailingWhitespace(text) {
65
+ const trimmedText = text.trimEnd();
66
+ return text.slice(trimmedText.length);
67
+ }
68
+
39
69
  /**
40
70
  * Records whitespace that separates text from inline elements before Crowdin translates the page.
41
71
  * @returns {Array<{
42
72
  * node: Text,
43
73
  * leading: boolean,
74
+ * leadingWhitespace: string,
44
75
  * trailing: boolean,
76
+ * trailingWhitespace: string,
45
77
  * previousInline: Element|null,
46
78
  * nextInline: Element|null,
47
79
  * whitespaceOnly: boolean
@@ -53,21 +85,23 @@ function _captureCrowdinWhitespaceBoundaries() {
53
85
  let node = walker.nextNode();
54
86
 
55
87
  while (node !== null) {
56
- const previousIsInline = node.previousSibling instanceof globalThis.Element &&
57
- node.previousSibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR);
58
- const nextIsInline = node.nextSibling instanceof globalThis.Element &&
59
- node.nextSibling.matches(CROWDIN_INLINE_ELEMENT_SELECTOR);
60
- const leading = previousIsInline && /^\s/.test(node.data);
61
- const trailing = nextIsInline && /\s$/.test(node.data);
88
+ const previousInline = _getCrowdinInlineSibling(node.previousSibling);
89
+ const nextInline = _getCrowdinInlineSibling(node.nextSibling);
90
+ const leadingWhitespace = previousInline === null ? '' : _getLeadingWhitespace(node.data);
91
+ const trailingWhitespace = nextInline === null ? '' : _getTrailingWhitespace(node.data);
92
+ const leading = leadingWhitespace !== '';
93
+ const trailing = trailingWhitespace !== '';
62
94
 
63
95
  if (leading || trailing) {
64
96
  boundaries.push({
65
97
  node,
66
98
  leading,
99
+ leadingWhitespace,
67
100
  trailing,
68
- previousInline: previousIsInline ? node.previousSibling : null,
69
- nextInline: nextIsInline ? node.nextSibling : null,
70
- whitespaceOnly: /^\s*$/.test(node.data),
101
+ trailingWhitespace,
102
+ previousInline,
103
+ nextInline,
104
+ whitespaceOnly: node.data.trim() === '',
71
105
  });
72
106
  }
73
107
 
@@ -119,11 +153,14 @@ function _restoreCrowdinWhitespaceBoundaries(boundaries) {
119
153
  const node = _resolveCrowdinWhitespaceNode(boundary);
120
154
  if (node === null) return;
121
155
 
122
- if (boundary.leading && !/^\s/.test(node.data)) {
123
- node.data = ' ' + node.data;
156
+ const currentLeadingWhitespace = _getLeadingWhitespace(node.data);
157
+ if (boundary.leading && currentLeadingWhitespace !== boundary.leadingWhitespace) {
158
+ node.data = boundary.leadingWhitespace + node.data.slice(currentLeadingWhitespace.length);
124
159
  }
125
- if (boundary.trailing && !/\s$/.test(node.data)) {
126
- node.data += ' ';
160
+ const currentTrailingWhitespace = _getTrailingWhitespace(node.data);
161
+ if (boundary.trailing && currentTrailingWhitespace !== boundary.trailingWhitespace) {
162
+ const translatedTextEnd = node.data.length - currentTrailingWhitespace.length;
163
+ node.data = node.data.slice(0, translatedTextEnd) + boundary.trailingWhitespace;
127
164
  }
128
165
  });
129
166
  }
@@ -187,7 +224,7 @@ function _installCrowdinFetchInterceptor() {
187
224
 
188
225
  /**
189
226
  * Re-attempts platform styling while Crowdin inserts the language picker.
190
- * @param {string} platform - UI platform ('sphinx' or 'rustdoc').
227
+ * @param {string} platform - UI platform ('dockle' or 'jekyll').
191
228
  * @param {number} attempt - Current retry count.
192
229
  */
193
230
  function _retryCrowdinPlatformStyling(platform, attempt) {
@@ -202,61 +239,42 @@ function _retryCrowdinPlatformStyling(platform, attempt) {
202
239
 
203
240
  /**
204
241
  * Applies platform-specific placement after the Crowdin picker exists.
205
- * @param {string} platform - UI platform ('sphinx' or 'rustdoc').
242
+ * @param {string} platform - UI platform ('dockle' or 'jekyll').
206
243
  * @param {number} attempt - Current retry count.
207
244
  */
208
245
  function _applyCrowdinPlatformStyling(platform, attempt = 0) {
209
- const container = document.getElementById('crowdin-language-picker');
210
-
211
- if (platform === 'sphinx') {
212
- const button = document.getElementsByClassName('cr-picker-button')[0];
213
- const sidebar = document.getElementsByClassName('sidebar-sticky')[0];
214
-
215
- if (container === null || button === undefined || sidebar === undefined) {
216
- _retryCrowdinPlatformStyling(platform, attempt);
217
- return;
218
- }
219
-
220
- container.classList.remove('cr-position-bottom-left');
221
- container.style.width = button.offsetWidth + 10 + 'px';
222
- container.style.position = 'relative';
223
- container.style.left = '10px';
224
- container.style.bottom = '10px';
225
-
226
- // move button to related pages
227
- sidebar.appendChild(container);
246
+ if (platform === 'jekyll') {
228
247
  return;
229
248
  }
230
249
 
231
- const sidebar = document.querySelector('.sidebar .sidebar-elems') || document.querySelector('.sidebar');
250
+ const container = document.getElementById('crowdin-language-picker');
232
251
 
233
- if (container === null || sidebar === null) {
252
+ if (container === null) {
234
253
  _retryCrowdinPlatformStyling(platform, attempt);
235
254
  return;
236
255
  }
237
256
 
238
257
  container.classList.remove('cr-position-bottom-left');
239
- container.classList.add('rustdoc-crowdin-picker');
240
- container.style.position = 'static';
258
+ container.classList.add('dockle-crowdin-picker');
259
+ container.style.position = 'fixed';
241
260
  container.style.left = 'auto';
242
261
  container.style.bottom = 'auto';
243
-
244
- sidebar.appendChild(container);
262
+ document.body.appendChild(container);
245
263
  }
246
264
 
247
265
  /**
248
266
  * Initializes Crowdin translation widget based on project and UI platform.
249
267
  * @param {string} project - Project name ('LizardByte' or 'LizardByte-docs').
250
- * @param {string|null} platform - UI platform ('sphinx', 'rustdoc', or null).
268
+ * @param {string} platform - UI platform ('dockle' or 'jekyll').
251
269
  */
252
- function initCrowdIn(project = 'LizardByte', platform = null) {
270
+ function initCrowdIn(project = 'LizardByte', platform = 'jekyll') {
253
271
  // Input validation
254
272
  if (!['LizardByte', 'LizardByte-docs'].includes(project)) {
255
273
  console.error('Invalid project. Must be "LizardByte" or "LizardByte-docs"');
256
274
  return;
257
275
  }
258
- if (!['sphinx', 'rustdoc', null].includes(platform)) {
259
- console.error('Invalid UI. Must be "sphinx", "rustdoc", or null');
276
+ if (!['dockle', 'jekyll'].includes(platform)) {
277
+ console.error('Invalid UI. Must be "dockle" or "jekyll"');
260
278
  return;
261
279
  }
262
280
 
@@ -324,10 +342,6 @@ function initCrowdIn(project = 'LizardByte', platform = null) {
324
342
  });
325
343
 
326
344
  // Apply styling based on UI framework
327
- if (platform === null) {
328
- return;
329
- }
330
-
331
345
  _applyCrowdinPlatformStyling(platform);
332
346
  });
333
347
  }
@@ -24,30 +24,6 @@ const delayedPickerMarkup = `
24
24
  </div>
25
25
  `;
26
26
 
27
- function expectDelayedStyling(options) {
28
- globalThis.document.body.innerHTML = options.initialMarkup;
29
-
30
- initCrowdIn('LizardByte', options.platform);
31
-
32
- expect(() => {
33
- jest.advanceTimersByTime(0);
34
- }).not.toThrow();
35
-
36
- globalThis.document.body.insertAdjacentHTML('beforeend', delayedPickerMarkup);
37
-
38
- jest.advanceTimersByTime(50);
39
-
40
- const container = document.getElementById('crowdin-language-picker');
41
- const sidebar = document.getElementsByClassName(options.sidebarClass)[0];
42
-
43
- expect(container.classList.contains('cr-position-bottom-left')).toBe(false);
44
- expect(container.style.position).toBe(options.position);
45
- if (options.pickerClass !== null) {
46
- expect(container.classList.contains(options.pickerClass)).toBe(true);
47
- }
48
- expect(sidebar.contains(container)).toBe(true);
49
- }
50
-
51
27
  describe('initCrowdIn', () => {
52
28
  beforeEach(() => {
53
29
  // Mock DOM elements
@@ -58,13 +34,6 @@ describe('initCrowdIn', () => {
58
34
  <div class="cr-selected"></div>
59
35
  </div>
60
36
 
61
- <!-- Sphinx sidebar -->
62
- <div class="sidebar-sticky"></div>
63
-
64
- <!-- rustdoc sidebar -->
65
- <nav class="sidebar">
66
- <div class="sidebar-elems"></div>
67
- </nav>
68
37
  `;
69
38
 
70
39
  // Mock console.error
@@ -97,7 +66,9 @@ describe('initCrowdIn', () => {
97
66
 
98
67
  it('should validate platform parameter', () => {
99
68
  initCrowdIn('LizardByte', 'invalidPlatform');
100
- expect(console.error).toHaveBeenCalledWith('Invalid UI. Must be "sphinx", "rustdoc", or null');
69
+ expect(console.error).toHaveBeenCalledWith(
70
+ 'Invalid UI. Must be "dockle" or "jekyll"'
71
+ );
101
72
  });
102
73
 
103
74
  it('should initialize proxyTranslator with LizardByte settings', () => {
@@ -175,6 +146,27 @@ describe('initCrowdIn', () => {
175
146
  expect(nextAnchor.textContent).toBe(' kept');
176
147
  });
177
148
 
149
+ it('should restore line breaks between translated syntax-highlighted lines', () => {
150
+ globalThis.document.body.innerHTML = [
151
+ '<pre id="code-block"><span></span><span class="k">- [x]</span> This is a complete item\n',
152
+ '<span class="k">- [ ]</span> This is an incomplete item\n</pre>',
153
+ ].join('');
154
+
155
+ initCrowdIn();
156
+ jest.runAllTimers();
157
+
158
+ const options = globalThis.proxyTranslator.init.mock.calls[0][0];
159
+ const codeBlock = globalThis.document.getElementById('code-block');
160
+ const firstLineText = codeBlock.querySelector('.k').nextSibling;
161
+
162
+ firstLineText.data = firstLineText.data.trim();
163
+ options.callback();
164
+
165
+ expect(codeBlock.textContent).toBe(
166
+ '- [x] This is a complete item\n- [ ] This is an incomplete item\n'
167
+ );
168
+ });
169
+
178
170
  it('should restore whitespace after later asynchronous DOM changes', async () => {
179
171
  globalThis.document.body.innerHTML = '<p id="translated">Use <a href="#">this link</a> here.</p>';
180
172
 
@@ -213,93 +205,47 @@ describe('initCrowdIn', () => {
213
205
  );
214
206
  });
215
207
 
216
- it('should not apply styling when platform is null', () => {
217
- initCrowdIn('LizardByte', null);
218
-
219
- // Simulate script loading and run the setTimeout from UI styling
220
- jest.runAllTimers();
221
-
222
- // Verify that no styling was applied
223
- const button = document.getElementsByClassName('cr-picker-button')[0];
224
- expect(button.classList.contains('btn')).toBe(false);
225
- });
208
+ it('should leave native positioning in place for Jekyll', () => {
209
+ const container = document.getElementById('crowdin-language-picker');
210
+ container.classList.add('cr-position-bottom-left');
226
211
 
227
- it('should apply sphinx styling', () => {
228
- initCrowdIn('LizardByte', 'sphinx');
212
+ initCrowdIn('LizardByte', 'jekyll');
229
213
 
230
- // Simulate script loading and UI styling timeout
231
214
  jest.runAllTimers();
232
215
 
233
- const container = document.getElementById('crowdin-language-picker');
234
- const sidebar = document.getElementsByClassName('sidebar-sticky')[0];
235
-
236
- expect(container.classList.contains('cr-position-bottom-left')).toBe(false);
237
- expect(container.style.position).toBe('relative');
238
- expect(sidebar.contains(container)).toBe(true);
216
+ expect(container.classList.contains('cr-position-bottom-left')).toBe(true);
217
+ expect(container.classList.contains('dockle-crowdin-picker')).toBe(false);
239
218
  });
240
219
 
241
- it('should apply rustdoc styling', () => {
242
- initCrowdIn('LizardByte', 'rustdoc');
243
-
244
- // Simulate script loading and UI styling timeout
220
+ it('should apply consistent Dockle styling', () => {
221
+ initCrowdIn('LizardByte', 'dockle');
245
222
  jest.runAllTimers();
246
223
 
247
224
  const container = document.getElementById('crowdin-language-picker');
248
- const sidebar = document.getElementsByClassName('sidebar-elems')[0];
249
225
 
250
226
  expect(container.classList.contains('cr-position-bottom-left')).toBe(false);
251
- expect(container.classList.contains('rustdoc-crowdin-picker')).toBe(true);
252
- expect(container.style.position).toBe('static');
253
- expect(sidebar.contains(container)).toBe(true);
254
- });
255
-
256
- it('should wait for sphinx language picker before applying styling', () => {
257
- expectDelayedStyling({
258
- initialMarkup: '<div class="sidebar-sticky"></div>',
259
- pickerClass: null,
260
- platform: 'sphinx',
261
- position: 'relative',
262
- sidebarClass: 'sidebar-sticky',
263
- });
227
+ expect(container.classList.contains('dockle-crowdin-picker')).toBe(true);
228
+ expect(container.style.position).toBe('fixed');
229
+ expect(document.body.lastElementChild).toBe(container);
264
230
  });
265
231
 
266
- it('should wait for rustdoc language picker before applying styling', () => {
267
- expectDelayedStyling({
268
- initialMarkup: '<nav class="sidebar"><div class="sidebar-elems"></div></nav>',
269
- pickerClass: 'rustdoc-crowdin-picker',
270
- platform: 'rustdoc',
271
- position: 'static',
272
- sidebarClass: 'sidebar-elems',
273
- });
274
- });
275
-
276
- it('should move rustdoc language picker to sidebar when sidebar-elems is unavailable', () => {
277
- globalThis.document.body.innerHTML = `
278
- <nav class="sidebar"></nav>
279
- <div id="crowdin-language-picker" class="cr-position-bottom-left">
280
- <div class="cr-picker-button"></div>
281
- <div class="cr-picker-submenu"></div>
282
- </div>
283
- `;
232
+ it('should wait for the Dockle language picker before applying styling', () => {
233
+ globalThis.document.body.innerHTML = '';
284
234
 
285
- initCrowdIn('LizardByte', 'rustdoc');
286
- jest.runAllTimers();
235
+ initCrowdIn('LizardByte', 'dockle');
236
+ jest.advanceTimersByTime(0);
237
+ globalThis.document.body.insertAdjacentHTML('beforeend', delayedPickerMarkup);
238
+ jest.advanceTimersByTime(50);
287
239
 
288
240
  const container = document.getElementById('crowdin-language-picker');
289
- const sidebar = document.getElementsByClassName('sidebar')[0];
290
-
291
- expect(container.classList.contains('rustdoc-crowdin-picker')).toBe(true);
292
- expect(sidebar.contains(container)).toBe(true);
241
+ expect(container.classList.contains('dockle-crowdin-picker')).toBe(true);
242
+ expect(container.style.position).toBe('fixed');
293
243
  });
294
244
 
295
245
  it('should stop retrying platform styling after the retry limit', () => {
296
- globalThis.document.body.innerHTML = `
297
- <nav class="sidebar">
298
- <div class="sidebar-elems"></div>
299
- </nav>
300
- `;
246
+ globalThis.document.body.innerHTML = '';
301
247
 
302
- initCrowdIn('LizardByte', 'rustdoc');
248
+ initCrowdIn('LizardByte', 'dockle');
303
249
 
304
250
  jest.advanceTimersByTime(0);
305
251
  jest.advanceTimersByTime(5000);
@@ -1,3 +0,0 @@
1
- :root{--crowdin-primary-color: #f7f7f7;--crowdin-secondary-color: #565656;--crowdin-primary-text-color: #111111;--crowdin-secondary-text-color: #c5c5c9}@media(prefers-color-scheme: dark){body[data-theme=dark]{--crowdin-primary-color: #222222;--crowdin-secondary-color: #717171;--crowdin-primary-text-color: #ffffff;--crowdin-secondary-text-color: #8b949e}}#crowdin-language-picker .cr-picker-button,#crowdin-language-picker .cr-picker-submenu{background-color:#f7f7f7 !important;background-color:var(--crowdin-primary-color) !important;border:1px solid #565656 !important;border:1px solid var(--crowdin-secondary-color) !important;color:#111111 !important;color:var(--crowdin-primary-text-color) !important}#crowdin-language-picker .cr-picker-button:hover,#crowdin-language-picker .cr-picker-submenu>a:hover{background-color:#111111 !important;background-color:var(--crowdin-primary-text-color) !important;color:#f7f7f7 !important;color:var(--crowdin-primary-color) !important}#crowdin-language-picker .cr-picker-submenu>a{color:#111111 !important;color:var(--crowdin-primary-text-color) !important}#crowdin-language-picker .cr-picker-submenu>a.cr-selected{color:#c5c5c9 !important;color:var(--crowdin-secondary-text-color) !important}
2
-
3
- /*# sourceMappingURL=crowdin-clean-jsdoc-css.css.map*/
@@ -1 +0,0 @@
1
- {"version":3,"file":"crowdin-clean-jsdoc-css.css","mappings":"AAAA,MACE,iCACA,mCACA,sCACA,wCAGF,mCACE,sBACE,iCACA,mCACA,sCACA,yCAIJ,uFAEI,6FACA,+FACA,4EAGJ,qGAEE,kGACA,uEAGF,8CACE,4EAGF,0DACE,8E","sources":["webpack://@lizardbyte/shared-web/./src/css/crowdin-clean-jsdoc.scss"],"sourcesContent":[":root {\n --crowdin-primary-color: #f7f7f7;\n --crowdin-secondary-color: #565656;\n --crowdin-primary-text-color: #111111;\n --crowdin-secondary-text-color: #c5c5c9;\n}\n\n@media (prefers-color-scheme: dark) {\n body[data-theme=\"dark\"] {\n --crowdin-primary-color: #222222;\n --crowdin-secondary-color: #717171;\n --crowdin-primary-text-color: #ffffff;\n --crowdin-secondary-text-color: #8b949e;\n }\n}\n\n#crowdin-language-picker .cr-picker-button,\n#crowdin-language-picker .cr-picker-submenu {\n background-color: var(--crowdin-primary-color) !important;\n border: 1px solid var(--crowdin-secondary-color) !important;\n color: var(--crowdin-primary-text-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-button:hover,\n#crowdin-language-picker .cr-picker-submenu > a:hover {\n background-color: var(--crowdin-primary-text-color) !important;\n color: var(--crowdin-primary-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a {\n color: var(--crowdin-primary-text-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a.cr-selected {\n color: var(--crowdin-secondary-text-color) !important;\n}\n"],"names":[],"sourceRoot":""}
@@ -1,3 +0,0 @@
1
- #crowdin-language-picker .cr-picker-button,#crowdin-language-picker .cr-picker-submenu{background-color:var(--page-background-color) !important;border:1px solid var(--page-secondary-foreground-color) !important;color:var(--page-foreground-color) !important}#crowdin-language-picker .cr-picker-button:hover,#crowdin-language-picker .cr-picker-submenu>a:hover{background-color:var(--page-foreground-color) !important;color:var(--page-background-color) !important}#crowdin-language-picker .cr-picker-submenu>a{color:var(--page-foreground-color) !important}#crowdin-language-picker .cr-picker-submenu>a.cr-selected{color:var(--page-secondary-foreground-color) !important}
2
-
3
- /*# sourceMappingURL=crowdin-doxygen-css.css.map*/
@@ -1 +0,0 @@
1
- {"version":3,"file":"crowdin-doxygen-css.css","mappings":"AAAA,uFAEE,yDACA,mEACA,8CAGF,qGAEE,yDACA,8CAGF,8CACE,8CAGF,0DACE,wD","sources":["webpack://@lizardbyte/shared-web/./src/css/crowdin-doxygen.scss"],"sourcesContent":["#crowdin-language-picker .cr-picker-button,\n#crowdin-language-picker .cr-picker-submenu {\n background-color: var(--page-background-color) !important;\n border: 1px solid var(--page-secondary-foreground-color) !important;\n color: var(--page-foreground-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-button:hover,\n#crowdin-language-picker .cr-picker-submenu > a:hover {\n background-color: var(--page-foreground-color) !important;\n color: var(--page-background-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a {\n color: var(--page-foreground-color) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a.cr-selected {\n color: var(--page-secondary-foreground-color) !important;\n}\n"],"names":[],"sourceRoot":""}
File without changes
@@ -1,3 +0,0 @@
1
- #crowdin-language-picker .cr-picker-button,#crowdin-language-picker .cr-picker-submenu{background-color:var(--color-code-background) !important;border:1px solid var(--color-foreground-border) !important;color:var(--color-code-foreground) !important}#crowdin-language-picker .cr-picker-button:hover,#crowdin-language-picker .cr-picker-submenu>a:hover{background-color:var(--color-code-foreground) !important;color:var(--color-code-background) !important}#crowdin-language-picker .cr-picker-submenu>a{color:var(--color-code-foreground) !important}#crowdin-language-picker .cr-picker-submenu>a.cr-selected{color:var(--color-background-item) !important}
2
-
3
- /*# sourceMappingURL=crowdin-furo-css.css.map*/
@@ -1 +0,0 @@
1
- {"version":3,"file":"crowdin-furo-css.css","mappings":"AAAA,uFAEE,yDACA,2DACA,8CAGF,qGAEE,yDACA,8CAGF,8CACE,8CAGF,0DACE,8C","sources":["webpack://@lizardbyte/shared-web/./src/css/crowdin-furo.scss"],"sourcesContent":["#crowdin-language-picker .cr-picker-button,\n#crowdin-language-picker .cr-picker-submenu {\n background-color: var(--color-code-background) !important;\n border: 1px solid var(--color-foreground-border) !important;\n color: var(--color-code-foreground) !important;\n}\n\n#crowdin-language-picker .cr-picker-button:hover,\n#crowdin-language-picker .cr-picker-submenu > a:hover {\n background-color: var(--color-code-foreground) !important;\n color: var(--color-code-background) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a {\n color: var(--color-code-foreground) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a.cr-selected {\n color: var(--color-background-item) !important;\n}\n"],"names":[],"sourceRoot":""}
File without changes
@@ -1,3 +0,0 @@
1
- #crowdin-language-picker.rustdoc-crowdin-picker{margin:1rem;width:calc(100% - 2rem)}#crowdin-language-picker.rustdoc-crowdin-picker .cr-picker-button{box-sizing:border-box;width:100%}#crowdin-language-picker .cr-picker-button,#crowdin-language-picker .cr-picker-submenu{background-color:#ffffff !important;background-color:var(--sidebar-background-color, var(--main-background-color, #ffffff)) !important;border:1px solid #dddddd !important;border:1px solid var(--border-color, #dddddd) !important;color:#111111 !important;color:var(--main-color, #111111) !important}#crowdin-language-picker .cr-picker-button:hover,#crowdin-language-picker .cr-picker-submenu>a:hover{background-color:#111111 !important;background-color:var(--main-color, #111111) !important;color:#ffffff !important;color:var(--main-background-color, #ffffff) !important}#crowdin-language-picker .cr-picker-submenu>a{color:#111111 !important;color:var(--main-color, #111111) !important}#crowdin-language-picker .cr-picker-submenu>a.cr-selected{color:#3873ad !important;color:var(--link-color, #3873ad) !important}
2
-
3
- /*# sourceMappingURL=crowdin-rustdoc-css.css.map*/
@@ -1 +0,0 @@
1
- {"version":3,"file":"crowdin-rustdoc-css.css","mappings":"AAAA,gDACE,YACA,wBAGF,kEACE,sBACA,WAGF,uFAEE,uIACA,6FACA,qEAGF,qGAEE,2FACA,gFAGF,8CACE,qEAGF,0DACE,qE","sources":["webpack://@lizardbyte/shared-web/./src/css/crowdin-rustdoc.scss"],"sourcesContent":["#crowdin-language-picker.rustdoc-crowdin-picker {\n margin: 1rem;\n width: calc(100% - 2rem);\n}\n\n#crowdin-language-picker.rustdoc-crowdin-picker .cr-picker-button {\n box-sizing: border-box;\n width: 100%;\n}\n\n#crowdin-language-picker .cr-picker-button,\n#crowdin-language-picker .cr-picker-submenu {\n background-color: var(--sidebar-background-color, var(--main-background-color, #ffffff)) !important;\n border: 1px solid var(--border-color, #dddddd) !important;\n color: var(--main-color, #111111) !important;\n}\n\n#crowdin-language-picker .cr-picker-button:hover,\n#crowdin-language-picker .cr-picker-submenu > a:hover {\n background-color: var(--main-color, #111111) !important;\n color: var(--main-background-color, #ffffff) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a {\n color: var(--main-color, #111111) !important;\n}\n\n#crowdin-language-picker .cr-picker-submenu > a.cr-selected {\n color: var(--link-color, #3873ad) !important;\n}\n"],"names":[],"sourceRoot":""}
File without changes
@@ -1,36 +0,0 @@
1
- :root {
2
- --crowdin-primary-color: #f7f7f7;
3
- --crowdin-secondary-color: #565656;
4
- --crowdin-primary-text-color: #111111;
5
- --crowdin-secondary-text-color: #c5c5c9;
6
- }
7
-
8
- @media (prefers-color-scheme: dark) {
9
- body[data-theme="dark"] {
10
- --crowdin-primary-color: #222222;
11
- --crowdin-secondary-color: #717171;
12
- --crowdin-primary-text-color: #ffffff;
13
- --crowdin-secondary-text-color: #8b949e;
14
- }
15
- }
16
-
17
- #crowdin-language-picker .cr-picker-button,
18
- #crowdin-language-picker .cr-picker-submenu {
19
- background-color: var(--crowdin-primary-color) !important;
20
- border: 1px solid var(--crowdin-secondary-color) !important;
21
- color: var(--crowdin-primary-text-color) !important;
22
- }
23
-
24
- #crowdin-language-picker .cr-picker-button:hover,
25
- #crowdin-language-picker .cr-picker-submenu > a:hover {
26
- background-color: var(--crowdin-primary-text-color) !important;
27
- color: var(--crowdin-primary-color) !important;
28
- }
29
-
30
- #crowdin-language-picker .cr-picker-submenu > a {
31
- color: var(--crowdin-primary-text-color) !important;
32
- }
33
-
34
- #crowdin-language-picker .cr-picker-submenu > a.cr-selected {
35
- color: var(--crowdin-secondary-text-color) !important;
36
- }
@@ -1,20 +0,0 @@
1
- #crowdin-language-picker .cr-picker-button,
2
- #crowdin-language-picker .cr-picker-submenu {
3
- background-color: var(--page-background-color) !important;
4
- border: 1px solid var(--page-secondary-foreground-color) !important;
5
- color: var(--page-foreground-color) !important;
6
- }
7
-
8
- #crowdin-language-picker .cr-picker-button:hover,
9
- #crowdin-language-picker .cr-picker-submenu > a:hover {
10
- background-color: var(--page-foreground-color) !important;
11
- color: var(--page-background-color) !important;
12
- }
13
-
14
- #crowdin-language-picker .cr-picker-submenu > a {
15
- color: var(--page-foreground-color) !important;
16
- }
17
-
18
- #crowdin-language-picker .cr-picker-submenu > a.cr-selected {
19
- color: var(--page-secondary-foreground-color) !important;
20
- }
@@ -1,20 +0,0 @@
1
- #crowdin-language-picker .cr-picker-button,
2
- #crowdin-language-picker .cr-picker-submenu {
3
- background-color: var(--color-code-background) !important;
4
- border: 1px solid var(--color-foreground-border) !important;
5
- color: var(--color-code-foreground) !important;
6
- }
7
-
8
- #crowdin-language-picker .cr-picker-button:hover,
9
- #crowdin-language-picker .cr-picker-submenu > a:hover {
10
- background-color: var(--color-code-foreground) !important;
11
- color: var(--color-code-background) !important;
12
- }
13
-
14
- #crowdin-language-picker .cr-picker-submenu > a {
15
- color: var(--color-code-foreground) !important;
16
- }
17
-
18
- #crowdin-language-picker .cr-picker-submenu > a.cr-selected {
19
- color: var(--color-background-item) !important;
20
- }
@@ -1,30 +0,0 @@
1
- #crowdin-language-picker.rustdoc-crowdin-picker {
2
- margin: 1rem;
3
- width: calc(100% - 2rem);
4
- }
5
-
6
- #crowdin-language-picker.rustdoc-crowdin-picker .cr-picker-button {
7
- box-sizing: border-box;
8
- width: 100%;
9
- }
10
-
11
- #crowdin-language-picker .cr-picker-button,
12
- #crowdin-language-picker .cr-picker-submenu {
13
- background-color: var(--sidebar-background-color, var(--main-background-color, #ffffff)) !important;
14
- border: 1px solid var(--border-color, #dddddd) !important;
15
- color: var(--main-color, #111111) !important;
16
- }
17
-
18
- #crowdin-language-picker .cr-picker-button:hover,
19
- #crowdin-language-picker .cr-picker-submenu > a:hover {
20
- background-color: var(--main-color, #111111) !important;
21
- color: var(--main-background-color, #ffffff) !important;
22
- }
23
-
24
- #crowdin-language-picker .cr-picker-submenu > a {
25
- color: var(--main-color, #111111) !important;
26
- }
27
-
28
- #crowdin-language-picker .cr-picker-submenu > a.cr-selected {
29
- color: var(--link-color, #3873ad) !important;
30
- }
@@ -1 +0,0 @@
1
- import "../css/crowdin-clean-jsdoc.scss";
@@ -1 +0,0 @@
1
- import "../css/crowdin-doxygen.scss";
@@ -1 +0,0 @@
1
- import "../css/crowdin-furo.scss";
@@ -1 +0,0 @@
1
- import "../css/crowdin-rustdoc.scss";