diff --git a/src/systems/generation/promptBuilder.js b/src/systems/generation/promptBuilder.js
index bb2c302..7cf787b 100644
--- a/src/systems/generation/promptBuilder.js
+++ b/src/systems/generation/promptBuilder.js
@@ -401,6 +401,7 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
}
instructions += '- Empty arrays [] for sections with no items\n';
+ instructions += '- Items may be added or removed from all sections\n';
instructions += '- null for main quest if none active\n';
// Add stat descriptions if any have descriptions
diff --git a/src/systems/rendering/thoughts.js b/src/systems/rendering/thoughts.js
index 436db8a..1bed375 100644
--- a/src/systems/rendering/thoughts.js
+++ b/src/systems/rendering/thoughts.js
@@ -478,6 +478,7 @@ export function renderThoughts() {
`;
@@ -541,6 +542,15 @@ export function renderThoughts() {
updateCharacterField(character, field, value);
});
+ // Add event handlers for remove character buttons
+ $thoughtsContainer.find('.rpg-character-remove').on('click', function(e) {
+ e.stopPropagation();
+ const characterName = $(this).data('character');
+ if (characterName && confirm(`Remove ${characterName} from present characters?`)) {
+ removeCharacter(characterName);
+ }
+ });
+
// Remove updating class after animation
if (extensionSettings.enableAnimations) {
setTimeout(() => $thoughtsContainer.removeClass('rpg-content-updating'), 600);
@@ -780,6 +790,112 @@ export function updateCharacterField(characterName, field, value) {
}
}
+/**
+ * Removes a character from Present Characters data and re-renders.
+ * Works with both structured (charactersData) and text (characterThoughts) formats.
+ *
+ * @param {string} characterName - Name of the character to remove
+ */
+export function removeCharacter(characterName) {
+ console.log('[RPG Companion] Removing character:', characterName);
+
+ // Remove from structured data if it exists
+ if (extensionSettings.charactersData && Array.isArray(extensionSettings.charactersData)) {
+ const initialLength = extensionSettings.charactersData.length;
+ extensionSettings.charactersData = extensionSettings.charactersData.filter(
+ char => char.name && char.name.toLowerCase() !== characterName.toLowerCase()
+ );
+ if (extensionSettings.charactersData.length < initialLength) {
+ console.log('[RPG Companion] Removed character from structured data');
+ }
+ }
+
+ // Remove from text format
+ if (!lastGeneratedData.characterThoughts) {
+ console.log('[RPG Companion] No characterThoughts data to remove from');
+ return;
+ }
+
+ const lines = lastGeneratedData.characterThoughts.split('\n');
+ const presentCharsConfig = extensionSettings.trackerConfig?.presentCharacters;
+
+ let characterFound = false;
+ let inTargetCharacter = false;
+ let characterStartIndex = -1;
+ let characterEndIndex = -1;
+ const linesToRemove = [];
+
+ // Find the character block
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i].trim();
+
+ if (line.startsWith('- ')) {
+ const name = line.substring(2).trim();
+ if (name.toLowerCase() === characterName.toLowerCase()) {
+ characterFound = true;
+ inTargetCharacter = true;
+ characterStartIndex = i;
+ linesToRemove.push(i);
+ } else if (inTargetCharacter) {
+ characterEndIndex = i;
+ break;
+ }
+ } else if (inTargetCharacter) {
+ // Include all lines until the next character or end of file
+ linesToRemove.push(i);
+ // Check if this is a character name line (next character)
+ if (line.startsWith('- ')) {
+ characterEndIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (characterFound && characterEndIndex === -1) {
+ characterEndIndex = lines.length;
+ }
+
+ if (characterFound && linesToRemove.length > 0) {
+ // Remove lines in reverse order to maintain indices
+ for (let i = linesToRemove.length - 1; i >= 0; i--) {
+ lines.splice(linesToRemove[i], 1);
+ }
+
+ // Clean up any trailing empty lines after removal
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
+ lines.pop();
+ }
+
+ lastGeneratedData.characterThoughts = lines.join('\n');
+ committedTrackerData.characterThoughts = lines.join('\n');
+
+ console.log('[RPG Companion] Removed character from text format');
+
+ // Update chat swipe data
+ const chat = getContext().chat;
+ if (chat && chat.length > 0) {
+ for (let i = chat.length - 1; i >= 0; i--) {
+ const message = chat[i];
+ if (!message.is_user) {
+ if (message.extra && message.extra.rpg_companion_swipes) {
+ const swipeId = message.swipe_id || 0;
+ if (message.extra.rpg_companion_swipes[swipeId]) {
+ message.extra.rpg_companion_swipes[swipeId].characterThoughts = lines.join('\n');
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ saveChatData();
+ renderThoughts();
+ updateChatThoughts();
+ } else {
+ console.log('[RPG Companion] Character not found in text format:', characterName);
+ }
+}
+
/**
* Updates or removes thought overlays in the chat.
* Creates floating thought bubbles positioned near character avatars.
diff --git a/style.css b/style.css
index 45e92ec..c8379e7 100644
--- a/style.css
+++ b/style.css
@@ -1957,6 +1957,38 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
white-space: nowrap; /* Prevent name from wrapping */
overflow: hidden;
text-overflow: ellipsis;
+ flex: 1;
+ min-width: 0;
+}
+
+/* Character remove button */
+.rpg-character-remove {
+ flex-shrink: 0;
+ background: rgba(255, 0, 0, 0.2);
+ border: 1px solid rgba(255, 0, 0, 0.4);
+ border-radius: 50%;
+ width: clamp(18px, 2.5vh, 22px);
+ height: clamp(18px, 2.5vh, 22px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: clamp(14px, 2vw, 18px);
+ color: var(--rpg-highlight);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ padding: 0;
+ line-height: 1;
+ margin-left: auto;
+}
+
+.rpg-character-remove:hover {
+ background: rgba(255, 0, 0, 0.4);
+ border-color: rgba(255, 0, 0, 0.6);
+ transform: scale(1.1);
+}
+
+.rpg-character-remove:active {
+ transform: scale(0.95);
}
/* Character traits/status line and custom fields */