diff --git a/package.json b/package.json index 5fd152ef..df6bde22 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 347aebf3..7fd688d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index 0715018c..0da9a0bc 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -178,10 +178,10 @@ {#if currentChar.type !== 'group' && licensed !== 'private'} {language.description} - + {tokens.desc} {language.tokens} {language.firstMessage} - + {tokens.firstMsg} {language.tokens} {: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()} /> {tokens.localNote} {language.tokens} @@ -468,7 +469,7 @@ {#if currentChar.data.inlayViewScreen} {language.imgGenInstructions} - + {/if} { @@ -488,11 +489,11 @@ {language.emotionWarn} {language.imgGenPrompt} - + {language.imgGenNegatives} - + {language.imgGenInstructions} - + { if(currentChar.type === 'character'){ @@ -727,7 +728,7 @@ {language.advancedSettings} {#if currentChar.type !== 'group'} {language.exampleMessage} - + {language.creatorNotes} { @@ -735,25 +736,25 @@ }}> {language.systemPrompt} - + {language.replaceGlobalNote} - + {language.additionalText} - + {#if $DataBase.showUnrecommended || currentChar.data.personality.length > 3} {language.personality} - + {/if} {#if $DataBase.showUnrecommended || currentChar.data.scenario.length > 3} {language.scenario} - + {/if} {language.backgroundHTML} - + {language.creator} @@ -795,7 +796,7 @@ {#each currentChar.data.alternateGreetings as bias, i} - + { diff --git a/src/lib/SideBars/LoreBook/LoreBookData.svelte b/src/lib/SideBars/LoreBook/LoreBookData.svelte index db7fe2f3..64dbc9b1 100644 --- a/src/lib/SideBars/LoreBook/LoreBookData.svelte +++ b/src/lib/SideBars/LoreBook/LoreBookData.svelte @@ -86,7 +86,7 @@ {/if} {language.prompt} - + {#await getTokens(value.content)} {tokens} {language.tokens} {:then e} diff --git a/src/lib/SideBars/Scripts/RegexData.svelte b/src/lib/SideBars/Scripts/RegexData.svelte index b70bf782..f6cc5c5b 100644 --- a/src/lib/SideBars/Scripts/RegexData.svelte +++ b/src/lib/SideBars/Scripts/RegexData.svelte @@ -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 @@ IN: OUT: - + {#if value.ableFlag} FLAG: diff --git a/src/lib/SideBars/Scripts/TriggerData.svelte b/src/lib/SideBars/Scripts/TriggerData.svelte index ddcfcb39..1db0f237 100644 --- a/src/lib/SideBars/Scripts/TriggerData.svelte +++ b/src/lib/SideBars/Scripts/TriggerData.svelte @@ -122,7 +122,7 @@ {language.triggerMatchRegex} {language.value} - + {language.searchDepth} @@ -133,7 +133,7 @@ {/if} {#if cond.type === 'value'} - + {/if} {language.value} @@ -147,7 +147,7 @@ {#if cond.operator !== 'null'} - + {/if} {/if} {/each} @@ -255,7 +255,7 @@ {language.promptend} {language.value} - + {/if} {#if effect.type === 'setvar'} {language.varableName} @@ -269,7 +269,7 @@ {language.TriggerDivToVar} {language.value} - + {/if} {#if effect.type === 'runtrigger'} @@ -278,7 +278,7 @@ {/if} {#if effect.type === 'command'} {language.value} - + {/if} {#if effect.type === 'impersonate'} {language.role} @@ -287,7 +287,7 @@ {language.character} {language.value} - + {/if} {/each} diff --git a/src/lib/UI/GUI/SyntaxHightlightedTextarea.svelte b/src/lib/UI/GUI/SyntaxHightlightedTextarea.svelte new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/UI/GUI/TextAreaInput.svelte b/src/lib/UI/GUI/TextAreaInput.svelte index 93f50254..e1530d8b 100644 --- a/src/lib/UI/GUI/TextAreaInput.svelte +++ b/src/lib/UI/GUI/TextAreaInput.svelte @@ -1,16 +1,10 @@ - { - if(optimaizedInput){ - if(inpa++ > 10){ - value = e.currentTarget.value - inpa = 0 + 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} + { + if(optimaizedInput){ + if(inpa++ > 10){ + value = e.currentTarget.value + inpa = 0 + onInput() + } + } + else{ + value = e.currentTarget.value + onInput() + } + }} + on:change={(e) => { + if(optimaizedInput){ + value = e.currentTarget.value + onInput() + } + }} + /> + {:else} + { onInput() - } - } - else{ - value = e.currentTarget.value - onInput() - } - }} - on:change={(e) => { - if(optimaizedInput){ - value = e.currentTarget.value - onInput() - } - }} -/> - + }} + >{value ?? ''} + {/if} + \ No newline at end of file diff --git a/src/lib/UI/PromptDataItem.svelte b/src/lib/UI/PromptDataItem.svelte index 7e4fc88e..80a1f3ca 100644 --- a/src/lib/UI/PromptDataItem.svelte +++ b/src/lib/UI/PromptDataItem.svelte @@ -68,7 +68,7 @@ {language.globalNote} {language.prompt} - + {language.role} {language.user} @@ -115,7 +115,7 @@ }} /> {:else} {language.innerFormat} - + { if(promptItem.type === 'persona' || promptItem.type === 'description' || promptItem.type === 'authornote' || promptItem.type === 'memory'){ promptItem.innerFormat = null diff --git a/src/styles.css b/src/styles.css index bdc4183b..d72b31c9 100644 --- a/src/styles.css +++ b/src/styles.css @@ -190,4 +190,29 @@ 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; } \ No newline at end of file diff --git a/src/ts/characters.ts b/src/ts/characters.ts index ddab967b..a749567a 100644 --- a/src/ts/characters.ts +++ b/src/ts/characters.ts @@ -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) } diff --git a/src/ts/gui/highlight.ts b/src/ts/gui/highlight.ts new file mode 100644 index 00000000..f4e389f2 --- /dev/null +++ b/src/ts/gui/highlight.ts @@ -0,0 +1,261 @@ +type HighlightType = 'decorator'|'deprecated'|'cbsnest0'|'cbsnest1'|'cbsnest2'|'cbsnest3'|'cbsnest4' + +type HighlightInt = [Range, HighlightType] + +let highLights = new Map(); + +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 +} \ No newline at end of file