mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 16:30:27 +00:00
- Add Exclusions heading section in settings - Replace activeDocument calls with Obsidian helper functions (createEl, createDiv, createSpan, createFragment) - Standardize file monitoring disclaimers with icon grid layout - Add template warning for local models with external documentation link - Consolidate file disclaimer rendering logic with clickable help links - Remove redundant tooltip enum entry - Add new CSS classes for icon grid layouts
146 lines
No EOL
4.9 KiB
TypeScript
146 lines
No EOL
4.9 KiB
TypeScript
import { Exception } from "./Exception";
|
|
|
|
export abstract class StringTools {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment -- regex-parser is a CommonJS module without ESM support
|
|
private static RegexParser: (input: string) => RegExp = require("regex-parser");
|
|
|
|
public static isValidJson(str: string): boolean {
|
|
try {
|
|
JSON.parse(str);
|
|
} catch {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static dateToString(date: Date, includeTime: boolean = true): string {
|
|
if (includeTime) {
|
|
return date.toLocaleString('sv-SE', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
}).replace(/[:\s]/g, '-');
|
|
} else {
|
|
return date.toLocaleDateString('sv-SE', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit'
|
|
}).replace(/[:\s]/g, '-');
|
|
}
|
|
}
|
|
|
|
public static escapeRegex(string: string): string {
|
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|
|
|
|
// Builds a regex from a string that matches flexibly on whitespace but strictly on all other characters.
|
|
public static toWhitespaceFlexibleRegex(input: string): RegExp {
|
|
const pattern = this.escapeRegex(input).replace(/(\\\s|\s)+/g, "\\s+");
|
|
return new RegExp(pattern);
|
|
}
|
|
|
|
public static asRegex(input: string, requiredFlags: string[]): RegExp | null {
|
|
let regex: RegExp;
|
|
|
|
try {
|
|
regex = this.RegexParser(input);
|
|
let flags = regex.flags;
|
|
|
|
for (const requiredFlag of requiredFlags) {
|
|
if (!flags.includes(requiredFlag)) {
|
|
flags = flags + requiredFlag;
|
|
}
|
|
}
|
|
|
|
regex = new RegExp(regex.source, flags);
|
|
} catch {
|
|
try { // If parsing fails, escape the input and use required flags
|
|
regex = new RegExp(StringTools.escapeRegex(input), requiredFlags.join(""));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return regex;
|
|
}
|
|
|
|
public static toBase64(text: string): string {
|
|
const bytes = new TextEncoder().encode(text);
|
|
let binary = "";
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return btoa(binary);
|
|
}
|
|
|
|
public static toBytes(input: string): Uint8Array {
|
|
return new Uint8Array(this.toBuffer(input));
|
|
}
|
|
|
|
public static toBuffer(input: string): ArrayBuffer {
|
|
const binaryString = atob(input);
|
|
const buffer = new ArrayBuffer(binaryString.length);
|
|
const bytes = new Uint8Array(buffer);
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
public static async computeSHA256Hash(base64: string): Promise<string> {
|
|
const buffer = this.toBuffer(base64);
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
public static async resizeB64Image(base64: string, mimeType: string, maxWidth: number = 1000, maxHeight: number = 1000): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image();
|
|
|
|
img.onload = () => {
|
|
let width = img.width;
|
|
let height = img.height;
|
|
|
|
if (width > maxWidth || height > maxHeight) {
|
|
const aspectRatio = width / height;
|
|
if (width > height) {
|
|
width = maxWidth;
|
|
height = maxWidth / aspectRatio;
|
|
} else {
|
|
height = maxHeight;
|
|
width = maxHeight * aspectRatio;
|
|
}
|
|
}
|
|
|
|
const canvas = createEl("canvas");
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
const context = canvas.getContext("2d");
|
|
|
|
if (!context) {
|
|
reject(Exception.new("Failed to get canvas context"));
|
|
return;
|
|
}
|
|
|
|
context.drawImage(img, 0, 0, width, height);
|
|
|
|
const dataURL = canvas.toDataURL(mimeType, 0.92);
|
|
const base64Only = dataURL.split(',')[1];
|
|
resolve(base64Only);
|
|
};
|
|
|
|
img.onerror = () => reject(Exception.new("Failed to load image"));
|
|
|
|
if (!base64.startsWith('data:')) {
|
|
base64 = `data:${mimeType};base64,${base64}`;
|
|
}
|
|
img.src = base64;
|
|
});
|
|
}
|
|
|
|
}
|