Add CodeMirror based Highlighted editor (#325)

# PR Checklist
- [ ] Did you check if it works normally in all models? *ignore this
when it dosen't uses models*
- [ ] Did you check if it works normally in all of web, local and node
hosted versions? if it dosen't, did you blocked it in those versions?
- [ ] Did you added a type def?

# Description

![highlightedEditor](https://github.com/kwaroran/RisuAI/assets/18527479/17aa1b99-39b8-48c4-98b4-0c0e20dc1a44)

I have added an editor and settings that highlight asterisks, quotes,
and backticks, similar to AutoMark, using a CodeMirror-based editor.

1. Added the CodeMirror5 package
2. Added language and settings for "useAdvancedEditor"
3. Wrote AdvancedChatEditor.svelte

Thank you for your hard work on the project.
This commit is contained in:
kwaroran
2024-03-25 02:26:22 +09:00
committed by GitHub
10 changed files with 187 additions and 2 deletions

View File

@@ -403,4 +403,5 @@ export const languageChinese = {
"appendNameNAI": "在NAI上附加名称",
module: "模块",
modules: "模块",
useAdvancedEditor: "使用高级编辑器",
}

View File

@@ -432,4 +432,5 @@ export const languageGerman = {
textAdventureNAI: "Als Textabenteuer ausführen",
appendNameNAI: "Namen an NAI anhängen",
customStopWords: "Benutzerdefinierte Stoppwörter",
useAdvancedEditor: "Erweiterten Editor verwenden",
}

View File

@@ -551,4 +551,5 @@ export const languageEnglish = {
popularityLevelDesc: "Popularity increases with downloads, etc. to estimate, 3.7 popularity is about 1 downloads.",
additionalParams: "Additional Parameters",
heightMode: "Height Mode",
useAdvancedEditor: "Use Advanced Editor",
}

View File

@@ -432,4 +432,5 @@ export const languageKorean = {
largePersonaPortrait: "페르소나 세로 이미지",
module: "모듈",
modules: "모듈",
useAdvancedEditor: "고급 에디터 사용",
}

View File

@@ -406,4 +406,5 @@ export const LanguageVietnamese = {
"defaultPrompt": "Lời nhắc mặc định",
module: "Mô-đun",
modules: "Mô-đun",
useAdvancedEditor: "Sử dụng trình biên tập nâng cao",
}

View File

@@ -0,0 +1,163 @@
<script>
import { onMount, createEventDispatcher } from 'svelte';
import { EditIcon, LanguagesIcon } from "lucide-svelte";
import { DataBase } from "../../ts/storage/database";
import CodeMirror from 'codemirror';
import 'codemirror/lib/codemirror.css';
export let value, translate;
const dispatch = createEventDispatcher();
let toggleTranslate = !$DataBase.useAutoTranslateInput;
let velement, veditor;
let telement, teditor;
let _value = value;
let _translate = translate;
const markdowns = [
{
regex: /["“”](.*?)(["“”]|$)/gs,
className: "ci-quote",
},
{
regex: /`([^`]+)`/gs,
className: "ci-backtick",
},
{
regex: /\*\*\*([^*]+)(\*\*\*|$)/gs,
className: "ci-asterisk3",
},
{
regex: /(?<!\*)\*\*([^*]+)(\*\*(?!\*)|$)/gs,
className: "ci-asterisk2",
},
{
regex: /(?<!\*)\*([^*]+)(\*(?!\*)|$)/gs,
className: "ci-asterisk1",
},
];
onMount(() => {
veditor = initEditor(velement, value);
teditor = initEditor(telement, translate);
veditor.on('change', (_, evt) => {
if(evt.origin != 'setValue' && !toggleTranslate) {
const input = veditor.getValue('\r\n');
if(input != value) {
value = _value = input;
dispatch('change', { translate: false, value: input });
}
}
});
teditor.on('change', (_, evt) => {
if(evt.origin != 'setValue' && toggleTranslate) {
const input = teditor.getValue('\r\n');
if(input != translate) {
translate = _translate = input;
dispatch('change', { translate: true, value: input });
}
}
});
toggleTranslateText();
});
$: if(value != _value) {
veditor.setValue(_value = value);
}
$: if(translate != _translate) {
teditor.setValue(_translate = translate);
}
function toggleTranslateText() {
toggleTranslate = !toggleTranslate;
if(toggleTranslate) {
velement.style.display = "none";
telement.style.display = null;
teditor.refresh();
} else {
velement.style.display = null;
telement.style.display = "none";
veditor.refresh();
}
}
function initEditor(element, value) {
const editor = CodeMirror(element, {
lineNumbers: true,
value: value,
});
editor.on('change', (sender) => updateMarks(sender.doc));
return editor;
}
function updateMarks(doc) {
const text = doc.getValue();
for (const mark of doc.getAllMarks()) {
mark.clear();
}
for(const markdown of markdowns) {
for (const match of text.matchAll(markdown.regex)) {
const start = doc.posFromIndex(match.index);
const end = doc.posFromIndex(match.index + match[0].length);
doc.markText(start, end, { className: markdown.className });
}
}
}
</script>
<div class="flex flex-1 items-end ml-2 mr-2">
{#if $DataBase.useAutoTranslateInput}
<button
on:click={toggleTranslateText}
class="mr-2 bg-textcolor2 flex justify-center items-center text-gray-100 w-12 h-12 rounded-md hover:bg-green-500 transition-colors">
{#if toggleTranslate}
<LanguagesIcon />
{:else}
<EditIcon />
{/if}
</button>
{/if}
<div class="flex-1">
<div class="chatEditor" bind:this={velement}></div>
<div class="chatEditor" hidden bind:this={telement}></div>
</div>
</div>
<style>
.chatEditor {
display: table;
table-layout: fixed;
width: 100%;
}
.chatEditor :global(.CodeMirror) {
min-height: 2em;
height: auto;
background-color: var(--risu-theme-bgcolor);
color: #DD0;
}
.chatEditor :global(.CodeMirror:focus-within) {
background-color: var(--risu-theme-textcolor2);
}
.chatEditor :global(.CodeMirror-gutters) {
background-color: var(--risu-theme-selected);
border-left-color: var(--risu-theme-borderc);
}
.chatEditor :global(.ci-quote) {
color: #FFF;
}
.chatEditor :global(.ci-backtick) {
color: #6AC;
}
.chatEditor :global(.ci-asterisk3) {
font-weight: bold;
font-style: italic;
color: #E22;
}
.chatEditor :global(.ci-asterisk2) {
font-style: italic;
color: #E84;
}
.chatEditor :global(.ci-asterisk1) {
font-style: italic;
color: #990;
}
</style>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Suggestion from './Suggestion.svelte';
import AdvancedChatEditor from './AdvancedChatEditor.svelte';
import { CameraIcon, DatabaseIcon, DicesIcon, GlobeIcon, ImagePlusIcon, LanguagesIcon, Laugh, MenuIcon, MicOffIcon, PackageIcon, RefreshCcwIcon, ReplyIcon, Send, StepForwardIcon } from "lucide-svelte";
import { CurrentCharacter, CurrentChat, CurrentUsername, selectedCharID, CurrentUserIcon, CurrentShowMemoryLimit,CurrentSimpleCharacter } from "../../ts/stores";
import Chat from "./Chat.svelte";
@@ -385,6 +386,7 @@
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="w-full h-full" style={customStyle} on:click={() => {
openMenu = false
}}>
@@ -405,6 +407,8 @@
<Laugh/>
</div>
{/if}
{#if !$DataBase.useAdvancedEditor}
<textarea class="text-textcolor p-2 min-w-0 bg-transparent input-text text-xl flex-grow ml-4 mr-2 border-darkbutton resize-none focus:bg-selected overflow-y-hidden overflow-x-hidden max-w-full"
bind:value={messageInput}
bind:this={inputEle}
@@ -423,9 +427,16 @@
on:input={()=>{updateInputSizeAll();updateInputTransateMessage(false)}}
style:height={inputHeight}
/>
{:else}
<AdvancedChatEditor
bind:value={messageInput}
bind:translate={messageInputTranslate}
on:change={(e) => { updateInputTransateMessage(e.detail.translate);}}
/>
{/if}
{#if $doingChat || doingChatInputTranslate}
{#if $doingChat || doingChatInputTranslate}
<div
class="mr-2 bg-selected flex justify-center items-center text-gray-100 w-12 h-12 rounded-md hover:bg-green-500 transition-colors" on:click={abortChat}>
<div class="loadmove" class:autoload={autoMode}>
@@ -443,7 +454,7 @@
class="mr-2 bg-textcolor2 flex justify-center items-center text-gray-100 w-12 h-12 rounded-md hover:bg-green-500 transition-colors"><MenuIcon />
</div>
</div>
{#if $DataBase.useAutoTranslateInput}
{#if $DataBase.useAutoTranslateInput && !$DataBase.useAdvancedEditor}
<div class="flex items-center mt-2 mb-2">
<label for='messageInputTranslate' class="text-textcolor ml-4">
<LanguagesIcon />

View File

@@ -192,6 +192,10 @@
<Check bind:check={$DataBase.roundIcons} name={language.roundIcons}/>
</div>
<div class="flex items-center mt-2">
<Check bind:check={$DataBase.useAdvancedEditor} name={language.useAdvancedEditor}/>
</div>
{#if $DataBase.textScreenColor}
<div class="flex items-center mt-2">
<Check check={true} onChange={() => {

View File

@@ -604,6 +604,7 @@ export interface Database{
requestInfoInsideChat?:boolean
additionalParams:[string, string][]
heightMode:string
useAdvancedEditor:boolean
}
export interface customscript{