From c35b2b7dd00b8d9aa508e35cc967a7ed417f96d9 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Wed, 24 Jun 2026 20:38:10 +0530 Subject: [PATCH 1/2] fix: Use inline themed SVG for loader spinner Import tail-spin.svg as raw SVG markup and render it through the loader DOM instead of using the circular border/background spinner. Update the SVG to inherit currentColor, expand its viewBox to avoid edge clipping, and style the dialog spinner with the app primary theme color. Add ?raw SVG handling to both Rspack and Webpack so inline SVG imports do not conflict with normal asset/resource SVG usage. Also removes the old theme-side tail-spin recoloring path now that the spinner color is driven by CSS. --- rspack.config.js | 6 ++++++ src/cm/themes/noctisLilac.js | 2 +- src/dialogs/loader.js | 17 ++++++++++++++++- src/dialogs/style.scss | 15 ++++++++++++--- src/lib/showFileInfo.js | 5 +++-- src/main.scss | 30 ++++++++++++++++++++---------- src/res/tail-spin.svg | 14 +++++++------- src/theme/list.js | 25 ++++--------------------- webpack.config.js | 30 ++++++++++++++++++------------ 9 files changed, 87 insertions(+), 57 deletions(-) diff --git a/rspack.config.js b/rspack.config.js index 9bb41530b..4cf1ebf9a 100644 --- a/rspack.config.js +++ b/rspack.config.js @@ -89,9 +89,15 @@ module.exports = (env, options) => { ], type: 'javascript/auto', }, + { + test: /\.svg$/, + resourceQuery: /raw/, + type: 'asset/source', + }, // Asset files { test: /\.(png|svg|jpg|jpeg|ico|ttf|webp|eot|woff|webm|mp4|wav)(\?.*)?$/, + resourceQuery: { not: [/raw/] }, type: 'asset/resource', }, // Regular CSS/SCSS files diff --git a/src/cm/themes/noctisLilac.js b/src/cm/themes/noctisLilac.js index c8665b661..c32479274 100644 --- a/src/cm/themes/noctisLilac.js +++ b/src/cm/themes/noctisLilac.js @@ -11,7 +11,7 @@ export const config = { cursor: "#5c49e9", dropdownBackground: "#f2f1f8", dropdownBorder: "#e1def3", - activeLine: "#e1def3", + activeLine: "#e1def355", lineNumber: "#0c006b70", lineNumberActive: "#0c006b", matchingBracket: "#d5d1f2", diff --git a/src/dialogs/loader.js b/src/dialogs/loader.js index 0c69daf4f..18af12af1 100644 --- a/src/dialogs/loader.js +++ b/src/dialogs/loader.js @@ -2,11 +2,13 @@ import DOMPurify from "dompurify"; import Ref from "html-tag-js/ref"; import actionStack from "lib/actionStack"; import restoreTheme from "lib/restoreTheme"; +import tailSpinSvg from "res/tail-spin.svg?raw"; let loaderIsImmortal = false; let onCancelCallback = null; let $currentDialog = null; let $currentMask = null; +const titleLoaderId = "__title-loader"; /** * @typedef {object} LoaderOptions @@ -51,7 +53,7 @@ function create(titleText, message = "", options = {}) { {titleText} - +
+ ); + + if (!$titleLoader.isConnected) { + app.append($titleLoader); + } + + return $titleLoader; +} + /** * Removes the loader from DOM permanently */ @@ -159,6 +173,7 @@ function showTitleLoader(immortal = false) { } setTimeout(() => { + createTitleLoader(); app.classList.remove("title-loading-hide"); app.classList.add("title-loading"); }, 0); diff --git a/src/dialogs/style.scss b/src/dialogs/style.scss index a91908105..2db23f2bb 100644 --- a/src/dialogs/style.scss +++ b/src/dialogs/style.scss @@ -1,5 +1,3 @@ -@use "../styles/mixins.scss"; - .prompt { position: fixed; left: 50%; @@ -285,8 +283,19 @@ align-items: center; .loader { - @include mixins.circular-loader(30px); + width: 30px; + height: 30px; + color: rgb(153, 153, 255); + color: var(--primary-color); + display: flex; + flex-shrink: 0; margin: 0 10px; + + svg { + width: 100%; + height: 100%; + display: block; + } } .message { diff --git a/src/lib/showFileInfo.js b/src/lib/showFileInfo.js index ebc5bd0b8..9b0650c3c 100644 --- a/src/lib/showFileInfo.js +++ b/src/lib/showFileInfo.js @@ -1,5 +1,6 @@ import fsOperation from "fileSystem"; import dialog from "dialogs/dialog"; +import loader from "dialogs/loader"; import { filesize } from "filesize"; import mustache from "mustache"; import helpers from "utils/helpers"; @@ -13,7 +14,7 @@ import settings from "./settings"; */ export default async function showFileInfo(url) { if (!url) url = editorManager.activeFile.uri; - app.classList.add("title-loading"); + loader.showTitleLoader(); try { const fs = fsOperation(url); const stats = await fs.stat(); @@ -65,5 +66,5 @@ export default async function showFileInfo(url) { helpers.error(err); } - app.classList.remove("title-loading"); + loader.removeTitleLoader(); } diff --git a/src/main.scss b/src/main.scss index 152cb77a4..7f985f59f 100644 --- a/src/main.scss +++ b/src/main.scss @@ -47,38 +47,48 @@ body { box-shadow: none !important; } + #__title-loader { + display: none; + } + &:not(.loading).title-loading { &.title-loading-hide { - &::after { - background-image: none; + #__title-loader { transform: translateX(-50%) translateY(-100%) scale3d(0.5, 0.5, 1); opacity: 0; animation: hide-loader 100ms ease-in 1; } } - &::after { - content: ""; - background-color: #3333ff; - background-color: var(--primary-color); + #__title-loader { + align-items: center; + background-color: #ffffff; + background-color: var(--popup-background-color); border-radius: 50%; + color: #9999ff; + color: var(--popup-text-color); + display: flex; + justify-content: center; position: fixed; height: 40px; width: 40px; top: 6px; left: 50%; transform: translateX(-50%); - background-image: url(res/tail-spin.svg); - background-repeat: no-repeat; - background-position: center; - background-size: 30px; box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2); box-shadow: 0 0 4px 0 var(--box-shadow-color); border: solid 1px transparent; border: solid 1px var(--popup-border-color); animation: appear 100ms ease-out 1; box-sizing: border-box; + pointer-events: none; z-index: 999; + + svg { + width: 30px; + height: 30px; + display: block; + } } } diff --git a/src/res/tail-spin.svg b/src/res/tail-spin.svg index a12f0f4b9..b72bb6088 100644 --- a/src/res/tail-spin.svg +++ b/src/res/tail-spin.svg @@ -1,15 +1,15 @@ - + - - - - + + + + - + - + @@ -127,6 +122,7 @@ export async function apply(id, init) { updateActiveTerminals("theme", theme.preferredTerminalTheme); } } + localStorage.__primary_color = theme.primaryColor; document.body.setAttribute("theme-type", theme.type); $style.textContent = theme.css; @@ -144,19 +140,6 @@ export async function apply(id, init) { }, 1000); firstTime = false; } - - try { - let fs = fsOperation(loaderFile); - const svg = await fs.readFile("utf8"); - - fs = fsOperation(img); - if (!(await fs.exists())) { - await fsOperation(DATA_STORAGE).createFile(svgName); - } - await fs.writeFile(svg.replace(/#fff/g, theme.primaryColor)); - } catch (error) { - window.log("error", error); - } } /** diff --git a/webpack.config.js b/webpack.config.js index a87528b11..8767eaa8f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -30,18 +30,24 @@ module.exports = (env, options) => { test: /\.(hbs|md)$/, use: ['raw-loader'], }, - { - test: /\.m.(sa|sc|c)ss$/, - use: [ - 'raw-loader', - 'postcss-loader', - 'sass-loader', - ], - }, - { - test: /\.(png|svg|jpg|jpeg|ico|ttf|webp|eot|woff|webm|mp4|webp|wav)(\?.*)?$/, - type: "asset/resource", - }, + { + test: /\.m.(sa|sc|c)ss$/, + use: [ + 'raw-loader', + 'postcss-loader', + 'sass-loader', + ], + }, + { + test: /\.svg$/, + resourceQuery: /raw/, + type: 'asset/source', + }, + { + test: /\.(png|svg|jpg|jpeg|ico|ttf|webp|eot|woff|webm|mp4|webp|wav)(\?.*)?$/, + resourceQuery: { not: [/raw/] }, + type: "asset/resource", + }, { test: /(? Date: Wed, 24 Jun 2026 20:47:42 +0530 Subject: [PATCH 2/2] fix: duplicate gradient id in spinner.svg --- src/dialogs/loader.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dialogs/loader.js b/src/dialogs/loader.js index 18af12af1..133b982bd 100644 --- a/src/dialogs/loader.js +++ b/src/dialogs/loader.js @@ -9,6 +9,13 @@ let onCancelCallback = null; let $currentDialog = null; let $currentMask = null; const titleLoaderId = "__title-loader"; +const tailSpinGradientId = "tail-spin-gradient"; +let tailSpinSvgId = 0; + +function createTailSpinSvg() { + const gradientId = `${tailSpinGradientId}-${tailSpinSvgId++}`; + return tailSpinSvg.split(tailSpinGradientId).join(gradientId); +} /** * @typedef {object} LoaderOptions @@ -53,7 +60,7 @@ function create(titleText, message = "", options = {}) { {titleText} - +
+ ); if (!$titleLoader.isConnected) {