Add subtitle

This commit is contained in:
Kwaroran
2024-12-15 19:33:26 +09:00
parent e2149ced1b
commit dcc48d5cb3
9 changed files with 611 additions and 85 deletions

View File

@@ -8,6 +8,7 @@ function nodeObserve(node:HTMLElement){
const triggerName = node.getAttribute('risu-trigger');
const btnEvent = node.getAttribute('risu-btn');
const observerAdded = node.getAttribute('risu-observer');
const hlLang = node.getAttribute('x-hl-lang');
if(observerAdded){
return
@@ -45,13 +46,65 @@ function nodeObserve(node:HTMLElement){
node.setAttribute('risu-observer', 'true');
return
}
if(hlLang){
node.addEventListener('contextmenu', (e)=>{
e.preventDefault();
const menu = document.createElement('div');
menu.setAttribute('class', 'fixed z-50 min-w-[160px] py-2 bg-gray-800 rounded-lg border border-gray-700')
const copyOption = document.createElement('div');
copyOption.textContent = 'Copy';
copyOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
copyOption.addEventListener('click', ()=>{
navigator.clipboard.writeText(node.getAttribute('x-hl-text'));
menu.remove();
})
const downloadOption = document.createElement('div');
downloadOption.textContent = 'Download';
downloadOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
downloadOption.addEventListener('click', ()=>{
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([node.getAttribute('x-hl-text')], {type: 'text/plain'}));
a.download = 'code.' + hlLang;
a.click();
menu.remove();
})
menu.appendChild(copyOption);
menu.appendChild(downloadOption);
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
document.body.appendChild(menu);
document.addEventListener('click', ()=>{
menu.remove();
}, {once: true})
})
}
}
export async function startObserveDom(){
//For codeblock we are using MutationObserver since it doesn't appear well
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if(node instanceof HTMLElement){
nodeObserve(node);
}
})
})
})
//We are using a while loop intead of MutationObserver because MutationObserver is expensive for just a few elements
while(true){
document.querySelectorAll('[risu-trigger]').forEach(nodeObserve);
document.querySelectorAll('[risu-btn]').forEach(nodeObserve);
document.querySelectorAll('[x-hl-lang]').forEach(nodeObserve);
await sleep(100);
}
}

View File

