feat(web): role colors with picker, Material 3 draw, effects and inline markdown
This commit is contained in:
1 parent
bef8898cd8
commit
3b062687cd
20 files changed
+688
-83
No files matched your search
@@ -0,0 +1,45 @@
|
||||
export interface Hsv { h: number; s: number; v: number }
|
||||
|
||||
const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));
|
||||
const byte = (value: number) => Math.round(clamp(value, 0, 255)).toString(16).padStart(2, '0');
|
||||
|
||||
/** "#rgb" / "#rrggbb"(大小写均可)转成小写 "#rrggbb",其他返回 undefined */
|
||||
export function parseHex(text: string): string | undefined {
|
||||
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(text.trim());
|
||||
if (!m) return undefined;
|
||||
const hex = m[1]!.toLowerCase();
|
||||
return `#${hex.length === 3 ? [...hex].map((c) => c + c).join('') : hex}`;
|
||||
}
|
||||
|
||||
export function hexToHsv(hex: string): Hsv {
|
||||
const value = parseHex(hex) ?? '#000000';
|
||||
const r = parseInt(value.slice(1, 3), 16) / 255; const g = parseInt(value.slice(3, 5), 16) / 255; const b = parseInt(value.slice(5, 7), 16) / 255;
|
||||
const max = Math.max(r, g, b); const delta = max - Math.min(r, g, b);
|
||||
let h = 0;
|
||||
if (delta) {
|
||||
if (max === r) h = ((g - b) / delta) % 6;
|
||||
else if (max === g) h = (b - r) / delta + 2;
|
||||
else h = (r - g) / delta + 4;
|
||||
h = (h * 60 + 360) % 360;
|
||||
}
|
||||
return { h, s: max ? delta / max : 0, v: max };
|
||||
}
|
||||
|
||||
export function hsvToHex({ h, s, v }: Hsv): string {
|
||||
const f = (n: number) => { const k = (n + h / 60) % 6; return v - v * s * Math.max(0, Math.min(k, 4 - k, 1)); };
|
||||
return `#${byte(f(5) * 255)}${byte(f(3) * 255)}${byte(f(1) * 255)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一段文字里取出颜色:兼容在线渐变生成器复制出来的 CSS(linear-gradient(90deg, #ff8a3d 0%, rgb(240,54,107) 100%) 之类)。
|
||||
* 按出现顺序最多取 max 个,透明度忽略。
|
||||
*/
|
||||
export function extractColors(text: string, max = 5): string[] {
|
||||
const found: string[] = [];
|
||||
const re = /#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3})\b|rgba?\(\s*(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})/gi;
|
||||
for (let m = re.exec(text); m && found.length < max; m = re.exec(text)) {
|
||||
if (m[1]) found.push(parseHex(m[1].length === 8 ? m[1].slice(0, 6) : m[1])!);
|
||||
else found.push(`#${byte(Number(m[2]))}${byte(Number(m[3]))}${byte(Number(m[4]))}`);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
Reference in new issue
Block a user