您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a Location and Character System to JanitorAI, hopefully making the bots a little less dumb.
当前为
// ==UserScript== // @name JanitorAI Context Maker // @namespace http://tampermonkey.net/ // @version 1.7 // @license MIT // @description Adds a Location and Character System to JanitorAI, hopefully making the bots a little less dumb. // @match https://janitorai.com/chats/* // @icon https://www.google.com/s2/favicons?sz=64&domain=https://janitorai.com/ // @grant GM.setValue // @grant GM.getValue // ==/UserScript== (async function() { 'use strict'; const themes = { dark: { '--bg-color': 'rgba(34, 34, 34, var(--ui-transparency))', '--text-color': '#ffffff', '--border-color': 'rgba(68, 68, 68, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.5)' }, light: { '--bg-color': 'rgba(255, 255, 255, var(--ui-transparency))', '--text-color': '#000000', '--border-color': 'rgba(204, 204, 204, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(40, 167, 69, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.1)' }, sepia: { '--bg-color': 'rgba(244, 232, 208, var(--ui-transparency))', '--text-color': '#5b4636', '--border-color': 'rgba(193, 154, 107, var(--ui-transparency))', '--button-bg-color': 'rgba(160, 82, 45, var(--ui-transparency))', '--active-char-color': 'rgba(210, 180, 140, var(--ui-transparency))', '--success-bg-color': 'rgba(107, 68, 35, var(--ui-transparency))', '--info-bg-color': 'rgba(194, 148, 110, var(--ui-transparency))', '--warning-bg-color': 'rgba(215, 172, 116, var(--ui-transparency))', '--muted-bg-color': 'rgba(160, 130, 94, var(--ui-transparency))', '--danger-bg-color': 'rgba(168, 96, 50, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.3)' }, solarized: { '--bg-color': 'rgba(253, 246, 227, var(--ui-transparency))', '--text-color': '#657b83', '--border-color': 'rgba(238, 232, 213, var(--ui-transparency))', '--button-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--active-char-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--success-bg-color': 'rgba(133, 153, 0, var(--ui-transparency))', '--info-bg-color': 'rgba(38, 139, 210, var(--ui-transparency))', '--warning-bg-color': 'rgba(181, 137, 0, var(--ui-transparency))', '--muted-bg-color': 'rgba(147, 161, 161, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 50, 47, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.2)' }, forest: { '--bg-color': 'rgba(34, 49, 34, var(--ui-transparency))', '--text-color': '#e0f7e9', '--border-color': 'rgba(46, 61, 46, var(--ui-transparency))', '--button-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--active-char-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--success-bg-color': 'rgba(34, 139, 34, var(--ui-transparency))', '--info-bg-color': 'rgba(144, 238, 144, var(--ui-transparency))', '--warning-bg-color': 'rgba(189, 183, 107, var(--ui-transparency))', '--muted-bg-color': 'rgba(85, 107, 47, var(--ui-transparency))', '--danger-bg-color': 'rgba(139, 69, 19, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.4)' }, ocean: { '--bg-color': 'rgba(28, 107, 160, var(--ui-transparency))', '--text-color': '#ffffff', '--border-color': 'rgba(54, 144, 192, var(--ui-transparency))', '--button-bg-color': 'rgba(0, 123, 255, var(--ui-transparency))', '--active-char-color': 'rgba(173, 216, 230, var(--ui-transparency))', '--success-bg-color': 'rgba(60, 179, 113, var(--ui-transparency))', '--info-bg-color': 'rgba(23, 162, 184, var(--ui-transparency))', '--warning-bg-color': 'rgba(255, 193, 7, var(--ui-transparency))', '--muted-bg-color': 'rgba(108, 117, 125, var(--ui-transparency))', '--danger-bg-color': 'rgba(220, 53, 69, var(--ui-transparency))', '--shadow-color': 'rgba(0,0,0,0.3)' } }; // Initialize CSS variables function setTheme(themeName) { const theme = themes[themeName]; Object.keys(theme).forEach(key => { document.documentElement.style.setProperty(key, theme[key]); }); } // Get saved transparency and theme, or use defaults const defaultTransparency = await GM.getValue('transparency', '0.9'); const defaultTheme = await GM.getValue('theme', 'dark'); // Set default theme and transparency document.documentElement.style.setProperty('--ui-transparency', defaultTransparency); setTheme(defaultTheme); // Add placeholder styles const style = document.createElement('style'); style.innerHTML = ` input::placeholder, textarea::placeholder { color: var(--text-color); } input:-ms-input-placeholder, textarea:-ms-input-placeholder { color: var(--text-color); } input::-ms-input-placeholder, textarea::-ms-input-placeholder { color: var(--text-color); } input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: var(--text-color); } `; document.head.appendChild(style); // Character Sidebar const characterSidebar = document.createElement('div'); characterSidebar.id = 'character-sheet-sidebar'; characterSidebar.style.cssText = ` position: fixed; top: 0; right: -300px; width: 300px; height: 100%; display: flex; flex-direction: column; background-color: var(--bg-color); border-left: 2px solid var(--border-color); box-shadow: -2px 0 5px var(--shadow-color); box-sizing: border-box; transition: right 0.3s; z-index: 9999; `; document.body.appendChild(characterSidebar); const characterToggleButton = document.createElement('button'); characterToggleButton.textContent = 'Characters ☰'; characterToggleButton.style.cssText = ` position: fixed; top: 10px; right: 10px; padding: 5px 10px; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; transition: right 0.3s; z-index: 10000; `; characterToggleButton.addEventListener('click', () => { const isOpen = characterSidebar.style.right === '0px'; characterSidebar.style.right = isOpen ? '-300px' : '0'; characterToggleButton.style.right = isOpen ? '10px' : '310px'; }); document.body.appendChild(characterToggleButton); const characterSlotList = document.createElement('div'); characterSlotList.id = 'character-slot-list'; characterSlotList.style.cssText = ` flex-grow: 1; overflow-y: auto; margin-top: 10px; `; characterSidebar.appendChild(characterSlotList); const characterButtonBox = document.createElement('div'); characterButtonBox.style.cssText = ` height: 40px; background-color: var(--bg-color); flex-shrink: 0; margin-bottom: 10px; `; characterSidebar.appendChild(characterButtonBox); const characterButtonContainer = document.createElement('div'); characterButtonContainer.style.cssText = ` display: flex; flex-wrap: wrap; justify-content: space-between; `; const newCharacterButton = document.createElement('button'); newCharacterButton.textContent = 'New Character'; newCharacterButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: calc(50% - 5px); `; newCharacterButton.addEventListener('click', createNewCharacter); const importCharacterButton = document.createElement('button'); importCharacterButton.textContent = 'Import Character'; importCharacterButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: calc(50% - 5px); `; importCharacterButton.addEventListener('click', importCharacter); const exportAllButton = document.createElement('button'); exportAllButton.textContent = 'Export All'; exportAllButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--warning-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: 100%; `; exportAllButton.addEventListener('click', exportAllCharacters); const importAllButton = document.createElement('button'); importAllButton.textContent = 'Import All'; importAllButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--muted-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: 100%; `; importAllButton.addEventListener('click', importAllCharacters); characterButtonContainer.appendChild(newCharacterButton); characterButtonContainer.appendChild(importCharacterButton); characterButtonContainer.appendChild(exportAllButton); characterButtonContainer.appendChild(importAllButton); characterSidebar.appendChild(characterButtonContainer); let characterData = []; function createNewCharacter() { const newCharacter = { active: true, emoji: '😀', name: 'New Character', sex: '', species: '', age: '', bodyType: '', personality: '', bio: '', userControlled: false }; characterData.push(newCharacter); renderCharacterSlots(); } function importCharacter() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { const importedCharacter = JSON.parse(e.target.result); characterData.push(importedCharacter); renderCharacterSlots(); }; reader.readAsText(file); }); fileInput.click(); } function exportAllCharacters() { const json = JSON.stringify(characterData, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `all_characters.json`; link.click(); } function importAllCharacters() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { const importedCharacters = JSON.parse(e.target.result); characterData = importedCharacters; renderCharacterSlots(); }; reader.readAsText(file); }); fileInput.click(); } function renderCharacterSlots() { characterSlotList.innerHTML = ''; characterData.sort((a, b) => a.name.localeCompare(b.name)); characterData.forEach((character, index) => { const characterSlot = document.createElement('div'); characterSlot.className = 'character-slot'; characterSlot.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 10px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: ${character.userControlled ? 'var(--active-char-color)' : 'var(--bg-color)'}; `; const activeButton = document.createElement('button'); activeButton.textContent = character.active ? '🟢' : '🔴'; activeButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; activeButton.addEventListener('click', () => { character.active = !character.active; renderCharacterSlots(); }); const emojiSpan = document.createElement('span'); emojiSpan.textContent = character.emoji; emojiSpan.style.marginRight = '10px'; const nameSpan = document.createElement('span'); nameSpan.textContent = character.name; nameSpan.style.cssText = ` overflow: hidden; white-space: nowrap; text-overflow: ellipsis; max-width: 80px; color: var(--text-color); `; const editButton = document.createElement('button'); editButton.textContent = '✏️'; editButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; editButton.addEventListener('click', () => { editCharacter(index); }); const deleteButton = document.createElement('button'); deleteButton.textContent = '🗑️'; deleteButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; deleteButton.addEventListener('click', () => { deleteCharacter(index); }); const exportButton = document.createElement('button'); exportButton.textContent = '⬇️'; exportButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; exportButton.addEventListener('click', () => { exportCharacter(character); }); characterSlot.appendChild(activeButton); characterSlot.appendChild(emojiSpan); characterSlot.appendChild(nameSpan); characterSlot.appendChild(editButton); characterSlot.appendChild(deleteButton); characterSlot.appendChild(exportButton); characterSlotList.appendChild(characterSlot); }); } function editCharacter(index) { const character = characterData[index]; const editForm = document.createElement('form'); editForm.innerHTML = ` <label style="color: var(--text-color);">Emoji: <input type="text" name="emoji" value="${character.emoji}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Name: <input type="text" name="name" value="${character.name}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Sex: <input type="text" name="sex" value="${character.sex}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Species: <input type="text" name="species" value="${character.species}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Age: <input type="text" name="age" value="${character.age}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Body Type: <input type="text" name="bodyType" value="${character.bodyType}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Personality: <input type="text" name="personality" value="${character.personality}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Bio: <textarea name="bio" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;">${character.bio}</textarea></label><br> <label style="color: var(--text-color);">User Controlled: <input type="checkbox" name="userControlled" ${character.userControlled ? 'checked' : ''}></label><br> <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 5px 10px; border-radius: 5px;">Save</button> `; editForm.addEventListener('submit', (event) => { event.preventDefault(); character.emoji = editForm.emoji.value; character.name = editForm.name.value; character.sex = editForm.sex.value; character.species = editForm.species.value; character.age = editForm.age.value; character.bodyType = editForm.bodyType.value; character.personality = editForm.personality.value; character.bio = editForm.bio.value; character.userControlled = editForm.userControlled.checked; renderCharacterSlots(); }); characterSlotList.innerHTML = ''; characterSlotList.appendChild(editForm); } function deleteCharacter(index) { characterData.splice(index, 1); renderCharacterSlots(); } function exportCharacter(character) { const json = JSON.stringify(character, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${character.name}.json`; link.click(); } // Location Sidebar const locationSidebar = document.createElement('div'); locationSidebar.id = 'location-sheet-sidebar'; locationSidebar.style.cssText = ` position: fixed; top: 0; left: -300px; width: 300px; height: 100%; display: flex; flex-direction: column; background-color: var(--bg-color); border-right: 2px solid var(--border-color); box-shadow: 2px 0 5px var(--shadow-color); box-sizing: border-box; transition: left 0.3s; z-index: 9999; `; document.body.appendChild(locationSidebar); const locationToggleButton = document.createElement('button'); locationToggleButton.textContent = '☰ Locations'; locationToggleButton.style.cssText = ` position: fixed; top: 10px; left: 10px; padding: 5px 10px; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; transition: left 0.3s; z-index: 10000; `; locationToggleButton.addEventListener('click', () => { const isOpen = locationSidebar.style.left === '0px'; locationSidebar.style.left = isOpen ? '-300px' : '0'; locationToggleButton.style.left = isOpen ? '10px' : '310px'; }); document.body.appendChild(locationToggleButton); const locationSlotList = document.createElement('div'); locationSlotList.id = 'location-slot-list'; locationSlotList.style.cssText = ` flex-grow: 1; overflow-y: auto; margin-top: 10px; `; locationSidebar.appendChild(locationSlotList); const globalInputs = document.createElement('div'); globalInputs.style.cssText = ` display: flex; justify-content: space-between; margin-bottom: 10px; `; const timeInput = document.createElement('input'); timeInput.type = 'text'; timeInput.placeholder = 'Time'; timeInput.style.cssText = ` width: calc(50% - 5px); padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; const weatherInput = document.createElement('input'); weatherInput.type = 'text'; weatherInput.placeholder = 'Weather'; weatherInput.style.cssText = ` width: calc(50% - 5px); padding: 5px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; globalInputs.appendChild(timeInput); globalInputs.appendChild(weatherInput); locationSidebar.appendChild(globalInputs); const locationButtonContainer = document.createElement('div'); locationButtonContainer.style.cssText = ` display: flex; flex-wrap: wrap; justify-content: space-between; `; const newLocationButton = document.createElement('button'); newLocationButton.textContent = 'New Location'; newLocationButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: calc(50% - 5px); `; newLocationButton.addEventListener('click', createNewLocation); const importLocationButton = document.createElement('button'); importLocationButton.textContent = 'Import Location'; importLocationButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--info-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: calc(50% - 5px); `; importLocationButton.addEventListener('click', importLocation); const exportAllLocationsButton = document.createElement('button'); exportAllLocationsButton.textContent = 'Export All'; exportAllLocationsButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--warning-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: 100%; `; exportAllLocationsButton.addEventListener('click', exportAllLocations); const importAllLocationsButton = document.createElement('button'); importAllLocationsButton.textContent = 'Import All'; importAllLocationsButton.style.cssText = ` margin: 5px 0; padding: 5px 10px; border: none; background-color: var(--muted-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; width: 100%; `; importAllLocationsButton.addEventListener('click', importAllLocations); locationButtonContainer.appendChild(newLocationButton); locationButtonContainer.appendChild(importLocationButton); locationButtonContainer.appendChild(exportAllLocationsButton); locationButtonContainer.appendChild(importAllLocationsButton); locationSidebar.appendChild(locationButtonContainer); let locationData = []; function createNewLocation() { const newLocation = { active: locationData.length === 0, emoji: '📍', name: 'New Location', description: '' }; locationData.push(newLocation); renderLocationSlots(); } function importLocation() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { const importedLocation = JSON.parse(e.target.result); locationData.push(importedLocation); renderLocationSlots(); }; reader.readAsText(file); }); fileInput.click(); } function exportAllLocations() { const json = JSON.stringify(locationData, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `all_locations.json`; link.click(); } function importAllLocations() { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.json'; fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { const importedLocations = JSON.parse(e.target.result); locationData = importedLocations; renderLocationSlots(); }; reader.readAsText(file); }); fileInput.click(); } function renderLocationSlots() { locationSlotList.innerHTML = ''; locationData.sort((a, b) => a.name.localeCompare(b.name)); locationData.forEach((location, index) => { const locationSlot = document.createElement('div'); locationSlot.className = 'location-slot'; locationSlot.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 10px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); `; const activeButton = document.createElement('button'); activeButton.textContent = location.active ? '🟢' : '🔴'; activeButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; activeButton.addEventListener('click', () => { locationData.forEach(loc => loc.active = false); location.active = true; renderLocationSlots(); }); const emojiSpan = document.createElement('span'); emojiSpan.textContent = location.emoji; emojiSpan.style.marginRight = '10px'; const nameSpan = document.createElement('span'); nameSpan.textContent = location.name; nameSpan.style.cssText = ` overflow: hidden; white-space: nowrap; text-overflow: ellipsis; max-width: 80px; color: var(--text-color); `; const editButton = document.createElement('button'); editButton.textContent = '✏️'; editButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; editButton.addEventListener('click', () => { editLocation(index); }); const deleteButton = document.createElement('button'); deleteButton.textContent = '🗑️'; deleteButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; deleteButton.addEventListener('click', () => { deleteLocation(index); }); const exportButton = document.createElement('button'); exportButton.textContent = '⬇️'; exportButton.style.cssText = ` padding: 3px; border: none; background: none; cursor: pointer; color: var(--text-color); `; exportButton.addEventListener('click', () => { exportLocation(location); }); locationSlot.appendChild(activeButton); locationSlot.appendChild(emojiSpan); locationSlot.appendChild(nameSpan); locationSlot.appendChild(editButton); locationSlot.appendChild(deleteButton); locationSlot.appendChild(exportButton); locationSlotList.appendChild(locationSlot); }); } function editLocation(index) { const location = locationData[index]; const editForm = document.createElement('form'); editForm.innerHTML = ` <label style="color: var(--text-color);">Emoji: <input type="text" name="emoji" value="${location.emoji}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Name: <input type="text" name="name" value="${location.name}" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;"></label><br> <label style="color: var(--text-color);">Description: <textarea name="description" style="background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 5px; width: 100%; box-sizing: border-box;">${location.description}</textarea></label><br> <button type="submit" style="background-color: var(--button-bg-color); color: var(--text-color); border: none; padding: 5px 10px; border-radius: 5px;">Save</button> `; editForm.addEventListener('submit', (event) => { event.preventDefault(); location.emoji = editForm.emoji.value; location.name = editForm.name.value; location.description = editForm.description.value; renderLocationSlots(); }); locationSlotList.innerHTML = ''; locationSlotList.appendChild(editForm); } function deleteLocation(index) { if (locationData[index].active && locationData.length > 1) { locationData.splice(index, 1); locationData[0].active = true; } else if (locationData.length === 1) { alert("At least one location must be active."); } else { locationData.splice(index, 1); } renderLocationSlots(); } function exportLocation(location) { const json = JSON.stringify(location, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${location.name}.json`; link.click(); } // Context Menu const contextMenuButton = document.createElement('button'); contextMenuButton.textContent = 'Context Menu'; contextMenuButton.style.cssText = ` position: fixed; top: 10px; left: 50%; transform: translateX(-50%); padding: 5px 10px; border: none; background-color: var(--button-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; z-index: 10000; `; document.body.appendChild(contextMenuButton); const contextPanel = document.createElement('div'); contextPanel.id = 'context-panel'; contextPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; padding: 20px; background-color: var(--bg-color); border: 1px solid var(--border-color); box-shadow: 0 0 10px var(--shadow-color); border-radius: 5px; display: none; z-index: 10001; `; document.body.appendChild(contextPanel); const transparencyLabel = document.createElement('label'); transparencyLabel.textContent = 'UI Transparency'; transparencyLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(transparencyLabel); const transparencySlider = document.createElement('input'); transparencySlider.type = 'range'; transparencySlider.min = '0.1'; transparencySlider.max = '1'; transparencySlider.step = '0.1'; transparencySlider.value = '0.9'; transparencySlider.style.cssText = ` width: 100%; margin-bottom: 10px; `; transparencySlider.addEventListener('input', () => { document.documentElement.style.setProperty('--ui-transparency', transparencySlider.value); // Re-apply current theme to update transparency setTheme(themeDropdown.value); GM.setValue('transparency', transparencySlider.value); }); contextPanel.appendChild(transparencySlider); // After creating transparencySlider transparencySlider.value = defaultTransparency; const dropperLabel = document.createElement('label'); dropperLabel.textContent = 'UI Theme'; dropperLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(dropperLabel); const themeDropdown = document.createElement('select'); themeDropdown.style.cssText = ` width: 100%; padding: 5px; margin-bottom: 10px; border: 1px solid var(--border-color); border-radius: 5px; background-color: var(--bg-color); color: var(--text-color); `; Object.keys(themes).forEach(theme => { const option = document.createElement('option'); option.value = theme; option.textContent = theme.charAt(0).toUpperCase() + theme.slice(1); themeDropdown.appendChild(option); }); themeDropdown.addEventListener('change', () => { setTheme(themeDropdown.value); GM.setValue('theme', themeDropdown.value); }); contextPanel.appendChild(themeDropdown); // After creating themeDropdown and adding options themeDropdown.value = defaultTheme; // Add divider const divider = document.createElement('hr'); divider.style.cssText = ` border: none; border-top: 1px solid var(--border-color); margin: 10px 0; `; contextPanel.appendChild(divider); const contextLabel = document.createElement('label'); contextLabel.textContent = 'Primary Context'; contextLabel.style.cssText = ` color: var(--text-color); display: block; margin-bottom: 5px; `; contextPanel.appendChild(contextLabel); const primaryContextInput = document.createElement('textarea'); primaryContextInput.placeholder = 'Primary context goes here...'; primaryContextInput.style.cssText = ` width: 100%; height: 100px; margin-bottom: 10px; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; box-sizing: border-box; background-color: var(--bg-color); color: var(--text-color); `; contextPanel.appendChild(primaryContextInput); const updateContextButton = document.createElement('button'); updateContextButton.textContent = 'Update Context'; updateContextButton.style.cssText = ` width: 100%; padding: 10px; border: none; background-color: var(--success-bg-color); color: var(--text-color); border-radius: 5px; cursor: pointer; `; // Define the sleep function function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } updateContextButton.addEventListener('click', async () => { const primaryContext = primaryContextInput.value; const fullContext = `Context;\n"` + primaryContext + `"\n\n` + window.getContext(); console.log("setting context; " + fullContext); window.manuClick('//*[@id="menu-button-:rb:"]'); await sleep(250); window.manuClick('//*[@id="menu-list-:rb:-menuitem-:ru:"]'); await sleep(250); window.manuWrite("/html/body/div[8]/div[3]/div/section/div/textarea", fullContext); await sleep(250); window.manuClick("/html/body/div[8]/div[3]/div/section/footer/button[2]"); }); window.manuWrite = function(xpath, text) { var result = document.evaluate( xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); var node = result.singleNodeValue; if (node) { node.focus(); // Since the node is an HTMLTextAreaElement, get the value setter from its prototype const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor( window.HTMLTextAreaElement.prototype, 'value' ).set; nativeTextAreaValueSetter.call(node, text); // Dispatch the 'input' event to simulate user input var event = new Event('input', { bubbles: true }); node.dispatchEvent(event); } else { console.error("Node not found for XPath:", xpath); } } window.manuClick = function(xpath) { var result = document.evaluate( xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); var node = result.singleNodeValue; if (node) { node.click(); } } contextPanel.appendChild(updateContextButton); contextMenuButton.addEventListener('click', () => { contextPanel.style.display = contextPanel.style.display === 'none' ? 'block' : 'none'; }); // Make getContext accessible from the console window.getContext = function() { const activeLocation = locationData.find(loc => loc.active); if (!activeLocation) return ''; let context = `Setting;\n"${activeLocation.name}" (${timeInput.value}, ${weatherInput.value}):\n`; context += `\u0009"${activeLocation.description}"\n`; context += `\n{{user}}'s characters;\n`; characterData .filter(char => char.active && char.userControlled) .forEach(char => { context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`; context += `\u0009"${char.bio}"\n`; }); context += `\n{{char}}'s characters;\n`; characterData .filter(char => char.active && !char.userControlled) .forEach(char => { context += `"${char.name}" (${char.sex}, ${char.species}, ${char.age}, ${char.bodyType}, ${char.personality}):\n`; context += `\u0009"${char.bio}"\n`; }); return context; }; // Initial render renderCharacterSlots(); renderLocationSlots(); })();