@@ -117,18 +117,30 @@ async function renderHighlightableMarkdown(data:string) {
//import language if not already loaded
//we do not refactor this to a function because we want to keep vite to only import the languages that are needed
let languageModule:any = null
let shotLang = ''
switch(lang){
case 'js':
case 'javascript':{
lang = 'javascript'
shotLang = 'js'
if(!hljs.getLanguage('javascript')){
languageModule = await import('highlight.js/lib/languages/javascript')
}
break
}
case 'txt':
case 'vtt':{
shotLang = lang
lang = 'plaintext'
if(!hljs.getLanguage('plaintext')){
languageModule = await import('highlight.js/lib/languages/plaintext')
}
break
}
case 'py':
case 'python':{
lang = 'python'
shotLang = 'py'
if(!hljs.getLanguage('python')){
languageModule = await import('highlight.js/lib/languages/python')
}
@@ -136,6 +148,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'css':{
lang = 'css'
shotLang = 'css'
if(!hljs.getLanguage('css')){
languageModule = await import('highlight.js/lib/languages/css')
}
@@ -144,6 +157,7 @@ async function renderHighlightableMarkdown(data:string) {
case 'xml':
case 'html':{
lang = 'xml'
shotLang = 'xml'
if(!hljs.getLanguage('xml')){
languageModule = await import('highlight.js/lib/languages/xml')
}
@@ -151,6 +165,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'lua':{
lang = 'lua'
shotLang = 'lua'
if(!hljs.getLanguage('lua')){
languageModule = await import('highlight.js/lib/languages/lua')
}
@@ -158,6 +173,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'dart':{
lang = 'dart'
shotLang = 'dart'
if(!hljs.getLanguage('dart')){
languageModule = await import('highlight.js/lib/languages/dart')
}
@@ -165,6 +181,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'java':{
lang = 'java'
shotLang = 'java'
if(!hljs.getLanguage('java')){
languageModule = await import('highlight.js/lib/languages/java')
}
@@ -172,6 +189,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'rust':{
lang = 'rust'
shotLang = 'rs'
if(!hljs.getLanguage('rust')){
languageModule = await import('highlight.js/lib/languages/rust')
}
@@ -180,6 +198,7 @@ async function renderHighlightableMarkdown(data:string) {
case 'c':
case 'cpp':{
lang = 'cpp'
shotLang = 'cpp'
if(!hljs.getLanguage('cpp')){
languageModule = await import('highlight.js/lib/languages/cpp')
}
@@ -188,6 +207,7 @@ async function renderHighlightableMarkdown(data:string) {
case 'csharp':
case 'cs':{
lang = 'csharp'
shotLang = 'cs'
if(!hljs.getLanguage('csharp')){
languageModule = await import('highlight.js/lib/languages/csharp')
}
@@ -196,6 +216,7 @@ async function renderHighlightableMarkdown(data:string) {
case 'ts':
case 'typescript':{
lang = 'typescript'
shotLang = 'ts'
if(!hljs.getLanguage('typescript')){
languageModule = await import('highlight.js/lib/languages/typescript')
}
@@ -203,6 +224,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'json':{
lang = 'json'
shotLang = 'json'
if(!hljs.getLanguage('json')){
languageModule = await import('highlight.js/lib/languages/json')
}
@@ -210,6 +232,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'yaml':{
lang = 'yaml'
shotLang = 'yml'
if(!hljs.getLanguage('yaml')){
languageModule = await import('highlight.js/lib/languages/yaml')
}
@@ -217,6 +240,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'shell':{
lang = 'shell'
shotLang = 'sh'
if(!hljs.getLanguage('shell')){
languageModule = await import('highlight.js/lib/languages/shell')
}
@@ -224,6 +248,7 @@ async function renderHighlightableMarkdown(data:string) {
}
case 'bash':{
lang = 'bash'
shotLang = 'sh'
if(!hljs.getLanguage('bash')){
languageModule = await import('highlight.js/lib/languages/bash')
}
@@ -231,6 +256,7 @@ async function renderHighlightableMarkdown(data:string) {
}
default:{
lang = 'none'
shotLang = 'none'
}
}
if(languageModule){
@@ -244,7 +270,9 @@ async function renderHighlightableMarkdown(data:string) {
language: lang,
ignoreIllegals: true
}).value
rendered = rendered.replace(placeholder, `<pre class="hljs"><code>${highlighted}</code></pre>`)
rendered = rendered.replace(placeholder, `<pre class="hljs" x-hl-lang="${shotLang}" x-hl-text="${
Buffer.from(code).toString('hex')
}"><code>${highlighted}</code></pre>`)
}
} catch (error) {
@@ -492,8 +520,8 @@ export async function ParseMarkdown(
data = await renderHighlightableMarkdown(data)
}
return decodeStyle(DOMPurify.sanitize(data, {
ADD_TAGS: ["iframe", "style", "risu-style", "x-em"],
ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling", "risu-btn", 'risu-trigger', 'risu-mark'],
ADD_TAGS: ["iframe", "style", "risu-style", "x-em",],
ADD_ATTR: ["allow", "allowfullscreen", "frameborder", "scrolling", "risu-btn", 'risu-trigger', 'risu-mark', 'x-hl-lang', 'x-hl-text'],
}))
}

View File

@@ -500,12 +500,12 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
}
else{
const prevChat = reformatedChat[reformatedChat.length-1]
if(prevChat.role === chat.role){
if(prevChat?.role === chat.role){
reformatedChat[reformatedChat.length-1].content += '\n' + chat.content
continue
}
else if(chat.role === 'system'){
if(prevChat.role === 'user'){
if(prevChat?.role === 'user'){
reformatedChat[reformatedChat.length-1].content += '\nSystem:' + chat.content
}
else{
@@ -1387,14 +1387,50 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
for(let i=0;i<formated.length;i++){
const chat = formated[i]
if(i === 0){
if(chat.role === 'user' || chat.role === 'assistant'){
reformatedChat.push({
role: chat.role === 'user' ? 'USER' : 'MODEL',
parts: [{
text: chat.content
}]
})
const prevChat = reformatedChat[reformatedChat.length-1]
const qRole =
chat.role === 'user' ? 'USER' :
chat.role === 'assistant' ? 'MODEL' :
chat.role
if (chat.multimodals && chat.multimodals.length > 0 && chat.role === "user") {
let geminiParts: GeminiPart[] = [];
geminiParts.push({
text: chat.content,
});
for (const modal of chat.multimodals) {
if (
(modal.type === "image" && arg.modelInfo.flags.includes(LLMFlags.hasImageInput)) ||
(modal.type === "audio" && arg.modelInfo.flags.includes(LLMFlags.hasAudioInput)) ||
(modal.type === "video" && arg.modelInfo.flags.includes(LLMFlags.hasVideoInput))
) {
const dataurl = modal.base64;
const base64 = dataurl.split(",")[1];
const mediaType = dataurl.split(";")[0].split(":")[1];
geminiParts.push({
inlineData: {
mimeType: mediaType,
data: base64,
}
});
}
}
reformatedChat.push({
role: "USER",
parts: geminiParts,
});
} else if (prevChat?.role === qRole) {
reformatedChat[reformatedChat.length-1].parts[0].text += '\n' + chat.content
continue
}
else if(chat.role === 'system'){
if(prevChat?.role === 'USER'){
reformatedChat[reformatedChat.length-1].parts[0].text += '\nsystem:' + chat.content
}
else{
reformatedChat.push({
@@ -1405,78 +1441,22 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
})
}
}
else if(chat.role === 'assistant' || chat.role === 'user'){
reformatedChat.push({
role: chat.role === 'user' ? 'USER' : 'MODEL',
parts: [{
text: chat.content
}]
})
}
else{
const prevChat = reformatedChat[reformatedChat.length-1]
const qRole =
chat.role === 'user' ? 'USER' :
chat.role === 'assistant' ? 'MODEL' :
chat.role
if (chat.multimodals && chat.multimodals.length > 0 && chat.role === "user") {
let geminiParts: GeminiPart[] = [];
geminiParts.push({
text: chat.content,
});
for (const modal of chat.multimodals) {
if (
(modal.type === "image" && arg.modelInfo.flags.includes(LLMFlags.hasImageInput)) ||
(modal.type === "audio" && arg.modelInfo.flags.includes(LLMFlags.hasAudioInput)) ||
(modal.type === "video" && arg.modelInfo.flags.includes(LLMFlags.hasVideoInput))
) {
const dataurl = modal.base64;
const base64 = dataurl.split(",")[1];
const mediaType = dataurl.split(";")[0].split(":")[1];
geminiParts.push({
inlineData: {
mimeType: mediaType,
data: base64,
}
});
}
}
reformatedChat.push({
role: "USER",
parts: geminiParts,
});
} else if (prevChat.role === qRole) {
reformatedChat[reformatedChat.length-1].parts[0].text += '\n' + chat.content
continue
}
else if(chat.role === 'system'){
if(prevChat.role === 'USER'){
reformatedChat[reformatedChat.length-1].parts[0].text += '\nsystem:' + chat.content
}
else{
reformatedChat.push({
role: "USER",
parts: [{
text: chat.role + ':' + chat.content
}]
})
}
}
else if(chat.role === 'assistant' || chat.role === 'user'){
reformatedChat.push({
role: chat.role === 'user' ? 'USER' : 'MODEL',
parts: [{
text: chat.content
}]
})
}
else{
reformatedChat.push({
role: "USER",
parts: [{
text: chat.role + ':' + chat.content
}]
})
}
reformatedChat.push({
role: "USER",
parts: [{
text: chat.role + ':' + chat.content
}]
})
}
}