@lenml/char-card-reader
Version:
SillyTavern character card info reader
1 lines • 49.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/utils.ts","../src/CharacterBook.ts","../src/MetadataReader.ts","../src/types.ts","../src/CharacterCard.ts"],"sourcesContent":["export function toBase64(\r\n input: Blob | Buffer | ArrayBuffer | Uint8Array\r\n): Promise<string> {\r\n if (typeof Buffer !== \"undefined\" && Buffer.isBuffer(input)) {\r\n // Node.js Buffer\r\n return Promise.resolve(input.toString(\"base64\"));\r\n }\r\n\r\n if (typeof Blob !== \"undefined\" && input instanceof Blob) {\r\n // Browser Blob\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n const result = reader.result as string;\r\n const base64 = result.split(\",\")[1]; // data:*/*;base64,...\r\n resolve(base64);\r\n };\r\n reader.onerror = reject;\r\n reader.readAsDataURL(input);\r\n });\r\n }\r\n\r\n // ArrayBuffer or Uint8Array\r\n let uint8: Uint8Array;\r\n if (input instanceof ArrayBuffer) {\r\n uint8 = new Uint8Array(input);\r\n } else if (input instanceof Uint8Array) {\r\n uint8 = input;\r\n } else {\r\n throw new TypeError(\"Unsupported input type\");\r\n }\r\n\r\n if (typeof Buffer !== \"undefined\") {\r\n // Node.js environment\r\n return Promise.resolve(Buffer.from(uint8).toString(\"base64\"));\r\n } else {\r\n // Browser: use btoa\r\n let binary = \"\";\r\n for (let i = 0; i < uint8.length; i++) {\r\n binary += String.fromCharCode(uint8[i]);\r\n }\r\n return Promise.resolve(btoa(binary));\r\n }\r\n}\r\n\r\nexport function isValidImageUrl(url: any) {\r\n if (typeof url !== \"string\") {\r\n return false;\r\n }\r\n // http 或者 data:\r\n if (\r\n url.startsWith(\"http://\") ||\r\n url.startsWith(\"https://\") ||\r\n url.startsWith(\"data:\")\r\n ) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\nexport const deepClone: <T>(x: T) => T = globalThis.structuredClone\r\n ? globalThis.structuredClone\r\n : <T>(x: T): T => JSON.parse(JSON.stringify(x));\r\n\r\nexport class Base64 {\r\n static encode(input: string): string {\r\n if (typeof window !== \"undefined\" && typeof window.btoa === \"function\") {\r\n return window.btoa(unescape(encodeURIComponent(input)));\r\n } else if (typeof Buffer !== \"undefined\") {\r\n return Buffer.from(input, \"utf-8\").toString(\"base64\");\r\n } else {\r\n throw new Error(\"Base64.encode: Environment not supported.\");\r\n }\r\n }\r\n\r\n static decode(base64: string): string {\r\n if (typeof window !== \"undefined\" && typeof window.atob === \"function\") {\r\n return decodeURIComponent(escape(window.atob(base64)));\r\n } else if (typeof Buffer !== \"undefined\") {\r\n return Buffer.from(base64, \"base64\").toString(\"utf-8\");\r\n } else {\r\n throw new Error(\"Base64.decode: Environment not supported.\");\r\n }\r\n }\r\n}\r\n\r\nfunction isDeepEqual(a: any, b: any) {\r\n if (a === b) {\r\n return true;\r\n }\r\n if (\r\n a === null ||\r\n b === null ||\r\n typeof a !== \"object\" ||\r\n typeof b !== \"object\"\r\n ) {\r\n return false;\r\n }\r\n if (Array.isArray(a) && Array.isArray(b)) {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < a.length; i++) {\r\n if (!isDeepEqual(a[i], b[i])) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n const keys = Object.keys(a);\r\n if (keys.length !== Object.keys(b).length) {\r\n return false;\r\n }\r\n for (const key of keys) {\r\n if (!isDeepEqual(a[key], b[key])) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\nfunction isObject(x: any): x is object {\r\n return typeof x === \"object\" && x !== null;\r\n}\r\nfunction isNoNone(x: any): x is any {\r\n return x !== undefined && x !== null;\r\n}\r\n/**\r\n * Recursively merge objects into a single object.\r\n *\r\n * If all values for a key are objects, merge them recursively.\r\n * If any value for a key is not an object, take the last non-null value.\r\n * @param {...any[]} objects objects to merge\r\n * @returns merged object\r\n */\r\nexport function mergeObjects(...objects: any[]): any {\r\n const keys = new Set(objects.flatMap(Object.keys));\r\n return Object.fromEntries(\r\n Array.from(keys).map((key) => {\r\n const vals = objects.map((x) => x[key]);\r\n if (vals.some(Array.isArray)) {\r\n // NOTE: 直接用最后一个非空,数组没有合并逻辑\r\n return [key, [...vals].reverse().filter(isNoNone).filter(Boolean)[0]];\r\n }\r\n if (vals.some(isObject)) {\r\n return [key, mergeObjects(...vals.filter(isObject))];\r\n }\r\n return [key, [...vals].reverse().filter(isNoNone)[0]];\r\n })\r\n );\r\n}\r\nexport function uniq<T>(arr: T[]): T[] {\r\n return Array.from(new Set(arr));\r\n}\r\n","import { SpecV3 } from \"./spec_types/spec_v3\";\r\nimport { deepClone, uniq } from \"./utils\";\r\n\r\n// lorebook\r\nexport class CharacterBook implements SpecV3.Lorebook {\r\n static from_json(data: any) {\r\n if (typeof data !== \"object\" || data === null) {\r\n throw new Error(\"data must be an object\");\r\n }\r\n let entries: any[] = [];\r\n if (Array.isArray(data)) {\r\n entries = data;\r\n } else {\r\n entries =\r\n // from lorabook.json\r\n Array.isArray(data?.entries)\r\n ? data.entries\r\n : // from character card\r\n Array.isArray(data?.data?.character_book?.entries)\r\n ? data.data.character_book.entries\r\n : [];\r\n }\r\n const book = new CharacterBook(entries);\r\n const character_book =\r\n data?.character_book ?? data?.data?.character_book ?? data;\r\n book.name = character_book?.name;\r\n book.description = character_book?.description;\r\n book.recursive_scanning = character_book?.recursive_scanning ?? true;\r\n book.scan_depth = character_book?.scan_depth ?? 10;\r\n book.extensions = character_book?.extensions ?? {};\r\n return book;\r\n }\r\n\r\n name: string = \"unknown\";\r\n description: string = \"\";\r\n // TODO\r\n token_budget?: number;\r\n recursive_scanning: boolean = true;\r\n extensions: SpecV3.Lorebook[\"extensions\"] = {};\r\n entries: SpecV3.Lorebook[\"entries\"] = [];\r\n scan_depth?: number | undefined = 10;\r\n\r\n constructor(entries: SpecV3.Lorebook[\"entries\"] = []) {\r\n this.entries = deepClone(entries);\r\n this._keys_fix();\r\n }\r\n\r\n public _keys_fix() {\r\n const pattern = /[,|;,;]/g;\r\n // 修复 keys ,有部分情况 keys 错误可能导致搜索出错\r\n for (const entry of this.entries) {\r\n const { keys } = entry;\r\n const fixed_keys: string[] = [];\r\n for (const k of keys) {\r\n if (pattern.test(k)) {\r\n fixed_keys.push(\r\n ...k\r\n .split(pattern)\r\n .map((x) => x.trim())\r\n .filter(Boolean)\r\n );\r\n } else {\r\n fixed_keys.push(k);\r\n }\r\n }\r\n entry.keys = fixed_keys;\r\n }\r\n }\r\n\r\n private _scan(\r\n context: string,\r\n matched: SpecV3.Lorebook[\"entries\"] = [],\r\n current_depth = 1\r\n ): SpecV3.Lorebook[\"entries\"] {\r\n if (current_depth >= (this.scan_depth ?? 10)) {\r\n return uniq(matched);\r\n }\r\n const current_context = [\r\n context,\r\n ...uniq(matched).map((x) => x.content),\r\n ].join(\"\\n\");\r\n const pending_entries = this.entries\r\n .filter((x) => x.content?.trim())\r\n .filter((x) => x.enabled && !matched.includes(x));\r\n if (pending_entries.length === 0) {\r\n return uniq(matched);\r\n }\r\n for (const entry of pending_entries) {\r\n const is_matched = entry.keys.some((k) => current_context.includes(k));\r\n if (is_matched) {\r\n matched.push(entry);\r\n }\r\n }\r\n if (this.recursive_scanning) {\r\n return this._scan(context, matched, current_depth + 1);\r\n }\r\n return uniq(matched);\r\n }\r\n public scan(context: string): SpecV3.Lorebook[\"entries\"] {\r\n const matched = this._scan(context);\r\n const constant = this.entries.filter(\r\n (x) => x.constant && x.enabled && x.content?.trim()\r\n );\r\n return uniq([...matched, ...constant]).sort(\r\n (a, b) => a.insertion_order - b.insertion_order\r\n );\r\n }\r\n}\r\n","import { ParsedMetadata, PngChunk, JpegSegment, WebPChunk } from \"./types\";\r\n\r\nexport function parseImageMetadata(\r\n buffer: ArrayBuffer | Uint8Array\r\n): ParsedMetadata {\r\n const data = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\r\n\r\n const isPng = data\r\n .slice(0, 8)\r\n .every((b, i) => b === [137, 80, 78, 71, 13, 10, 26, 10][i]);\r\n const isJpeg = data[0] === 0xff && data[1] === 0xd8;\r\n const isWebP =\r\n String.fromCharCode(...data.slice(0, 4)) === \"RIFF\" &&\r\n String.fromCharCode(...data.slice(8, 12)) === \"WEBP\";\r\n\r\n if (isPng) {\r\n return {\r\n format: \"png\",\r\n chunks: parsePngChunks(data),\r\n };\r\n }\r\n\r\n if (isJpeg) {\r\n return {\r\n format: \"jpeg\",\r\n segments: parseJpegSegments(data),\r\n };\r\n }\r\n\r\n if (isWebP) {\r\n return {\r\n format: \"webp\",\r\n chunks: parseWebPChunks(data),\r\n };\r\n }\r\n\r\n throw new Error(\"Unsupported image format\");\r\n}\r\n\r\nfunction parsePngChunks(data: Uint8Array): PngChunk[] {\r\n const chunks: PngChunk[] = [];\r\n let offset = 8;\r\n\r\n while (offset < data.length) {\r\n if (offset + 8 > data.length) break;\r\n\r\n const length =\r\n ((data[offset] << 24) |\r\n (data[offset + 1] << 16) |\r\n (data[offset + 2] << 8) |\r\n data[offset + 3]) >>>\r\n 0;\r\n\r\n const type = String.fromCharCode(\r\n data[offset + 4],\r\n data[offset + 5],\r\n data[offset + 6],\r\n data[offset + 7]\r\n );\r\n\r\n const chunkStart = offset + 8;\r\n const chunkEnd = chunkStart + length;\r\n if (chunkEnd + 4 > data.length) break;\r\n\r\n const chunkData = data.slice(chunkStart, chunkEnd);\r\n const crc =\r\n ((data[chunkEnd] << 24) |\r\n (data[chunkEnd + 1] << 16) |\r\n (data[chunkEnd + 2] << 8) |\r\n data[chunkEnd + 3]) >>>\r\n 0;\r\n\r\n const chunk: PngChunk = { type, length, crc };\r\n\r\n if (type === \"IHDR\") {\r\n chunk.width =\r\n ((chunkData[0] << 24) |\r\n (chunkData[1] << 16) |\r\n (chunkData[2] << 8) |\r\n chunkData[3]) >>>\r\n 0;\r\n chunk.height =\r\n ((chunkData[4] << 24) |\r\n (chunkData[5] << 16) |\r\n (chunkData[6] << 8) |\r\n chunkData[7]) >>>\r\n 0;\r\n chunk.bitDepth = chunkData[8];\r\n chunk.colorType = chunkData[9];\r\n } else if (type === \"tEXt\") {\r\n const text = new TextDecoder().decode(chunkData);\r\n const sep = text.indexOf(\"\\0\");\r\n if (sep >= 0) {\r\n chunk.keyword = text.slice(0, sep);\r\n chunk.text = text.slice(sep + 1);\r\n } else {\r\n chunk.rawText = text;\r\n }\r\n }\r\n\r\n chunks.push(chunk);\r\n offset = chunkEnd + 4;\r\n }\r\n\r\n return chunks;\r\n}\r\nfunction parseJpegSegments(data: Uint8Array): JpegSegment[] {\r\n const segments: JpegSegment[] = [];\r\n\r\n let offset = 2;\r\n while (offset < data.length) {\r\n if (data[offset] !== 0xff)\r\n throw new Error(`Invalid marker at offset ${offset}`);\r\n\r\n let marker = data[offset + 1];\r\n while (marker === 0xff) {\r\n offset++;\r\n marker = data[offset + 1];\r\n }\r\n\r\n const markerOffset = offset;\r\n offset += 2;\r\n\r\n if (marker === 0xd9 || marker === 0xda) break;\r\n\r\n const length = (data[offset] << 8) | data[offset + 1];\r\n const payloadStart = offset + 2;\r\n const payloadEnd = payloadStart + length - 2;\r\n const segmentData = data.slice(payloadStart, payloadEnd);\r\n\r\n const info: JpegSegment = {\r\n marker: `FF ${marker.toString(16).toUpperCase().padStart(2, \"0\")}`,\r\n offset: markerOffset,\r\n length,\r\n type: \"Other\",\r\n preview: Array.from(segmentData.slice(0, 16))\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\" \"),\r\n };\r\n\r\n if (\r\n marker === 0xe0 &&\r\n String.fromCharCode(...segmentData.slice(0, 5)) === \"JFIF\\0\"\r\n ) {\r\n info.type = \"JFIF\";\r\n } else if (marker === 0xe1) {\r\n const id = String.fromCharCode(...segmentData.slice(0, 6));\r\n if (id.startsWith(\"Exif\")) info.type = \"EXIF\";\r\n else if (\r\n String.fromCharCode(...segmentData.slice(0, 29)).includes(\r\n \"http://ns.adobe.com/xap/1.0/\"\r\n )\r\n ) {\r\n info.type = \"XMP\";\r\n } else info.type = \"APP1\";\r\n } else if (marker === 0xfe) {\r\n info.type = \"Comment\";\r\n info.comment = new TextDecoder().decode(segmentData);\r\n }\r\n\r\n segments.push(info);\r\n offset = payloadEnd;\r\n }\r\n\r\n return segments;\r\n}\r\nfunction parseWebPChunks(data: Uint8Array): WebPChunk[] {\r\n const chunks: WebPChunk[] = [];\r\n\r\n let offset = 12; // skip RIFF header (12 bytes)\r\n const len = data.length;\r\n\r\n while (offset + 8 <= len) {\r\n const type = String.fromCharCode(\r\n data[offset],\r\n data[offset + 1],\r\n data[offset + 2],\r\n data[offset + 3]\r\n );\r\n\r\n const chunkLength =\r\n data[offset + 4] |\r\n (data[offset + 5] << 8) |\r\n (data[offset + 6] << 16) |\r\n (data[offset + 7] << 24);\r\n\r\n const payloadStart = offset + 8;\r\n const payloadEnd = payloadStart + chunkLength;\r\n\r\n if (payloadEnd > len) break;\r\n\r\n chunks.push({\r\n type,\r\n offset,\r\n length: chunkLength,\r\n preview: Array.from(data.slice(payloadStart, payloadStart + 16))\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\" \"),\r\n });\r\n\r\n // chunks are padded to even sizes\r\n offset = payloadEnd + (chunkLength % 2);\r\n }\r\n\r\n return chunks;\r\n}\r\n\r\nexport function extractUserCommentFromWebPChunk(\r\n data: Uint8Array,\r\n chunk: WebPChunk\r\n) {\r\n const offset = chunk.offset + 8;\r\n // TIFF header starts at EXIF offset\r\n const byteOrder = String.fromCharCode(data[offset], data[offset + 1]);\r\n const littleEndian = byteOrder === \"II\";\r\n const readU16 = (off: number) =>\r\n littleEndian\r\n ? data[off] | (data[off + 1] << 8)\r\n : (data[off] << 8) | data[off + 1];\r\n const readU32 = (off: number) =>\r\n littleEndian\r\n ? data[off] |\r\n (data[off + 1] << 8) |\r\n (data[off + 2] << 16) |\r\n (data[off + 3] << 24)\r\n : (data[off] << 24) |\r\n (data[off + 1] << 16) |\r\n (data[off + 2] << 8) |\r\n data[off + 3];\r\n\r\n const tiffOffset = offset;\r\n const firstIFDOffset = readU32(offset + 4);\r\n const ifdOffset = tiffOffset + firstIFDOffset;\r\n const numEntries = readU16(ifdOffset);\r\n\r\n for (let i = 0; i < numEntries; i++) {\r\n const entryOffset = ifdOffset + 2 + i * 12;\r\n const tag = readU16(entryOffset);\r\n const type = readU16(entryOffset + 2);\r\n const count = readU32(entryOffset + 4);\r\n const valueOffset = entryOffset + 8;\r\n\r\n if (tag === 0x9286) {\r\n // UserComment\r\n let valuePtr = readU32(valueOffset);\r\n if (count <= 4) {\r\n valuePtr = valueOffset; // value is embedded directly\r\n } else {\r\n valuePtr = tiffOffset + valuePtr;\r\n }\r\n\r\n const raw = data.slice(valuePtr, valuePtr + count);\r\n // Skip known EXIF encodings\r\n const asciiPrefix = \"ASCII\\0\\0\\0\";\r\n const utf8Prefix = \"UTF8\\0\\0\\0\";\r\n const header = String.fromCharCode(...raw.slice(0, 8));\r\n\r\n let comment = \"\";\r\n\r\n if (header.startsWith(asciiPrefix) || header.startsWith(utf8Prefix)) {\r\n comment = new TextDecoder(\"utf-8\").decode(raw.slice(8));\r\n } else {\r\n // fallback: try decode full raw\r\n comment = new TextDecoder(\"utf-8\").decode(raw);\r\n }\r\n\r\n return comment;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n","export namespace CharacterSpec {\r\n export interface Root {\r\n spec: string;\r\n spec_version: string;\r\n data: Data;\r\n name: string;\r\n description: string;\r\n personality: string;\r\n scenario: string;\r\n first_mes: string;\r\n mes_example: string;\r\n creatorcomment: string;\r\n avatar: string;\r\n talkativeness: string;\r\n fav: boolean;\r\n tags: any[];\r\n create_date: string;\r\n }\r\n\r\n export interface Data {\r\n name: string;\r\n description: string;\r\n personality: string;\r\n scenario: string;\r\n first_mes: string;\r\n mes_example: string;\r\n creator_notes: string;\r\n system_prompt: string;\r\n post_history_instructions: string;\r\n tags: string[];\r\n creator: string;\r\n character_version: string;\r\n alternate_greetings: string[];\r\n extensions: Extensions;\r\n group_only_greetings: any[];\r\n character_book: CharacterBook;\r\n }\r\n\r\n export interface Extensions {\r\n talkativeness: string;\r\n fav: boolean;\r\n world: string;\r\n depth_prompt: DepthPrompt;\r\n }\r\n\r\n export interface DepthPrompt {\r\n prompt: string;\r\n depth: number;\r\n role: string;\r\n }\r\n\r\n export interface CharacterBook {\r\n entries: Entry[];\r\n name: string;\r\n extensions: Record<string, any>;\r\n }\r\n\r\n export interface Entry {\r\n id: number;\r\n keys: string[];\r\n secondary_keys: any[];\r\n comment: string;\r\n content: string;\r\n constant: boolean;\r\n selective: boolean;\r\n insertion_order: number;\r\n enabled: boolean;\r\n position: \"before_char\" | \"after_char\";\r\n use_regex: boolean;\r\n extensions: Extensions2;\r\n }\r\n\r\n export interface Extensions2 {\r\n position: number;\r\n exclude_recursion: boolean;\r\n display_index: number;\r\n probability: number;\r\n useProbability: boolean;\r\n depth: number;\r\n selectiveLogic: number;\r\n group: string;\r\n group_override: boolean;\r\n group_weight: number;\r\n prevent_recursion: boolean;\r\n delay_until_recursion: boolean;\r\n scan_depth: any;\r\n match_whole_words: any;\r\n use_group_scoring: boolean;\r\n case_sensitive: any;\r\n automation_id: string;\r\n role: number;\r\n vectorized: boolean;\r\n sticky: number;\r\n cooldown: number;\r\n delay: number;\r\n match_persona_description: boolean;\r\n match_character_description: boolean;\r\n match_character_personality: boolean;\r\n match_character_depth_prompt: boolean;\r\n match_scenario: boolean;\r\n match_creator_notes: boolean;\r\n }\r\n}\r\n\r\ntype PngChunkType =\r\n | \"IHDR\"\r\n | \"sRGB\"\r\n | \"gAMA\"\r\n | \"pHYs\"\r\n | \"tEXt\"\r\n | \"IDAT\"\r\n | \"IEND\";\r\n\r\nexport type PngChunk = {\r\n type: PngChunkType | ({} & string);\r\n length: number;\r\n crc: number;\r\n\r\n // type === \"IHDR\"\r\n width?: number;\r\n height?: number;\r\n bitDepth?: number;\r\n colorType?: number;\r\n\r\n // type === \"tEXt\"\r\n text?: string;\r\n keyword?: string;\r\n rawText?: string;\r\n\r\n [key: string]: any;\r\n};\r\n\r\nexport type JpegSegment = {\r\n marker: string;\r\n offset: number;\r\n length: number;\r\n type: string;\r\n preview: string;\r\n comment?: string;\r\n};\r\nexport type WebPChunk = {\r\n type: string;\r\n offset: number;\r\n length: number;\r\n preview: string;\r\n};\r\n\r\nexport type ParsedMetadata =\r\n | { format: \"png\"; chunks: PngChunk[] }\r\n | { format: \"jpeg\"; segments: JpegSegment[] }\r\n | { format: \"webp\"; chunks: WebPChunk[] };\r\n\r\nexport type CharRawData = {\r\n spec: string;\r\n spec_version: string;\r\n data: any;\r\n [key: string]: any;\r\n};\r\n\r\nexport class CharacterCardParserError extends Error {}\r\n","import { CharacterBook } from \"./CharacterBook\";\nimport {\n extractUserCommentFromWebPChunk,\n parseImageMetadata,\n} from \"./MetadataReader\";\nimport { SpecV1 } from \"./spec_types/spec_v1\";\nimport { SpecV2 } from \"./spec_types/spec_v2\";\nimport { SpecV3 } from \"./spec_types/spec_v3\";\nimport { CharacterCardParserError, CharRawData, ParsedMetadata } from \"./types\";\nimport { CharacterSpec } from \"./types\";\nimport {\n Base64,\n deepClone,\n isValidImageUrl,\n mergeObjects,\n toBase64,\n} from \"./utils\";\n\nexport class CharacterCard {\n static async from_file(file: ArrayBuffer | Uint8Array) {\n const exif_data = parseImageMetadata(file);\n const image_b64: string = await toBase64(file);\n const fallback_avatar = `data:image/${exif_data.format};base64,${image_b64}`;\n const raw_data = this.parse_char_info(file, exif_data);\n return new CharacterCard(\n {\n // default as v1\n spec: \"chara_card_v1\",\n spec_version: \"1.0\",\n data: {},\n ...raw_data,\n },\n fallback_avatar\n );\n }\n\n static from_json(raw_data: CharRawData, fallback_avatar = \"\") {\n return new CharacterCard(raw_data, fallback_avatar);\n }\n\n static parse_char_info(file: ArrayBuffer | Uint8Array, exif: ParsedMetadata) {\n let encoded_text: string | undefined;\n\n // NOTE: About CCV3 keyword checkout this\n // https://github.com/kwaroran/character-card-spec-v3/blob/f3a86af019fbd99f788f7a1155f399655b34ab35/SPEC_V3.md?plain=1#L22-L30\n if (exif.format === \"png\") {\n const encoded_ccv_1_2 = exif.chunks.find((x) => x.keyword === \"chara\");\n const encoded_ccv_3 = exif.chunks.find((x) => x.keyword === \"ccv3\");\n\n encoded_text = encoded_ccv_3?.text ?? encoded_ccv_1_2?.text;\n } else if (exif.format === \"jpeg\") {\n const encoded_ccv_1_2 = exif.segments.find((x) => x.marker === \"chara\");\n const encoded_ccv_3 = exif.segments.find((x) => x.marker === \"ccv3\");\n\n encoded_text = encoded_ccv_3?.comment ?? encoded_ccv_1_2?.comment;\n } else if (exif.format === \"webp\") {\n const exifChunk = exif.chunks.find((x) => x.type === \"EXIF\");\n if (exifChunk) {\n const exifData = extractUserCommentFromWebPChunk(\n file instanceof Uint8Array ? file : new Uint8Array(file),\n exifChunk\n );\n encoded_text = exifData;\n }\n }\n\n if (!encoded_text) {\n throw new CharacterCardParserError(\n \"Failed to extract chara card data from image\"\n );\n }\n\n const json_str = Base64.decode(encoded_text);\n const json = JSON.parse(json_str);\n return json;\n }\n\n constructor(readonly raw_data: CharRawData, readonly fallback_avatar = \"\") {}\n\n async get_avatar(without_fallback = false): Promise<string> {\n const fallback = without_fallback ? \"\" : this.fallback_avatar;\n return [this.raw_data.avatar, this.raw_data.data?.avatar, fallback].filter(\n isValidImageUrl\n )[0];\n }\n\n get avatar(): CharacterSpec.Root[\"avatar\"] {\n return [\n this.raw_data.avatar,\n this.raw_data.data?.avatar,\n this.fallback_avatar,\n ].filter(isValidImageUrl)[0];\n }\n\n get spec(): CharacterSpec.Root[\"spec\"] {\n return this.raw_data.spec || \"unknown\";\n }\n\n get spec_version(): CharacterSpec.Root[\"spec_version\"] {\n return this.raw_data.spec_version || \"unknown\";\n }\n\n get name(): CharacterSpec.Data[\"name\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.name ?? this.raw_data.name;\n case \"chara_card_v3\":\n return this.raw_data.data?.name ?? this.raw_data.name;\n default:\n return this.raw_data.char_name ?? this.raw_data.name ?? \"unknown\";\n }\n }\n\n get description(): CharacterSpec.Data[\"description\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.description ?? this.raw_data.description;\n case \"chara_card_v3\":\n return this.raw_data.data?.description ?? this.raw_data.description;\n default:\n return this.raw_data.description ?? \"unknown\";\n }\n }\n\n get first_message(): CharacterSpec.Data[\"first_mes\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.first_mes ?? this.raw_data.first_mes;\n case \"chara_card_v3\":\n return this.raw_data.data?.first_mes ?? this.raw_data.first_mes;\n default:\n return this.raw_data.first_mes ?? \"unknown\";\n }\n }\n\n get message_example(): CharacterSpec.Root[\"mes_example\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.mes_example ?? this.raw_data.mes_example;\n case \"chara_card_v3\":\n return this.raw_data.data?.mes_example ?? this.raw_data.mes_example;\n default:\n return this.raw_data.mes_example ?? \"unknown\";\n }\n }\n\n get create_date(): CharacterSpec.Root[\"create_date\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.create_date ?? this.raw_data.create_date;\n case \"chara_card_v3\":\n return this.raw_data.data?.create_date ?? this.raw_data.create_date;\n default:\n return this.raw_data.create_date ?? \"unknown\";\n }\n }\n\n get personality(): CharacterSpec.Data[\"personality\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.personality ?? this.raw_data.personality;\n case \"chara_card_v3\":\n return this.raw_data.data?.personality ?? this.raw_data.personality;\n default:\n return this.raw_data.personality ?? \"unknown\";\n }\n }\n\n get scenario(): CharacterSpec.Data[\"scenario\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.scenario ?? this.raw_data.scenario;\n case \"chara_card_v3\":\n return this.raw_data.data?.scenario ?? this.raw_data.scenario;\n default:\n return this.raw_data.scenario ?? \"unknown\";\n }\n }\n\n get alternate_greetings(): CharacterSpec.Data[\"alternate_greetings\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.alternate_greetings;\n case \"chara_card_v3\":\n return this.raw_data.data?.alternate_greetings;\n default:\n return [];\n }\n }\n\n get character_book(): CharacterSpec.CharacterBook {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.character_book;\n case \"chara_card_v3\":\n return this.raw_data.data?.character_book;\n default:\n return {\n entries: [],\n name: this.name,\n extensions: {},\n };\n }\n }\n\n get tags(): CharacterSpec.Data[\"tags\"] {\n switch (this.spec) {\n case \"chara_card_v2\":\n return this.raw_data.data?.tags;\n case \"chara_card_v3\":\n return this.raw_data.data?.tags;\n default:\n return [];\n }\n }\n\n /**\n * Converts the current character card data to the SpecV1 format.\n *\n * This method constructs a SpecV1.TavernCard object by extracting the necessary\n * fields from the current instance's raw data using a getter function. The function\n * retrieves data from multiple sources, including instance properties, the raw data\n * object, and its nested data object. The resulting object contains fields defined\n * in the chara_card_v1 specification, such as name, description, personality, scenario,\n * first message, and example messages.\n *\n * @returns A SpecV1.TavernCard object representing the character card data in SpecV1 format.\n */\n\n public toSpecV1(): SpecV1.TavernCard {\n const getter = (key: string) =>\n (this as any)[key] ?? this.raw_data[key] ?? this.raw_data.data?.[key];\n return {\n name: getter(\"name\") ?? getter(\"char_name\"),\n description: getter(\"description\"),\n personality: getter(\"personality\"),\n scenario: getter(\"scenario\"),\n first_mes: getter(\"first_mes\"),\n mes_example: getter(\"mes_example\"),\n };\n }\n\n /**\n * Converts the current character card data to the SpecV2 format.\n *\n * This method constructs a SpecV2.TavernCardV2 object by extracting the necessary\n * fields from the current instance's raw data using a getter function. The function\n * retrieves data from multiple sources, including instance properties, the raw data\n * object, and its nested data object. The resulting object contains fields defined\n * in the chara_card_v2 specification, including additional fields introduced in\n * later updates.\n *\n * @returns A deep-cloned SpecV2.TavernCardV2 object representing the character card\n * data in SpecV2 format.\n */\n\n public toSpecV2(): SpecV2.TavernCardV2 {\n const getter = (key: string) =>\n (this as any)[key] ?? this.raw_data[key] ?? this.raw_data.data?.[key];\n return deepClone({\n spec: \"chara_card_v2\",\n spec_version: \"2.0\",\n data: {\n // fields from CCV2\n name: getter(\"name\") ?? getter(\"char_name\"),\n description: getter(\"description\"),\n mes_example: getter(\"mes_example\"),\n first_mes: getter(\"first_mes\"),\n personality: getter(\"personality\"),\n scenario: getter(\"scenario\"),\n // New fields start here\n creator_notes: getter(\"creator_notes\"),\n system_prompt: getter(\"system_prompt\"),\n post_history_instructions: getter(\"post_history_instructions\"),\n alternate_greetings: getter(\"alternate_greetings\"),\n character_book: getter(\"character_book\"),\n // May 8th additions\n tags: getter(\"tags\"),\n creator: getter(\"creator\"),\n character_version: getter(\"character_version\"),\n extensions: getter(\"extensions\"),\n },\n });\n }\n\n /**\n * Converts the current character card data to the SpecV3 format.\n *\n * This function utilizes a getter to retrieve properties from the\n * character card's raw data and returns a deep-cloned object\n * conforming to the SpecV3.CharacterCardV3 structure. It includes\n * fields from the CCV2 specification, changes specific to CCV3,\n * and new fields introduced in CCV3.\n *\n * @returns A deep-cloned object representing the character card data\n * in SpecV3 format.\n */\n\n public toSpecV3(): SpecV3.CharacterCardV3 {\n const getter = (key: string) =>\n (this as any)[key] ?? this.raw_data[key] ?? this.raw_data.data?.[key];\n return deepClone({\n spec: \"chara_card_v3\",\n spec_version: \"3.0\",\n data: {\n // fields from CCV2\n name: getter(\"name\") ?? getter(\"char_name\"),\n description: getter(\"description\"),\n tags: getter(\"tags\"),\n creator: getter(\"creator\"),\n character_version: getter(\"character_version\"),\n mes_example: getter(\"mes_example\"),\n extensions: getter(\"extensions\"),\n system_prompt: getter(\"system_prompt\"),\n post_history_instructions: getter(\"post_history_instructions\"),\n first_mes: getter(\"first_mes\"),\n alternate_greetings: getter(\"alternate_greetings\"),\n personality: getter(\"personality\"),\n scenario: getter(\"scenario\"),\n //Changes from CCV2\n creator_notes: getter(\"creator_notes\"),\n character_book: getter(\"character_book\"),\n //New fields in CCV3\n assets: getter(\"assets\"),\n nickname: getter(\"nickname\"),\n creator_notes_multilingual: getter(\"creator_notes_multilingual\"),\n source: getter(\"source\"),\n group_only_greetings: getter(\"group_only_greetings\"),\n creation_date: getter(\"create_date\") ?? getter(\"creation_date\"),\n modification_date: getter(\"modify_date\") ?? getter(\"modification_date\"),\n },\n });\n }\n\n /**\n * Returns the maximum compatible version of the character card\n *\n * this card => merge(v1,v2,v3);\n */\n public toMaxCompatibleSpec():\n | SpecV3.CharacterCardV3\n | SpecV2.TavernCardV2\n | SpecV1.TavernCard {\n return mergeObjects(this.toSpecV1(), this.toSpecV2(), this.toSpecV3());\n }\n\n /**\n * Creates a clone of the current CharacterCard instance in the specified version format.\n *\n * This method generates a new CharacterCard object with the data formatted to match\n * the specified version's specification. It supports conversion to SpecV1, SpecV2, and\n * SpecV3 formats by utilizing the respective `toSpecV1`, `toSpecV2`, and `toSpecV3` methods.\n *\n * @param version - The specification version (\"v1\", \"v2\", or \"v3\") to clone the character card into.\n * Defaults to \"v3\" if not specified.\n *\n * @returns A new CharacterCard instance formatted according to the specified version.\n *\n * @throws Will throw an error if the specified version is unsupported.\n */\n\n public clone(version = \"v3\" as \"v1\" | \"v2\" | \"v3\") {\n let new_raw_data = null;\n switch (version) {\n case \"v1\": {\n new_raw_data = this.toSpecV1();\n break;\n }\n case \"v2\": {\n new_raw_data = this.toSpecV2();\n break;\n }\n case \"v3\": {\n new_raw_data = this.toSpecV3();\n break;\n }\n default: {\n throw new Error(`Unsupported version ${version}`);\n }\n }\n return CharacterCard.from_json({\n spec: \"chara_card_v1\",\n spec_version: \"1.0\",\n data: {},\n ...new_raw_data,\n });\n }\n\n public get_book() {\n return CharacterBook.from_json(this.raw_data);\n }\n}\n"],"mappings":"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","names":["toBase64","input","resolve","reject","reader","base64","uint8","binary","i","isValidImageUrl","url","deepClone","x","Base64","isObject","x","isNoNone","mergeObjects","objects","keys","key","vals","uniq","arr","CharacterBook","_CharacterBook","data","entries","book","character_book","deepClone","pattern","entry","keys","fixed_keys","k","x","context","matched","current_depth","uniq","current_context","pending_entries","constant","a","b","parseImageMetadata","buffer","data","isPng","b","i","isJpeg","isWebP","parsePngChunks","parseJpegSegments","parseWebPChunks","chunks","offset","length","type","chunkStart","chunkEnd","chunkData","crc","chunk","text","sep","segments","marker","markerOffset","payloadStart","payloadEnd","segmentData","info","len","chunkLength","extractUserCommentFromWebPChunk","littleEndian","readU16","off","readU32","tiffOffset","firstIFDOffset","ifdOffset","numEntries","entryOffset","tag","count","valueOffset","valuePtr","raw","asciiPrefix","utf8Prefix","header","comment","CharacterCardParserError","CharacterCard","_CharacterCard","raw_data","fallback_avatar","file","exif_data","parseImageMetadata","image_b64","toBase64","exif","encoded_text","encoded_ccv_1_2","x","exifChunk","extractUserCommentFromWebPChunk","CharacterCardParserError","json_str","Base64","without_fallback","fallback","isValidImageUrl","getter","key","deepClone","mergeObjects","version","new_raw_data","CharacterBook"]}