feat: gaps in tailwind migration closed
This commit is contained in:
46
assets/js/utils/color.js
Normal file
46
assets/js/utils/color.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { clamp } from "./dom.js";
|
||||
|
||||
export const cssVar = (name, fallback) => {
|
||||
const value = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||||
return value || fallback;
|
||||
};
|
||||
|
||||
const parseRgbColor = (value) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hex = value.match(/^#([0-9a-f]{6})$/i);
|
||||
|
||||
if (hex) {
|
||||
return {
|
||||
r: Number.parseInt(hex[1].slice(0, 2), 16),
|
||||
g: Number.parseInt(hex[1].slice(2, 4), 16),
|
||||
b: Number.parseInt(hex[1].slice(4, 6), 16)
|
||||
};
|
||||
}
|
||||
|
||||
const rgb = value.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
||||
|
||||
if (!rgb) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
r: Number.parseInt(rgb[1], 10),
|
||||
g: Number.parseInt(rgb[2], 10),
|
||||
b: Number.parseInt(rgb[3], 10)
|
||||
};
|
||||
};
|
||||
|
||||
export const normalizeMonacoColor = (value, fallback) => {
|
||||
const rgb = parseRgbColor(value);
|
||||
|
||||
if (!rgb) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return `#${[rgb.r, rgb.g, rgb.b]
|
||||
.map((channel) => clamp(channel, 0, 255).toString(16).padStart(2, "0"))
|
||||
.join("")}`;
|
||||
};
|
||||
34
assets/js/utils/dom.js
Normal file
34
assets/js/utils/dom.js
Normal file
@@ -0,0 +1,34 @@
|
||||
export const clamp = (value, min, max) => Math.max(min, Math.min(value, max));
|
||||
|
||||
export const parseJsonObject = (value) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const setMediaThumbnailLoaded = (image, loaded) => {
|
||||
const thumbnail = image?.closest(".media-thumbnail");
|
||||
|
||||
if (!thumbnail) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
thumbnail.classList.add("is-loaded");
|
||||
} else {
|
||||
thumbnail.classList.remove("is-loaded");
|
||||
}
|
||||
};
|
||||
|
||||
export const syncMediaThumbnailState = (root) => {
|
||||
root.querySelectorAll(".media-thumbnail-image").forEach((image) => {
|
||||
setMediaThumbnailLoaded(image, Boolean(image.complete && image.naturalWidth > 0));
|
||||
});
|
||||
};
|
||||
43
assets/js/utils/layout.js
Normal file
43
assets/js/utils/layout.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { clamp } from "./dom.js";
|
||||
import { SIDEBAR_STORAGE_KEY, ASSISTANT_STORAGE_KEY } from "../constants.js";
|
||||
|
||||
export const shellWidth = (selector) => {
|
||||
const shell = document.querySelector(selector);
|
||||
|
||||
if (!shell) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const width = Number.parseInt(shell.style.width || "0", 10);
|
||||
return Number.isNaN(width) ? Math.round(shell.getBoundingClientRect().width) : width;
|
||||
};
|
||||
|
||||
export const setShellWidth = (selector, width) => {
|
||||
const shell = document.querySelector(selector);
|
||||
|
||||
if (shell) {
|
||||
shell.style.width = `${width}px`;
|
||||
shell.classList.remove("is-hidden");
|
||||
}
|
||||
};
|
||||
|
||||
export const persistWidth = (target, width) => {
|
||||
const key = target === "assistant" ? ASSISTANT_STORAGE_KEY : SIDEBAR_STORAGE_KEY;
|
||||
window.localStorage.setItem(key, String(width));
|
||||
};
|
||||
|
||||
export const readStoredSize = (key, fallback, min, max) => {
|
||||
const raw = window.localStorage.getItem(key);
|
||||
|
||||
if (!raw) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
|
||||
if (Number.isNaN(parsed)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return clamp(parsed, min, max);
|
||||
};
|
||||
33
assets/js/utils/script_loader.js
Normal file
33
assets/js/utils/script_loader.js
Normal file
@@ -0,0 +1,33 @@
|
||||
export const loadScript = (src) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const existing = document.querySelector(`script[src="${src}"]`);
|
||||
|
||||
if (existing) {
|
||||
if (existing.dataset.loaded === "true") {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
existing.addEventListener("load", () => resolve(), { once: true });
|
||||
existing.addEventListener("error", () => reject(new Error(`Failed to load ${src}`)), {
|
||||
once: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = src;
|
||||
script.async = true;
|
||||
script.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
script.dataset.loaded = "true";
|
||||
resolve();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
script.addEventListener("error", () => reject(new Error(`Failed to load ${src}`)), {
|
||||
once: true
|
||||
});
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
30
assets/js/utils/shortcuts.js
Normal file
30
assets/js/utils/shortcuts.js
Normal file
@@ -0,0 +1,30 @@
|
||||
export const normalizeShortcutKey = (key) => String(key || "").toLowerCase();
|
||||
|
||||
export const shortcutTargetIsEditable = (event) => {
|
||||
const tag = event.target?.tagName || null;
|
||||
return event.target?.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(tag);
|
||||
};
|
||||
|
||||
export const shortcutMatchesEvent = (shortcut, event) => {
|
||||
const primary = event.metaKey || event.ctrlKey;
|
||||
|
||||
return (
|
||||
normalizeShortcutKey(event.key) === normalizeShortcutKey(shortcut.key) &&
|
||||
primary === Boolean(shortcut.primary) &&
|
||||
event.shiftKey === Boolean(shortcut.shift) &&
|
||||
event.altKey === Boolean(shortcut.alt)
|
||||
);
|
||||
};
|
||||
|
||||
export const parseShortcutConfig = (value) => {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch (_error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user