feat: add syntax highlighting
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
"@tauri-apps/cli": "1.5.11",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"@types/blueimp-md5": "^2.18.2",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/libsodium-wrappers-sumo": "^0.7.8",
|
||||
"@types/lodash": "^4.14.202",
|
||||
|
||||
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@@ -195,6 +195,9 @@ importers:
|
||||
'@types/blueimp-md5':
|
||||
specifier: ^2.18.2
|
||||
version: 2.18.2
|
||||
'@types/codemirror':
|
||||
specifier: ^5.60.15
|
||||
version: 5.60.15
|
||||
'@types/dompurify':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
@@ -983,6 +986,9 @@ packages:
|
||||
'@types/blueimp-md5@2.18.2':
|
||||
resolution: {integrity: sha512-dJ9yRry9Olt5GAWlgCtE5dK9d/Dfhn/V7hna86eEO2Pn76+E8Y0S0n61iEUEGhWXXgtKtHxtZLVNwL8X+vLHzg==}
|
||||
|
||||
'@types/codemirror@5.60.15':
|
||||
resolution: {integrity: sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==}
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
|
||||
|
||||
@@ -1040,6 +1046,9 @@ packages:
|
||||
'@types/streamsaver@2.0.4':
|
||||
resolution: {integrity: sha512-XxpGYIaBP+2NgZ5+4YeG7hI3wYAyOX8QB92xlPpNvStIAvlniml1th+D0bes1lUZz52IWkPlXMf88wy8NzFkbA==}
|
||||
|
||||
'@types/tern@0.23.9':
|
||||
resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==}
|
||||
|
||||
'@types/three@0.154.0':
|
||||
resolution: {integrity: sha512-IioqpGhch6FdLDh4zazRn3rXHj6Vn2nVOziJdXVbJFi9CaI65LtP9qqUtpzbsHK2Ezlox8NtsLNHSw3AQzucjA==}
|
||||
|
||||
@@ -4388,6 +4397,10 @@ snapshots:
|
||||
|
||||
'@types/blueimp-md5@2.18.2': {}
|
||||
|
||||
'@types/codemirror@5.60.15':
|
||||
dependencies:
|
||||
'@types/tern': 0.23.9
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
dependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
@@ -4436,6 +4449,10 @@ snapshots:
|
||||
|
||||
'@types/streamsaver@2.0.4': {}
|
||||
|
||||
'@types/tern@0.23.9':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
|
||||
'@types/three@0.154.0':
|
||||
dependencies:
|
||||
'@tweenjs/tween.js': 18.6.4
|
||||
|
||||
@@ -178,10 +178,10 @@
|
||||
{#if currentChar.type !== 'group' && licensed !== 'private'}
|
||||
<TextInput size="xl" marginBottom placeholder="Character Name" bind:value={currentChar.data.name} />
|
||||
<span class="text-textcolor">{language.description} <Help key="charDesc"/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.desc}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.desc}></TextAreaInput>
|
||||
<span class="text-textcolor2 mb-6 text-sm">{tokens.desc} {language.tokens}</span>
|
||||
<span class="text-textcolor">{language.firstMessage} <Help key="charFirstMessage"/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.firstMessage}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.firstMessage}></TextAreaInput>
|
||||
<span class="text-textcolor2 mb-6 text-sm">{tokens.firstMsg} {language.tokens}</span>
|
||||
|
||||
{:else if licensed !== 'private' && currentChar.type === 'group'}
|
||||
@@ -239,6 +239,7 @@
|
||||
margin="both"
|
||||
autocomplete="off"
|
||||
bind:value={currentChar.data.chats[currentChar.data.chatPage].note}
|
||||
highlight
|
||||
placeholder={getAuthorNoteDefaultText()}
|
||||
/>
|
||||
<span class="text-textcolor2 mb-6 text-sm">{tokens.localNote} {language.tokens}</span>
|
||||
@@ -468,7 +469,7 @@
|
||||
|
||||
{#if currentChar.data.inlayViewScreen}
|
||||
<span class="text-textcolor mt-2">{language.imgGenInstructions}</span>
|
||||
<TextAreaInput bind:value={currentChar.data.newGenData.emotionInstructions} />
|
||||
<TextAreaInput highlight bind:value={currentChar.data.newGenData.emotionInstructions} />
|
||||
{/if}
|
||||
|
||||
<CheckInput bind:check={currentChar.data.inlayViewScreen} name={language.inlayViewScreen} onChange={() => {
|
||||
@@ -488,11 +489,11 @@
|
||||
<span class="text-textcolor2 text-xs">{language.emotionWarn}</span>
|
||||
|
||||
<span class="text-textcolor mt-2">{language.imgGenPrompt}</span>
|
||||
<TextAreaInput bind:value={currentChar.data.newGenData.prompt} />
|
||||
<TextAreaInput highlight bind:value={currentChar.data.newGenData.prompt} />
|
||||
<span class="text-textcolor mt-2">{language.imgGenNegatives}</span>
|
||||
<TextAreaInput bind:value={currentChar.data.newGenData.negative} />
|
||||
<TextAreaInput highlight bind:value={currentChar.data.newGenData.negative} />
|
||||
<span class="text-textcolor mt-2">{language.imgGenInstructions}</span>
|
||||
<TextAreaInput bind:value={currentChar.data.newGenData.instructions} />
|
||||
<TextAreaInput highlight bind:value={currentChar.data.newGenData.instructions} />
|
||||
|
||||
<CheckInput bind:check={currentChar.data.inlayViewScreen} name={language.inlayViewScreen} onChange={() => {
|
||||
if(currentChar.type === 'character'){
|
||||
@@ -727,7 +728,7 @@
|
||||
<h2 class="mb-2 text-2xl font-bold mt-2">{language.advancedSettings}</h2>
|
||||
{#if currentChar.type !== 'group'}
|
||||
<span class="text-textcolor">{language.exampleMessage} <Help key="exampleMessage"/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.exampleMessage}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.exampleMessage}></TextAreaInput>
|
||||
|
||||
<span class="text-textcolor">{language.creatorNotes} <Help key="creatorQuotes"/></span>
|
||||
<MultiLangInput bind:value={currentChar.data.creatorNotes} className="my-2" onInput={() => {
|
||||
@@ -735,25 +736,25 @@
|
||||
}}></MultiLangInput>
|
||||
|
||||
<span class="text-textcolor">{language.systemPrompt} <Help key="systemPrompt"/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.systemPrompt}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.systemPrompt}></TextAreaInput>
|
||||
|
||||
<span class="text-textcolor">{language.replaceGlobalNote} <Help key="replaceGlobalNote"/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.replaceGlobalNote}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.replaceGlobalNote}></TextAreaInput>
|
||||
|
||||
<span class="text-textcolor mt-2">{language.additionalText} <Help key="additionalText" /></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.additionalText}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.additionalText}></TextAreaInput>
|
||||
|
||||
{#if $DataBase.showUnrecommended || currentChar.data.personality.length > 3}
|
||||
<span class="text-textcolor">{language.personality} <Help key="personality" unrecommended/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.personality}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.personality}></TextAreaInput>
|
||||
{/if}
|
||||
{#if $DataBase.showUnrecommended || currentChar.data.scenario.length > 3}
|
||||
<span class="text-textcolor">{language.scenario} <Help key="scenario" unrecommended/></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.scenario}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.scenario}></TextAreaInput>
|
||||
{/if}
|
||||
|
||||
<span class="text-textcolor mt-2">{language.backgroundHTML} <Help key="backgroundHTML" /></span>
|
||||
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.backgroundHTML}></TextAreaInput>
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.backgroundHTML}></TextAreaInput>
|
||||
|
||||
<span class="text-textcolor">{language.creator}</span>
|
||||
<TextInput size="sm" autocomplete="off" bind:value={currentChar.data.additionalData.creator} />
|
||||
@@ -795,7 +796,7 @@
|
||||
{#each currentChar.data.alternateGreetings as bias, i}
|
||||
<tr>
|
||||
<td class="font-medium truncate">
|
||||
<TextAreaInput bind:value={currentChar.data.alternateGreetings[i]} placeholder="..." fullwidth />
|
||||
<TextAreaInput highlight bind:value={currentChar.data.alternateGreetings[i]} placeholder="..." fullwidth />
|
||||
</td>
|
||||
<th class="font-medium cursor-pointer w-10">
|
||||
<button class="hover:text-green-500" on:click={() => {
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<NumberInput size="sm" bind:value={value.insertorder} min={0} max={1000}/>
|
||||
{/if}
|
||||
<span class="text-textcolor mt-4 mb-2">{language.prompt}</span>
|
||||
<TextAreaInput autocomplete="off" bind:value={value.content} />
|
||||
<TextAreaInput highlight autocomplete="off" bind:value={value.content} />
|
||||
{#await getTokens(value.content)}
|
||||
<span class="text-textcolor2 mt-2 mb-2 text-sm">{tokens} {language.tokens}</span>
|
||||
{:then e}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import type { customscript } from "src/ts/storage/database";
|
||||
import Check from "../../UI/GUI/CheckInput.svelte";
|
||||
import TextInput from "../../UI/GUI/TextInput.svelte";
|
||||
import TextAreaInput from "../../UI/GUI/TextAreaInput.svelte";
|
||||
import SelectInput from "../../UI/GUI/SelectInput.svelte";
|
||||
import OptionInput from "../../UI/GUI/OptionInput.svelte";
|
||||
|
||||
@@ -57,7 +58,7 @@
|
||||
<span class="text-textcolor mt-6">IN:</span>
|
||||
<TextInput size="sm" bind:value={value.in} />
|
||||
<span class="text-textcolor mt-6">OUT:</span>
|
||||
<TextInput size="sm" bind:value={value.out} />
|
||||
<TextAreaInput highlight autocomplete="off" size="sm" bind:value={value.out} />
|
||||
{#if value.ableFlag}
|
||||
<span class="text-textcolor mt-6">FLAG:</span>
|
||||
<TextInput size="sm" bind:value={value.flag} />
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<OptionInput value="regex">{language.triggerMatchRegex}</OptionInput>
|
||||
</SelectInput>
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<TextInput size="sm" bind:value={cond.value} />
|
||||
<TextAreaInput highlight bind:value={cond.value} />
|
||||
|
||||
<span class="text-textcolor2 text-sm">{language.searchDepth}</span>
|
||||
<NumberInput size="sm" bind:value={cond.depth} />
|
||||
@@ -133,7 +133,7 @@
|
||||
<TextInput size="sm" bind:value={cond.var} />
|
||||
{/if}
|
||||
{#if cond.type === 'value'}
|
||||
<TextInput size="sm" bind:value={cond.var} />
|
||||
<TextAreaInput highlight size="sm" bind:value={cond.var} />
|
||||
{/if}
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<SelectInput bind:value={cond.operator} size="sm">
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
</SelectInput>
|
||||
{#if cond.operator !== 'null'}
|
||||
<TextInput size="sm" bind:value={cond.value} />
|
||||
<TextAreaInput highlight size="sm" bind:value={cond.value} />
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -255,7 +255,7 @@
|
||||
<OptionInput value="promptend">{language.promptend}</OptionInput>
|
||||
</SelectInput>
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<TextAreaInput bind:value={effect.value} />
|
||||
<TextAreaInput highlight bind:value={effect.value} />
|
||||
{/if}
|
||||
{#if effect.type === 'setvar'}
|
||||
<span class="text-textcolor2 text-sm">{language.varableName}</span>
|
||||
@@ -269,7 +269,7 @@
|
||||
<OptionInput value="/=">{language.TriggerDivToVar}</OptionInput>
|
||||
</SelectInput>
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<TextInput bind:value={effect.value} />
|
||||
<TextAreaInput highlight bind:value={effect.value} />
|
||||
{/if}
|
||||
|
||||
{#if effect.type === 'runtrigger'}
|
||||
@@ -278,7 +278,7 @@
|
||||
{/if}
|
||||
{#if effect.type === 'command'}
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<TextAreaInput bind:value={effect.value} />
|
||||
<TextAreaInput highlight bind:value={effect.value} />
|
||||
{/if}
|
||||
{#if effect.type === 'impersonate'}
|
||||
<span class="text-textcolor2 text-sm">{language.role}</span>
|
||||
@@ -287,7 +287,7 @@
|
||||
<OptionInput value="char">{language.character}</OptionInput>
|
||||
</SelectInput>
|
||||
<span class="text-textcolor2 text-sm">{language.value}</span>
|
||||
<TextAreaInput bind:value={effect.value} />
|
||||
<TextAreaInput highlight bind:value={effect.value} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
0
src/lib/UI/GUI/SyntaxHightlightedTextarea.svelte
Normal file
0
src/lib/UI/GUI/SyntaxHightlightedTextarea.svelte
Normal file
@@ -1,16 +1,10 @@
|
||||
<textarea
|
||||
class={"border border-darkborderc n-scroll focus:border-borderc resize-none rounded-md shadow-sm text-textcolor bg-transparent focus:ring-borderc focus:ring-2 focus:outline-none transition-colors duration-200" + ((className) ? (' ' + className) : '')}
|
||||
<div
|
||||
class={"border border-darkborderc relative z-20 n-scroll focus-within:border-borderc rounded-md shadow-sm text-textcolor bg-transparent focus-within:ring-borderc focus-within:ring-2 focus-within:outline-none transition-colors duration-200" + ((className) ? (' ' + className) : '')}
|
||||
class:text-sm={size === 'sm' || (size === 'default' && $textAreaTextSize === 1)}
|
||||
class:text-md={size === 'md' || (size === 'default' && $textAreaTextSize === 2)}
|
||||
class:text-lg={size === 'lg' || (size === 'default' && $textAreaTextSize === 3)}
|
||||
class:text-xl={size === 'xl'}
|
||||
class:text-xs={size === 'xs' || (size === 'default' && $textAreaTextSize === 0)}
|
||||
class:px-4={padding}
|
||||
class:py-2={padding}
|
||||
class:mb-4={margin === 'bottom'}
|
||||
class:mb-2={margin === 'both'}
|
||||
class:mt-4={margin === 'top'}
|
||||
class:mt-2={margin === 'both'}
|
||||
class:w-full={fullwidth}
|
||||
class:h-20={height === '20' || (height === 'default' && $textAreaSize === -5)}
|
||||
class:h-24={height === '24' || (height === 'default' && $textAreaSize === -4)}
|
||||
@@ -35,6 +29,18 @@
|
||||
class:min-h-64={height === 'default' && $textAreaSize === 3}
|
||||
class:min-h-72={height === 'default' && $textAreaSize === 4}
|
||||
class:min-h-80={height === 'default' && $textAreaSize === 5}
|
||||
class:mb-4={margin === 'bottom'}
|
||||
class:mb-2={margin === 'both'}
|
||||
class:mt-4={margin === 'top'}
|
||||
class:mt-2={margin === 'both'}
|
||||
bind:this={highlightDom}
|
||||
>
|
||||
{#if !highlight || !CSS.highlights}
|
||||
<textarea
|
||||
class="w-full h-full bg-transparent focus-within:outline-none resize-none absolute top-0 left-0 z-10 overflow-y-auto"
|
||||
class:text-transparent={highlight}
|
||||
class:px-4={padding}
|
||||
class:py-2={padding}
|
||||
{autocomplete}
|
||||
{placeholder}
|
||||
id={id}
|
||||
@@ -59,9 +65,22 @@
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{:else}
|
||||
<div
|
||||
class="w-full h-full bg-transparent focus-within:outline-none resize-none absolute top-0 left-0 z-10 overflow-y-auto px-4 py-2 break-words whitespace-pre-wrap"
|
||||
contenteditable="plaintext-only"
|
||||
bind:innerText={value}
|
||||
on:keydown={(e) => {
|
||||
onInput()
|
||||
}}
|
||||
>{value ?? ''}</div>
|
||||
{/if}
|
||||
</div>
|
||||
<script lang="ts">
|
||||
import { textAreaSize, textAreaTextSize } from 'src/ts/gui/guisize'
|
||||
import { highlighter, getNewHighlightId, removeHighlight } from 'src/ts/gui/highlight'
|
||||
import { sleep } from 'src/ts/util';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
export let size: 'xs'|'sm'|'md'|'lg'|'xl'|'default' = 'default'
|
||||
export let autocomplete: 'on'|'off' = 'off'
|
||||
export let placeholder: string = ''
|
||||
@@ -74,10 +93,26 @@
|
||||
export let height:'20'|'24'|'28'|'32'|'36'|'full'|'default' = 'default'
|
||||
export let className = ''
|
||||
export let optimaizedInput = true
|
||||
export let highlight = false
|
||||
let highlightId = highlight ? getNewHighlightId() : 0
|
||||
let inpa = 0
|
||||
|
||||
let highlightDom: HTMLDivElement
|
||||
let optiValue = value
|
||||
|
||||
onMount(() => {
|
||||
highlighter(highlightDom, highlightId)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
removeHighlight(highlightId)
|
||||
})
|
||||
|
||||
const highlightChange = async (value:string, highlightId:number) => {
|
||||
await sleep(1)
|
||||
highlighter(highlightDom, highlightId)
|
||||
}
|
||||
|
||||
$: optiValue = value
|
||||
$: highlightChange(value, highlightId)
|
||||
|
||||
</script>
|
||||
@@ -68,7 +68,7 @@
|
||||
<OptionInput value="globalNote">{language.globalNote}</OptionInput>
|
||||
</SelectInput>
|
||||
<span>{language.prompt}</span>
|
||||
<TextAreaInput bind:value={promptItem.text} />
|
||||
<TextAreaInput highlight bind:value={promptItem.text} />
|
||||
<span>{language.role}</span>
|
||||
<SelectInput bind:value={promptItem.role}>
|
||||
<OptionInput value="user">{language.user}</OptionInput>
|
||||
@@ -115,7 +115,7 @@
|
||||
}} />
|
||||
{:else}
|
||||
<span>{language.innerFormat}</span>
|
||||
<TextAreaInput bind:value={promptItem.innerFormat}/>
|
||||
<TextAreaInput highlight bind:value={promptItem.innerFormat}/>
|
||||
<CheckInput name={language.customInnerFormat} check={true} className="mt-2" onChange={() => {
|
||||
if(promptItem.type === 'persona' || promptItem.type === 'description' || promptItem.type === 'authornote' || promptItem.type === 'memory'){
|
||||
promptItem.innerFormat = null
|
||||
|
||||
@@ -191,3 +191,28 @@ html, body{
|
||||
.x-risu-risu-comment{
|
||||
@apply border border-darkborderc bg-darkbg text-textcolor rounded-md shadow-sm focus:outline-none transition-colors duration-200 px-4 py-2 min-w-0
|
||||
}
|
||||
|
||||
|
||||
::highlight(cbsnest3) {
|
||||
@apply text-amber-500;
|
||||
}
|
||||
::highlight(cbsnest2) {
|
||||
@apply text-green-500;
|
||||
}
|
||||
::highlight(cbsnest1) {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
::highlight(cbsnest0) {
|
||||
@apply text-purple-500;
|
||||
}
|
||||
::highlight(cbsnest4) {
|
||||
@apply text-pink-500;
|
||||
}
|
||||
|
||||
::highlight(decorator) {
|
||||
color: var(--risu-theme-draculared);
|
||||
}
|
||||
::highlight(deprecated) {
|
||||
color: var(--risu-theme-textcolor2);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
@@ -333,6 +333,9 @@ export function characterFormatUpdate(index:number|character){
|
||||
model: '',
|
||||
language: 'en'
|
||||
}
|
||||
cha.backgroundHTML ??= ''
|
||||
cha.backgroundCSS ??= ''
|
||||
cha.creation_date ??= Date.now()
|
||||
if(!cha.newGenData){
|
||||
cha = updateInlayScreen(cha)
|
||||
}
|
||||
|
||||
261
src/ts/gui/highlight.ts
Normal file
261
src/ts/gui/highlight.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
type HighlightType = 'decorator'|'deprecated'|'cbsnest0'|'cbsnest1'|'cbsnest2'|'cbsnest3'|'cbsnest4'
|
||||
|
||||
type HighlightInt = [Range, HighlightType]
|
||||
|
||||
let highLights = new Map<number, HighlightInt[]>();
|
||||
|
||||
export const highlighter = (highlightDom:HTMLElement, id:number) => {
|
||||
if(highlightDom){
|
||||
if(!CSS.highlights){
|
||||
return
|
||||
}
|
||||
|
||||
const walker = document.createTreeWalker(highlightDom, NodeFilter.SHOW_TEXT)
|
||||
const nodes:Node[] = []
|
||||
let currentNode = walker.nextNode();
|
||||
while (currentNode) {
|
||||
nodes.push(currentNode);
|
||||
currentNode = walker.nextNode();
|
||||
}
|
||||
const str = "{{char}}"
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ranges:HighlightInt[] = []
|
||||
|
||||
nodes.map((el) => {
|
||||
// const indices = [];
|
||||
const text = el.textContent.toLowerCase()
|
||||
// let startPos = 0;
|
||||
// while (startPos < text.length) {
|
||||
// const index = text.indexOf(str, startPos);
|
||||
// if (index === -1) break;
|
||||
// indices.push(index);
|
||||
// startPos = index + str.length;
|
||||
// }
|
||||
|
||||
// // Create a range object for each instance of
|
||||
// // str we found in the text node.
|
||||
// return indices.map((index) => {
|
||||
// const range = new Range();
|
||||
// range.setStart(el, index);
|
||||
// range.setEnd(el, index + str.length);
|
||||
// return range;
|
||||
// });
|
||||
|
||||
const cbsParsed = simpleCBSHighlightParser(el,text)
|
||||
ranges.push(...cbsParsed)
|
||||
|
||||
for(const syntax of highlighterSyntax){
|
||||
const regex = syntax.regex
|
||||
let match:RegExpExecArray | null;
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const length = match[0].length;
|
||||
const index = match.index;
|
||||
const range = new Range();
|
||||
range.setStart(el, index);
|
||||
range.setEnd(el, index + length);
|
||||
ranges.push([range, syntax.type])
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
highLights.set(id, ranges)
|
||||
|
||||
runHighlight()
|
||||
}
|
||||
}
|
||||
|
||||
const runHighlight = () => {
|
||||
const formatedRanges:{[key:string]:Range[]} = {}
|
||||
for(const h of highLights){
|
||||
for(const range of h[1]){
|
||||
const type = range[1]
|
||||
if(!formatedRanges[type]){
|
||||
formatedRanges[type] = []
|
||||
}
|
||||
formatedRanges[type].push(range[0])
|
||||
}
|
||||
}
|
||||
|
||||
for(const key in formatedRanges){
|
||||
const highlight = new Highlight(...formatedRanges[key]);
|
||||
CSS.highlights.set(key, highlight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let highlightIds = 0
|
||||
|
||||
export const getNewHighlightId = () => {
|
||||
return highlightIds++
|
||||
}
|
||||
|
||||
export const removeHighlight = (id:number) => {
|
||||
highLights.delete(id)
|
||||
}
|
||||
|
||||
const normalCBS = [
|
||||
'previous_char_chat', 'lastcharmessage', 'previous_user_chat', 'lastusermessage', 'char', 'bot',
|
||||
'user', 'char_persona', 'description', 'char_desc', 'example_dialogue',
|
||||
'example_message', 'persona', 'user_persona', 'lorebook', 'world_info', 'history', 'messages',
|
||||
'chat_index', 'first_msg_index', 'blank', 'none', 'message_time', 'message_date', 'time',
|
||||
'date', 'isotime', 'isodate', 'message_idle_duration', 'idle_duration', 'br', 'newline',
|
||||
'model', 'axmodel', 'role', 'jbtoggled', 'random', 'maxcontext', 'lastmessage', 'lastmessageid',
|
||||
'lastmessageindex', 'emotionlist', 'assetlist', 'prefill_supported', 'unixtime', '/', '/if', '/each', '/pure', '/if_pure',
|
||||
]
|
||||
|
||||
const normalCBSwithParams = [
|
||||
'getvar', 'calc', 'addvar', 'setvar', 'setdefaultvar', 'button', 'equal', 'not_equal', 'file',
|
||||
'startswith', 'endswith', 'contains', 'replace', 'split', 'join', 'spread', 'trim', 'length',
|
||||
'arraylength', 'array_length', 'lower', 'upper', 'capitalize', 'round', 'floor', 'ceil', 'abs',
|
||||
'previous_chat_log', 'tonumber', 'arrayelement', 'array_element', 'arrayshift', 'array_shift',
|
||||
'arraypop', 'array_pop', 'arraypush', 'array_push', 'arraysplice', 'array_splice',
|
||||
'makearray', 'array', 'a', 'make_array', 'history', 'messages', 'range', 'date', 'time', 'datetimeformat', 'date_time_format',
|
||||
'random', 'pick', 'roll', 'datetimeformat', 'hidden_key', 'reverse', 'comment'
|
||||
]
|
||||
|
||||
const specialCBS = [
|
||||
'#if', '#if_pure ', '#pure ', '#each ', 'random:', 'pick:', 'roll:', 'datetimeformat:', '? ', 'hidden_key: ', 'reverse: ', 'comment: ',
|
||||
]
|
||||
|
||||
const deprecatedCBS = [
|
||||
'personality', 'scenario', 'main_prompt', 'system_prompt', 'ujb', 'global_note', 'system_note',
|
||||
]
|
||||
|
||||
const deprecatedCBSwithParams = [
|
||||
'greater', 'less', 'greater_equal', 'less_equal', 'and', 'or', 'not', 'remaind', 'pow'
|
||||
]
|
||||
|
||||
const decorators = [
|
||||
'activate_only_after', 'activate_only_every', 'keep_activate_after_match', 'dont_activate_after_match', 'depth', 'reverse_depth',
|
||||
'instruct_depth', 'reverse_instruct_depth', 'instruct_scan_depth', 'role', 'scan_depth', 'is_greeting', 'position', 'ignore_on_max_context',
|
||||
'additional_keys', 'exclude_keys', 'is_user_icon', 'activate', 'dont_activate', 'disable_ui_prompt'
|
||||
]
|
||||
|
||||
const deprecatedDecorators = [
|
||||
'end', 'assistant', 'user', 'system'
|
||||
]
|
||||
const highlighterSyntax = [
|
||||
{
|
||||
regex: /<(char|user|bot)>/gi,
|
||||
type: 'deprecated'
|
||||
},
|
||||
{
|
||||
regex: new RegExp(`@@@?(${decorators.join('|')})`, 'gi'),
|
||||
type: 'decorator'
|
||||
},
|
||||
{
|
||||
regex: new RegExp(`@@@?(${deprecatedDecorators.join('|')})`, 'gi'),
|
||||
type: 'deprecated'
|
||||
},
|
||||
] as const
|
||||
|
||||
|
||||
function simpleCBSHighlightParser(node:Node,text:string){
|
||||
let depth = 0
|
||||
let pointer = 0
|
||||
let depthStarts = new Uint8Array(100)
|
||||
let highlightMode = new Uint8Array(100)
|
||||
|
||||
const ranges:HighlightInt[] = []
|
||||
const excludesRanges:[number,number][] = []
|
||||
|
||||
text = text.toLowerCase()
|
||||
|
||||
const checkHighlight = () => {
|
||||
if(depth !== 0 && highlightMode[depth] === 0){
|
||||
highlightMode[depth] = 10
|
||||
const upString = text.slice(depthStarts[depth], pointer)
|
||||
|
||||
if(highlightMode[depth] === 10){
|
||||
for(const arg of normalCBS){
|
||||
if(upString === arg){
|
||||
highlightMode[depth] = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlightMode[depth] === 10){
|
||||
for(const arg of deprecatedCBS){
|
||||
if(upString === arg){
|
||||
highlightMode[depth] = 3
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlightMode[depth] === 10){
|
||||
for(const arg of normalCBSwithParams){
|
||||
if(upString.startsWith(arg + '::')){
|
||||
highlightMode[depth] = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlightMode[depth] === 10){
|
||||
for(const arg of deprecatedCBSwithParams){
|
||||
if(upString.startsWith(arg + '::')){
|
||||
highlightMode[depth] = 3
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(highlightMode[depth] === 10){
|
||||
for(const arg of specialCBS){
|
||||
if(upString.startsWith(arg)){
|
||||
highlightMode[depth] = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
colorHighlight()
|
||||
}
|
||||
}
|
||||
|
||||
const colorHighlight = () => {
|
||||
if(highlightMode[depth] !== 10){
|
||||
const range = new Range();
|
||||
range.setStart(node, depthStarts[depth] - 2);
|
||||
range.setEnd(node, pointer + 2);
|
||||
switch(highlightMode[depth]){
|
||||
case 1:
|
||||
ranges.push([range, `cbsnest${depth % 5}` as HighlightType])
|
||||
break;
|
||||
case 3:
|
||||
ranges.push([range, 'deprecated'])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(pointer < text.length){
|
||||
const c = text[pointer]
|
||||
const nextC = text[pointer + 1]
|
||||
if(c === '{' && nextC === '{'){
|
||||
checkHighlight()
|
||||
depth++
|
||||
pointer++
|
||||
depthStarts[depth] = pointer + 1
|
||||
highlightMode[depth] = 0
|
||||
}else if(c === '}' && nextC === '}'){
|
||||
if(highlightMode[depth] === 0){
|
||||
checkHighlight()
|
||||
}
|
||||
else{
|
||||
colorHighlight()
|
||||
}
|
||||
depth--
|
||||
pointer++
|
||||
depthStarts[depth] = pointer
|
||||
}
|
||||
pointer++
|
||||
}
|
||||
|
||||
return ranges
|
||||
}
|
||||
Reference in New Issue
Block a user