[feat] Add support for chat stickers
This commit adds support for chat stickers by allowing users to use stickers in chat message window. The users can toggle show additional asset list using a button. Added Additional assets file extension data. Additional assets list shows preview. Optimized render when use streaming api. (prevent markdown again when message not changed) Added controls to Video/Audio Assets
This commit is contained in:
@@ -329,7 +329,7 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array, mode?:'hub'|'
|
||||
let customScripts:customscript[] = []
|
||||
let utilityBot = false
|
||||
let sdData = defaultSdDataFunc()
|
||||
let extAssets:[string,string][] = []
|
||||
let extAssets:[string,string,string][] = []
|
||||
|
||||
if(risuext){
|
||||
if(risuext.emotions){
|
||||
@@ -350,8 +350,11 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array, mode?:'hub'|'
|
||||
msg: `Loading... (Getting Assets ${i} / ${risuext.additionalAssets.length})`
|
||||
})
|
||||
await sleep(10)
|
||||
const imgp = await saveAsset(mode === 'hub' ? (await getHubResources(risuext.additionalAssets[i][1])) :Buffer.from(risuext.additionalAssets[i][1], 'base64'))
|
||||
extAssets.push([risuext.additionalAssets[i][0],imgp])
|
||||
let fileName = ''
|
||||
if(risuext.additionalAssets[i].length >= 3)
|
||||
fileName = risuext.additionalAssets[i][2]
|
||||
const imgp = await saveAsset(mode === 'hub' ? (await getHubResources(risuext.additionalAssets[i][1])) :Buffer.from(risuext.additionalAssets[i][1], 'base64'), '', fileName)
|
||||
extAssets.push([risuext.additionalAssets[i][0],imgp,fileName])
|
||||
}
|
||||
}
|
||||
bias = risuext.bias ?? bias
|
||||
@@ -756,7 +759,7 @@ type CharacterCardV2 = {
|
||||
customScripts?:customscript[]
|
||||
utilityBot?: boolean,
|
||||
sdData?:[string,string][],
|
||||
additionalAssets?:[string,string][],
|
||||
additionalAssets?:[string,string,string][],
|
||||
backgroundHTML?:string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ export async function ParseMarkdown(data:string, char:(character | groupChat) =
|
||||
for(const asset of char.additionalAssets){
|
||||
const assetPath = await getFileSrc(asset[1])
|
||||
data = data.replaceAll(`{{raw::${asset[0]}}}`, assetPath).
|
||||
replaceAll(`{{img::${asset[0]}}}`,`<img src="${assetPath}" />`)
|
||||
.replaceAll(`{{video::${asset[0]}}}`,`<video autoplay loop><source src="${assetPath}" type="video/mp4"></video>`)
|
||||
.replaceAll(`{{audio::${asset[0]}}}`,`<audio autoplay loop><source src="${assetPath}" type="audio/mpeg"></audio>`)
|
||||
replaceAll(`{{img::${asset[0]}}}`,`<img src="${assetPath}" alt="${asset[0]}"/>`)
|
||||
.replaceAll(`{{video::${asset[0]}}}`,`<video controls autoplay loop><source src="${assetPath}" type="video/mp4"></video>`)
|
||||
.replaceAll(`{{audio::${asset[0]}}}`,`<audio controls autoplay loop><source src="${assetPath}" type="audio/mpeg"></audio>`)
|
||||
if(mode === 'back'){
|
||||
data = data.replaceAll(`{{bg::${asset[0]}}}`, `<div style="width:100%;height:100%;background: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),url(${assetPath}); background-size: cover;"></div>`)
|
||||
}
|
||||
|
||||
@@ -404,6 +404,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
|
||||
else if(req.type === 'streaming'){
|
||||
const reader = req.result.getReader()
|
||||
const msgIndex = db.characters[selectedChar].chats[selectedChat].message.length
|
||||
db.characters[selectedChar].chats[selectedChat].isStreaming = true
|
||||
db.characters[selectedChar].chats[selectedChat].message.push({
|
||||
role: 'char',
|
||||
data: "",
|
||||
@@ -419,6 +420,8 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
|
||||
setDatabase(db)
|
||||
}
|
||||
if(readed.done){
|
||||
db.characters[selectedChar].chats[selectedChat].isStreaming = false
|
||||
setDatabase(db)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ export interface character{
|
||||
VOLUME_SCALE?: number
|
||||
}
|
||||
supaMemory?:boolean
|
||||
additionalAssets?:[string, string][]
|
||||
additionalAssets?:[string, string, string][]
|
||||
ttsReadOnlyQuoted?:boolean
|
||||
replaceGlobalNote:string
|
||||
backgroundHTML?:string
|
||||
@@ -522,7 +522,8 @@ export interface Database{
|
||||
expires_in?: number
|
||||
}
|
||||
},
|
||||
classicMaxWidth: boolean
|
||||
classicMaxWidth: boolean,
|
||||
useChatSticker:boolean,
|
||||
}
|
||||
|
||||
interface hordeConfig{
|
||||
@@ -561,6 +562,7 @@ export interface Chat{
|
||||
supaMemoryData?:string
|
||||
lastMemory?:string
|
||||
suggestMessages?:string[]
|
||||
isStreaming?:boolean
|
||||
}
|
||||
|
||||
export interface Message{
|
||||
|
||||
@@ -170,7 +170,7 @@ export async function readImage(data:string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveAsset(data:Uint8Array, customId:string = ''){
|
||||
export async function saveAsset(data:Uint8Array, customId:string = '', fileName:string = ''){
|
||||
let id = ''
|
||||
if(customId !== ''){
|
||||
id = customId
|
||||
@@ -182,13 +182,17 @@ export async function saveAsset(data:Uint8Array, customId:string = ''){
|
||||
id = uuidv4()
|
||||
}
|
||||
}
|
||||
let fileExtension:string = 'png'
|
||||
if(fileName && fileName.split('.').length > 0){
|
||||
fileExtension = fileName.split('.').pop()
|
||||
}
|
||||
if(isTauri){
|
||||
await writeBinaryFile(`assets/${id}.png`, data ,{dir: BaseDirectory.AppData})
|
||||
return `assets/${id}.png`
|
||||
await writeBinaryFile(`assets/${id}.${fileExtension}`, data ,{dir: BaseDirectory.AppData})
|
||||
return `assets/${id}.${fileExtension}`
|
||||
}
|
||||
else{
|
||||
await forageStorage.setItem(`assets/${id}.png`, data)
|
||||
return `assets/${id}.png`
|
||||
await forageStorage.setItem(`assets/${id}.${fileExtension}`, data)
|
||||
return `assets/${id}.${fileExtension}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user