您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Gives visual feedback for the definition
当前为
- // ==UserScript==
- // @name c.ai X Character Creation Helper
- // @namespace c.ai X Character Creation Helper
- // @version 1.0
- // @license MIT
- // @description Gives visual feedback for the definition
- // @author Vishanka
- // @match https://character.ai/*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- // Function to check for element's presence and execute a callback when found
- const checkElementPresence = (selector, callback, maxAttempts = 10) => {
- let attempts = 0;
- const interval = setInterval(() => {
- const element = document.querySelector(selector);
- if (element) {
- clearInterval(interval);
- callback(element);
- } else if (++attempts >= maxAttempts) {
- clearInterval(interval);
- console.warn(`Element ${selector} not found after ${maxAttempts} attempts.`);
- }
- }, 1000);
- };
- // Function to monitor elements on the page
- function monitorElements() {
- const initialElementIds = [
- //'div.flex-auto:nth-child(1) > div:nth-child(2) > div:nth-child(1)',
- 'div.relative:nth-child(5) > div:nth-child(1) > div:nth-child(1)', // Greeting
- 'div.relative:nth-child(4) > div:nth-child(1) > div:nth-child(1)' // Description
- ];
- initialElementIds.forEach(selector => {
- checkElementPresence(selector, (element) => {
- console.log(`Content of ${selector}:`, element.textContent);
- });
- });
- // Selector for the definition
- const definitionSelector = '.transition > div:nth-child(1) > div:nth-child(1) > div:nth-child(1)';
- checkElementPresence(definitionSelector, (element) => {
- const textarea = element.querySelector('textarea');
- if (textarea && !document.querySelector('.custom-definition-panel')) {
- updatePanel(textarea); // Initial panel setup
- // Observer to detect changes in the textarea content
- const observer = new MutationObserver(() => {
- updatePanel(textarea);
- });
- observer.observe(textarea, {attributes: true, childList: true, subtree: true, characterData: true});
- }
- });
- }
- // Function to update or create the newPanel based on textarea content
- function updatePanel(textarea) {
- let newPanel = document.querySelector('.custom-definition-panel');
- if (!newPanel) {
- newPanel = document.createElement('div');
- newPanel.classList.add('custom-definition-panel');
- textarea.parentNode.insertBefore(newPanel, textarea);
- }
- newPanel.innerHTML = ''; // Clear existing content
- newPanel.style.border = '0px solid #ccc';
- newPanel.style.padding = '10px';
- newPanel.style.marginBottom = '10px';
- const cleanedContent = textarea.value.trim();
- console.log(`Content of Definition:`, cleanedContent);
- const textLines = cleanedContent.split('\n');
- let lastColor = '#222326';
- let isDialogueContinuation = false; // Track if the current line continues a dialogue
- let prevColor = null; // Track the previous line's color for detecting color changes
- // Determine line color based on content and dialogue continuation logic
- let consecutiveCharCount = 0;
- let lastCharColor = '';
- let lastNamedCharacterColor = '';
- //let isDialogueContinuation = false;
- function determineLineColor(line, prevLine) {
- const dialogueStarterRegex = /^\{\{(?:char|user|random_user_[^\}]*)\}\}:|^[A-Za-z]+:/;
- const isDialogueStarter = dialogueStarterRegex.test(line);
- const continuesDialogue = prevLine && prevLine.trim().endsWith(':') && (line.startsWith(' ') || !dialogueStarterRegex.test(line));
- if (isDialogueStarter) {
- isDialogueContinuation = true;
- if (line.startsWith('{{char}}:')) {
- consecutiveCharCount++;
- lastColor = consecutiveCharCount % 2 === 0 ? '#26272B' : '#292A2E';
- lastCharColor = lastColor;
- } else if (line.match(/^[A-Za-z]+:/)) {
- lastNamedCharacterColor = lastNamedCharacterColor === '#474747' ? '#4C4C4D' : '#474747';
- lastColor = lastNamedCharacterColor;
- }
- else if (line.match(/^\{\{random_user_[^\}]*\}\}:|^\{\{random_user_\}\}:|^{{random_user_}}/)) {
- lastNamedCharacterColor = lastNamedCharacterColor === '#474747' ? '#4C4C4D' : '#474747';
- lastColor = lastNamedCharacterColor;
- } else {
- consecutiveCharCount = 0;
- lastColor = line.startsWith('{{user}}:') ? '#37362F' : '#3B3A32';
- }
- } else if (line.startsWith('END_OF_DIALOG')) {
- isDialogueContinuation = false;
- lastColor = 'rgba(65, 65, 66, 0)';
- } else if (isDialogueContinuation && continuesDialogue) {
- // Do nothing, continuation of dialogue
- } else if (isDialogueContinuation && !isDialogueStarter) {
- // Do nothing, continuation of dialogue
- } else {
- isDialogueContinuation = false;
- lastColor = 'rgba(65, 65, 66, 0)';
- }
- return lastColor;
- }
- // Function to remove dialogue starters from the start of a line
- let trimmedParts = []; // Array to store trimmed parts
- let consecutiveLines = []; // Array to store consecutive lines with the same color
- //let prevColor = null;
- function trimDialogueStarters(line) {
- const dialogueStarterRegex = /^(\{\{char\}\}:|\{\{user\}\}:|\{\{random_user_[^\}]*\}\}:|[A-Za-z]+:)\s*/;
- const trimmed = line.match(dialogueStarterRegex);
- if (trimmed) {
- trimmedParts.push(trimmed[0]); // Store the trimmed part
- }
- return line.replace(dialogueStarterRegex, '');
- }
- function groupConsecutiveLines(color, lineDiv) {
- // Check if there are consecutive lines with the same color
- if (consecutiveLines.length > 0 && consecutiveLines[0].color === color) {
- consecutiveLines.push({ color, lineDiv });
- } else {
- // If not, append the previous group of consecutive lines and start a new group
- appendConsecutiveLines();
- consecutiveLines.push({ color, lineDiv });
- }
- }
- function appendConsecutiveLines() {
- if (consecutiveLines.length > 0) {
- const groupDiv = document.createElement('div');
- const color = consecutiveLines[0].color;
- // Create a container div that could potentially use flexbox
- const containerDiv = document.createElement('div');
- containerDiv.style.width = '100%';
- groupDiv.style.backgroundColor = color;
- groupDiv.style.padding = '12px';
- groupDiv.style.borderRadius = '16px';
- groupDiv.style.display = 'inline-block';
- groupDiv.style.maxWidth = '100%'; // You might adjust this as needed
- // Only apply flexbox styling if the color condition is met
- if (color === '#37362F' || color === '#3B3A32') {
- containerDiv.style.display = 'flex';
- containerDiv.style.justifyContent = 'flex-end'; // Aligns the child div to the right
- }
- consecutiveLines.forEach(({ lineDiv }) => {
- groupDiv.appendChild(lineDiv);
- });
- // Add the groupDiv to the containerDiv (flex or not based on color)
- containerDiv.appendChild(groupDiv);
- // Append the containerDiv to the newPanel
- newPanel.appendChild(containerDiv);
- consecutiveLines = []; // Clear the array
- }
- }
- function formatText(text) {
- // Handle headers; replace Markdown headers (# Header) with <h1>, <h2>, etc.
- text = text.replace(/^(######\s)(.*)$/gm, '<h6>$2</h6>'); // For h6
- text = text.replace(/^(#####\s)(.*)$/gm, '<h5>$2</h5>'); // For h5
- text = text.replace(/^(####\s)(.*)$/gm, '<h4>$2</h4>'); // For h4
- text = text.replace(/^(###\s)(.*)$/gm, '<h3>$2</h3>'); // For h3
- text = text.replace(/^(##\s)(.*)$/gm, '<h2>$2</h2>'); // For h2
- text = text.replace(/^(#\s)(.*)$/gm, '<h1>$2</h1>'); // For h1
- // Process bold italic before bold or italic to avoid nesting conflicts
- text = text.replace(/\*\*\*([^*]+)\*\*\*/g, '<em><strong>$1</strong></em>');
- // Replace text wrapped in double asterisks with <strong> tags for bold
- text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
- // Finally, replace text wrapped in single asterisks with <em> tags for italics
- text = text.replace(/\*([^*]+)\*/g, '<em>$1</em>');
- return text;
- }
- textLines.forEach((line, index) => {
- const prevLine = index > 0 ? textLines[index - 1] : null;
- const currentColor = determineLineColor(line, prevLine);
- const trimmedLine = trimDialogueStarters(line);
- if (prevColor && currentColor !== prevColor) {
- appendConsecutiveLines(); // Append previous group of consecutive lines
- const spacingDiv = document.createElement('div');
- spacingDiv.style.marginBottom = '20px';
- newPanel.appendChild(spacingDiv);
- }
- const lineDiv = document.createElement('div');
- lineDiv.style.wordWrap = 'break-word'; // Allow text wrapping
- if (trimmedLine.startsWith("END_OF_DIALOG")) {
- appendConsecutiveLines(); // Make sure to append any pending groups before adding the divider
- const separatorLine = document.createElement('hr');
- newPanel.appendChild(separatorLine); // This ensures the divider is on a new line
- } else {
- if (trimmedParts.length > 0) {
- const headerDiv = document.createElement('div');
- const headerText = trimmedParts.shift();
- const formattedHeaderText = headerText.replace(/:/g, '');
- headerDiv.textContent = formattedHeaderText;
- if (formattedHeaderText.includes('{{user}}')) {
- headerDiv.style.textAlign = 'right';
- }
- newPanel.appendChild(headerDiv);
- }
- if (trimmedLine.trim() === '') {
- lineDiv.appendChild(document.createElement('br'));
- } else {
- const paragraph = document.createElement('p');
- // Call formatTextForItalics to wrap text in asterisks with <em> tags
- paragraph.innerHTML = formatText(trimmedLine);
- lineDiv.appendChild(paragraph);
- }
- groupConsecutiveLines(currentColor, lineDiv);
- }
- prevColor = currentColor;
- });
- appendConsecutiveLines();
- }
- // Monitor for URL changes to re-initialize element monitoring
- let currentUrl = window.location.href;
- setInterval(() => {
- if (window.location.href !== currentUrl) {
- console.log("URL changed. Re-initializing element monitoring.");
- currentUrl = window.location.href;
- monitorElements();
- }
- }, 1000);
- monitorElements();
- })();