feat: json format, et al.

This commit is contained in:
Subarashimo
2025-12-03 14:55:30 +01:00
parent 56349f30e6
commit 0f7fdfcef1
28 changed files with 5692 additions and 237 deletions
+3 -2
View File
@@ -87,8 +87,9 @@ class Internationalization {
});
}
getTranslation(key) {
return this.translations[key] || null;
getTranslation(key, fallback = null) {
// Return translation, or fallback, or the key itself (prevents "null" from showing)
return this.translations[key] || fallback || key;
}
async setLanguage(lang) {
+47 -4
View File
@@ -140,6 +140,13 @@ export function saveChatData() {
quests: extensionSettings.quests,
lastGeneratedData: lastGeneratedData,
committedTrackerData: committedTrackerData,
// Structured data (JSON format)
inventoryV3: extensionSettings.inventoryV3,
skillsV2: extensionSettings.skillsV2,
skillAbilityLinks: extensionSettings.skillAbilityLinks,
infoBoxData: extensionSettings.infoBoxData,
charactersData: extensionSettings.charactersData,
questsV2: extensionSettings.questsV2,
timestamp: Date.now()
};
@@ -247,14 +254,50 @@ export function loadChatData() {
};
}
// Restore last generated data
// Restore last generated data (sanitize null values in infoBox)
if (savedData.lastGeneratedData) {
setLastGeneratedData({ ...savedData.lastGeneratedData });
const sanitizedData = { ...savedData.lastGeneratedData };
if (sanitizedData.infoBox && typeof sanitizedData.infoBox === 'string') {
// Remove lines that contain "null" values
sanitizedData.infoBox = sanitizedData.infoBox
.split('\n')
.filter(line => !line.match(/:\s*null\s*$/i) && !line.match(/:\s*undefined\s*$/i))
.join('\n');
}
setLastGeneratedData(sanitizedData);
}
// Restore committed tracker data
// Restore committed tracker data (sanitize null values in infoBox)
if (savedData.committedTrackerData) {
setCommittedTrackerData({ ...savedData.committedTrackerData });
const sanitizedData = { ...savedData.committedTrackerData };
if (sanitizedData.infoBox && typeof sanitizedData.infoBox === 'string') {
// Remove lines that contain "null" values
sanitizedData.infoBox = sanitizedData.infoBox
.split('\n')
.filter(line => !line.match(/:\s*null\s*$/i) && !line.match(/:\s*undefined\s*$/i))
.join('\n');
}
setCommittedTrackerData(sanitizedData);
}
// Restore structured data (JSON format)
if (savedData.inventoryV3) {
extensionSettings.inventoryV3 = savedData.inventoryV3;
}
if (savedData.skillsV2) {
extensionSettings.skillsV2 = savedData.skillsV2;
}
if (savedData.skillAbilityLinks) {
extensionSettings.skillAbilityLinks = savedData.skillAbilityLinks;
}
if (savedData.infoBoxData) {
extensionSettings.infoBoxData = savedData.infoBoxData;
}
if (savedData.charactersData) {
extensionSettings.charactersData = savedData.charactersData;
}
if (savedData.questsV2) {
extensionSettings.questsV2 = savedData.questsV2;
}
// Migrate inventory in chat data if feature flag enabled
+48 -2
View File
@@ -20,6 +20,9 @@ export let extensionSettings = {
showCharacterThoughts: true,
showInventory: true, // Show inventory section (v2 system)
useSimplifiedInventory: false, // Use simplified single-list inventory instead of categorized (On Person/Stored/Assets)
showSkills: false, // Show skills as separate section (moves skills from Status to own tab)
enableItemSkillLinks: false, // Enable linking items to skills (item grants skill, removing item removes skill)
deleteSkillWithItem: false, // When true, deleting an item also deletes linked skills. When false (default), just unlinks.
showQuests: true, // Show quests section
showThoughtsInChat: true, // Show thoughts overlay in chat
enableHtmlPrompt: false, // Enable immersive HTML prompt injection
@@ -50,14 +53,52 @@ export let extensionSettings = {
arousal: 0,
mood: '😐',
conditions: 'None',
/** @type {InventoryV2} */
/** @type {InventoryV2} Legacy string-based inventory */
inventory: {
version: 2,
onPerson: "None",
stored: {},
assets: "None"
}
},
skills: "None" // Legacy single-string skills (for Status section)
},
/**
* Structured inventory v3 - items as objects with name, description, and skill links
* @type {{onPerson: Array<{name: string, description: string, grantsSkill?: string}>, stored: Object<string, Array>, assets: Array}}
*/
inventoryV3: {
onPerson: [], // Array of { name, description, grantsSkill? }
stored: {}, // { locationName: [{ name, description, grantsSkill? }] }
assets: [] // Array of { name, description }
},
/**
* Structured skills v2 - abilities as objects with name, description, and item links
* Key is the skill category name from config
* @type {Object<string, Array<{name: string, description: string, grantedBy?: string}>>}
*/
skillsV2: {
// Example: "Combat": [{ name: "Sword Fighting", description: "Blade proficiency", grantedBy: "Iron Sword" }]
},
/**
* Structured info box data (from JSON parsing)
*/
infoBoxData: {},
/**
* Structured characters data (from JSON parsing)
*/
charactersData: [],
/**
* Structured quests v2 (from JSON parsing)
*/
questsV2: {
main: null,
optional: []
},
// Legacy fields kept for backwards compatibility
skills: { list: [], categories: {} },
itemSkillLinks: {},
skillAbilityLinks: {},
skillsData: {},
statNames: {
health: 'Health',
satiety: 'Satiety',
@@ -250,6 +291,7 @@ export let $userStatsContainer = null;
export let $infoBoxContainer = null;
export let $thoughtsContainer = null;
export let $inventoryContainer = null;
export let $skillsContainer = null;
export let $questsContainer = null;
/**
@@ -319,6 +361,10 @@ export function setInventoryContainer($element) {
$inventoryContainer = $element;
}
export function setSkillsContainer($element) {
$skillsContainer = $element;
}
export function setQuestsContainer($element) {
$questsContainer = $element;
}