Revert "feat: responsive dashboard layout"

This commit is contained in:
Spicy Marinara
2025-11-06 20:05:33 +01:00
committed by GitHub
parent a8d5a88438
commit 71727c0a50
17 changed files with 238 additions and 2804 deletions
+5 -256
View File
@@ -56,10 +56,6 @@ function separateEmojiFromText(str) {
function stripBrackets(text) {
if (!text) return text;
const originalLength = text.length;
debugLog('[RPG Parser] stripBrackets: Input length:', originalLength);
debugLog('[RPG Parser] stripBrackets: Contains "Skills:":', text.includes('Skills:'));
// Remove leading and trailing whitespace first
text = text.trim();
@@ -71,7 +67,6 @@ function stripBrackets(text) {
(text.startsWith('(') && text.endsWith(')'))
) {
text = text.substring(1, text.length - 1).trim();
debugLog('[RPG Parser] stripBrackets: Removed wrapping brackets, new length:', text.length);
}
// Remove placeholder text patterns like [Location], [Mood Emoji], [Name], etc.
@@ -107,103 +102,23 @@ function stripBrackets(text) {
};
// Replace placeholders with empty string, keep real content
let removedPlaceholders = [];
text = text.replace(placeholderPattern, (match, content) => {
if (isPlaceholder(match, content)) {
removedPlaceholders.push(match);
return ''; // Remove placeholder
}
return match; // Keep real bracketed content
});
if (removedPlaceholders.length > 0) {
debugLog('[RPG Parser] stripBrackets: Removed placeholders:', removedPlaceholders.join(', '));
}
// Clean up any resulting empty labels (e.g., "Status: " with nothing after)
// BUT: Don't remove structural section headers that have content on following lines
const beforeCleanup = text.length;
// Known section headers that should NEVER be removed (structural markers)
const structuralHeaders = ['Skills', 'Status', 'Inventory', 'On Person', 'Stored', 'Assets', 'Main Quest', 'Main Quests', 'Optional Quest', 'Optional Quests'];
// Split into lines to intelligently remove only truly empty labels
const lines = text.split('\n');
const filteredLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmedLine = line.trim();
// Check if this is a label line (ends with colon, no other content)
const labelMatch = trimmedLine.match(/^([A-Za-z\s]+):\s*$/);
if (labelMatch) {
const labelName = labelMatch[1];
// Never remove structural section headers
if (structuralHeaders.includes(labelName)) {
debugLog('[RPG Parser] stripBrackets: Keeping structural header:', trimmedLine);
filteredLines.push(line);
continue;
}
// Check if there's ANY content in the next few lines (look ahead up to 3 lines)
let hasContentBelow = false;
for (let j = i + 1; j < Math.min(i + 4, lines.length); j++) {
const futureLine = lines[j].trim();
if (futureLine === '') continue; // Skip empty lines
// If we find a line with content (not just another label), this label has content below
if (futureLine && !/^([A-Za-z\s]+):\s*$/.test(futureLine)) {
hasContentBelow = true;
break;
}
}
if (hasContentBelow) {
// This label has content below (even if through other labels), keep it
debugLog('[RPG Parser] stripBrackets: Keeping section header:', trimmedLine);
filteredLines.push(line);
} else {
// This is a truly empty label with no content anywhere below, remove it
debugLog('[RPG Parser] stripBrackets: Removing empty label:', trimmedLine);
}
} else {
// Not a label line, keep it
filteredLines.push(line);
}
}
text = filteredLines.join('\n');
if (text.length !== beforeCleanup) {
debugLog('[RPG Parser] stripBrackets: Removed empty labels, chars removed:', beforeCleanup - text.length);
}
text = text.replace(/^([A-Za-z\s]+):\s*$/gm, ''); // Remove lines that are just "Label: " with nothing
text = text.replace(/^([A-Za-z\s]+):\s*,/gm, '$1:'); // Fix "Label: ," patterns
text = text.replace(/:\s*\|/g, ':'); // Fix ": |" patterns
text = text.replace(/\|\s*\|/g, '|'); // Fix "| |" patterns (double pipes from removed content)
text = text.replace(/\|\s*$/gm, ''); // Remove trailing pipes at end of lines
// Clean up multiple spaces and empty lines
const beforeSpaceCleanup = text.length;
text = text.replace(/\s{2,}/g, ' '); // Multiple spaces to single space
text = text.replace(/^\s*\n/gm, ''); // Remove empty lines
if (text.length !== beforeSpaceCleanup) {
debugLog('[RPG Parser] stripBrackets: Cleaned up spaces/newlines, chars removed:', beforeSpaceCleanup - text.length);
}
const finalLength = text.trim().length;
debugLog('[RPG Parser] stripBrackets: Output length:', finalLength);
debugLog('[RPG Parser] stripBrackets: Total chars removed:', originalLength - finalLength);
debugLog('[RPG Parser] stripBrackets: Contains "Skills:" after processing:', text.includes('Skills:'));
if (text.includes('Skills:')) {
const skillsIndex = text.indexOf('Skills:');
debugLog('[RPG Parser] stripBrackets: Text around Skills (index ' + skillsIndex + '):', text.substring(skillsIndex, skillsIndex + 200));
} else if (originalLength !== finalLength) {
debugLog('[RPG Parser] stripBrackets: WARNING - Skills section was removed! Last 200 chars:', text.substring(text.length - 200));
}
return text.trim();
}
@@ -218,164 +133,6 @@ function debugLog(message, data = null) {
}
}
/**
* Extract structured skills data from stats text
* Parses format:
* Skills:
* CategoryName:
* - SkillName (Lv X)
* - SkillName (Lv X)
* Uncategorized:
* - SkillName (Lv X)
*
* @param {string} statsText - Stats section text containing skills
* @returns {Object|null} Structured skills data or null if not found
*/
function extractSkills(statsText) {
if (!statsText) {
debugLog('[RPG Parser] extractSkills: No stats text provided');
return null;
}
debugLog('[RPG Parser] extractSkills: Searching for Skills section in text length:', statsText.length);
debugLog('[RPG Parser] extractSkills: Text contains "Skills:":', statsText.includes('Skills:'));
// Find the Skills section
const skillsMatch = statsText.match(/Skills:([\s\S]*?)(?=\n\n|On Person:|Stored|Assets:|Main Quest|Optional Quest|$)/i);
if (!skillsMatch) {
debugLog('[RPG Parser] extractSkills: Main regex did not match');
debugLog('[RPG Parser] extractSkills: Checking if "On Person:" exists:', statsText.includes('On Person:'));
debugLog('[RPG Parser] extractSkills: Text around Skills:', statsText.substring(statsText.indexOf('Skills:'), statsText.indexOf('Skills:') + 200));
// Fallback: try simple format "Skills: skill1, skill2"
const simpleMatch = statsText.match(/Skills:\s*(.+)/i);
if (simpleMatch) {
const skillsText = simpleMatch[1].trim();
debugLog('[RPG Parser] extractSkills: Simple format matched:', skillsText);
if (skillsText && skillsText !== 'None') {
// Return as string for backward compatibility
return skillsText;
}
}
debugLog('[RPG Parser] extractSkills: No Skills section found');
return null;
}
debugLog('[RPG Parser] extractSkills: Main regex matched, captured length:', skillsMatch[1].length);
const skillsSection = skillsMatch[1];
const skillsData = {
version: 1,
categories: {},
uncategorized: []
};
// Split into lines and process
const lines = skillsSection.split('\n').map(line => line.trim()).filter(line => line);
debugLog('[RPG Parser] Skills section lines:', lines);
let currentCategory = null;
for (const line of lines) {
// Check if this is a category header (ends with colon, no dash)
if (line.endsWith(':') && !line.startsWith('-')) {
currentCategory = line.slice(0, -1).trim();
debugLog(`[RPG Parser] Found category header: "${currentCategory}"`);
if (currentCategory !== 'Uncategorized' && !skillsData.categories[currentCategory]) {
skillsData.categories[currentCategory] = [];
debugLog(`[RPG Parser] Created category array for: "${currentCategory}"`);
}
continue;
}
// Check if this is a skill line (starts with -, has level info)
// Try numeric format first: "- Skill Name (Lv 5)"
let skillMatch = line.match(/^-\s*(.+?)\s*\(Lv\s*(\d+)\)/i);
if (skillMatch) {
const skillName = skillMatch[1].trim();
const level = parseInt(skillMatch[2], 10) || 1;
const skill = {
name: skillName,
level: level,
xp: 0,
maxXP: 100
};
if (currentCategory === 'Uncategorized' || currentCategory === null) {
debugLog(`[RPG Parser] Adding "${skillName}" to uncategorized (currentCategory="${currentCategory}")`);
skillsData.uncategorized.push(skill);
} else if (currentCategory && skillsData.categories[currentCategory]) {
debugLog(`[RPG Parser] Adding "${skillName}" to category "${currentCategory}"`);
skillsData.categories[currentCategory].push(skill);
} else {
debugLog(`[RPG Parser] ERROR: Could not add "${skillName}" - currentCategory="${currentCategory}", categoryExists=${!!skillsData.categories[currentCategory]}`);
// Fallback to uncategorized if category doesn't exist
skillsData.uncategorized.push(skill);
}
} else {
// Fallback: Try text-based proficiency format: "- Skill Name (Proficient)"
const textMatch = line.match(/^-\s*(.+?)\s*\((.+?)\)/i);
if (textMatch) {
const skillName = textMatch[1].trim();
const proficiencyText = textMatch[2].trim().toLowerCase();
// Map text proficiency to numeric level
const proficiencyMap = {
'initiated': 1,
'novice': 1,
'basic': 2,
'beginner': 2,
'intermediate': 4,
'proficient': 5,
'competent': 6,
'advanced': 7,
'expert': 8,
'mastered': 9,
'master': 9,
'grandmaster': 10,
'legendary': 10
};
const level = proficiencyMap[proficiencyText] || 5; // Default to 5 if unknown
const skill = {
name: skillName,
level: level,
xp: 0,
maxXP: 100
};
if (currentCategory === 'Uncategorized' || currentCategory === null) {
debugLog(`[RPG Parser] Adding "${skillName}" to uncategorized (currentCategory="${currentCategory}")`);
skillsData.uncategorized.push(skill);
} else if (currentCategory && skillsData.categories[currentCategory]) {
debugLog(`[RPG Parser] Adding "${skillName}" to category "${currentCategory}"`);
skillsData.categories[currentCategory].push(skill);
} else {
debugLog(`[RPG Parser] ERROR: Could not add "${skillName}" - currentCategory="${currentCategory}", categoryExists=${!!skillsData.categories[currentCategory]}`);
// Fallback to uncategorized if category doesn't exist
skillsData.uncategorized.push(skill);
}
}
}
}
// Return null if no skills were found
if (Object.keys(skillsData.categories).length === 0 && skillsData.uncategorized.length === 0) {
return null;
}
debugLog('[RPG Parser] Final skills data:', {
categories: Object.keys(skillsData.categories),
categoryCounts: Object.entries(skillsData.categories).map(([cat, skills]) => `${cat}: ${skills.length}`),
uncategorizedCount: skillsData.uncategorized.length
});
return skillsData;
}
/**
* Parses the model response to extract the different data sections.
* Extracts tracker data from markdown code blocks in the AI response.
@@ -413,13 +170,7 @@ export function parseResponse(responseText) {
const content = match[1].trim();
debugLog(`[RPG Parser] --- Code Block ${i + 1} ---`);
debugLog('[RPG Parser] Content length:', content.length);
debugLog('[RPG Parser] First 300 chars:', content.substring(0, 300));
debugLog('[RPG Parser] Contains "Skills:":', content.includes('Skills:'));
if (content.includes('Skills:')) {
const skillsIndex = content.indexOf('Skills:');
debugLog('[RPG Parser] Text around Skills (index ' + skillsIndex + '):', content.substring(skillsIndex, skillsIndex + 200));
}
// Check if this is a combined code block with multiple sections
const hasMultipleSections = (
@@ -600,12 +351,10 @@ export function parseUserStats(statsText) {
// Parse skills section if enabled
const skillsConfig = trackerConfig?.userStats?.skillsSection;
if (skillsConfig?.enabled) {
const skillsData = extractSkills(statsText);
if (skillsData) {
extensionSettings.userStats.skills = skillsData;
debugLog('[RPG Parser] Skills extracted:', skillsData);
} else {
debugLog('[RPG Parser] Skills extraction failed or none found');
const skillsMatch = statsText.match(/Skills:\s*(.+)/i);
if (skillsMatch) {
extensionSettings.userStats.skills = skillsMatch[1].trim();
debugLog('[RPG Parser] Skills extracted:', skillsMatch[1].trim());
}
}
+3 -54
View File
@@ -10,53 +10,6 @@ import { extensionSettings, committedTrackerData, FEATURE_FLAGS } from '../../co
// Type imports
/** @typedef {import('../../types/inventory.js').InventoryV2} InventoryV2 */
/**
* Builds a formatted skills summary for AI context injection.
* Converts structured skills data to multi-line plaintext format organized by category.
*
* @param {Object|string} skills - Current skills (structured or legacy string)
* @returns {string} Formatted skills summary for prompt injection
* @example
* // Structured input: { version: 1, categories: { Combat: [{name: 'Swordsmanship', level: 5}] }, uncategorized: [] }
* // Returns: "Skills:\nCombat:\n- Swordsmanship (Lv 5)"
*/
export function buildSkillsSummary(skills) {
// Handle legacy string format
if (typeof skills === 'string') {
return `Skills: ${skills}`;
}
// Handle structured format
if (skills && typeof skills === 'object' && skills.version) {
let summary = 'Skills:';
const categories = skills.categories || {};
const uncategorized = skills.uncategorized || [];
// Add categorized skills
for (const [categoryName, skillsList] of Object.entries(categories)) {
if (skillsList && skillsList.length > 0) {
summary += `\n${categoryName}:`;
for (const skill of skillsList) {
summary += `\n- ${skill.name} (Lv ${skill.level})`;
}
}
}
// Add uncategorized skills
if (uncategorized.length > 0) {
summary += '\nUncategorized:';
for (const skill of uncategorized) {
summary += `\n- ${skill.name} (Lv ${skill.level})`;
}
}
return summary;
}
// Empty or invalid
return 'Skills: None';
}
/**
* Builds a formatted inventory summary for AI context injection.
* Converts v2 inventory structure to multi-line plaintext format.
@@ -213,13 +166,9 @@ export function generateTrackerInstructions(includeHtmlPrompt = true, includeCon
// Add skills section if enabled
if (userStatsConfig?.skillsSection?.enabled) {
instructions += `Skills:\n`;
instructions += `[Category Name]:\n`;
instructions += `- [Skill Name] (Lv [1-100])\n`;
instructions += `- [Another Skill] (Lv [1-100])\n`;
instructions += `Uncategorized:\n`;
instructions += `- [Uncategorized Skill] (Lv [1-100])\n`;
instructions += `(Organize skills by logical categories like Combat, Magic, Social, Crafting, etc. IMPORTANT: Use numeric levels only - write "Lv 5" not "Proficient", "Lv 7" not "Advanced". Use integers 1-100 where 1=novice, 5=intermediate, 10=expert. Skills without a clear category go in Uncategorized.)\n`;
const skillFields = userStatsConfig.skillsSection.customFields || [];
const skillFieldsText = skillFields.map(f => `[${f}]`).join(', ');
instructions += `Skills: [${skillFieldsText || 'Skill1, Skill2, etc.'}]\n`;
}
// Add inventory format based on feature flag