diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b088f65d..3ecb1f8c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,6 +16,9 @@ jobs: - name: Build Packs run: npm run pullYMLtoLDB + - name: Build daggerheart.js + run: npm run build + # get part of the tag after the `v` - name: Extract tag version number id: get_version @@ -34,7 +37,7 @@ jobs: download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip # Create a zip file with all files required by the module to add to the release - - run: zip -r ./system.zip system.json README.md LICENSE daggerheart.mjs templates/ styles/daggerheart.css packs/ lang/ + - run: zip -r ./system.zip system.json README.md LICENSE build/daggerheart.js assets/ templates/ styles/daggerheart.css packs/ lang/ # Create a release for this specific version - name: Update Release with Files @@ -46,6 +49,6 @@ jobs: draft: ${{ github.event.release.unpublished }} prerelease: ${{ github.event.release.prerelease }} token: ${{ secrets.GITHUB_TOKEN }} - artifacts: './module.json, ./module.zip' + artifacts: './system.json, ./system.zip' tag: ${{ github.event.release.tag_name }} body: ${{ github.event.release.body }} \ No newline at end of file diff --git a/daggerheart.mjs b/daggerheart.mjs index e09b26b8..002b68f2 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -4,18 +4,23 @@ import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; import DhCombatTracker from './module/ui/combatTracker.mjs'; -import { GMUpdateEvent, handleSocketEvent, socketEvent } from './module/helpers/socket.mjs'; +import { handleSocketEvent, registerSocketHooks } from './module/helpers/socket.mjs'; import { registerDHSettings } from './module/applications/settings.mjs'; import DhpChatLog from './module/ui/chatLog.mjs'; import DhpRuler from './module/ui/ruler.mjs'; import DhpTokenRuler from './module/ui/tokenRuler.mjs'; -import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs'; +import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs'; import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs'; import { abilities } from './module/config/actorConfig.mjs'; import Resources from './module/applications/resources.mjs'; +import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/countdowns.mjs'; import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs'; import { DualityRollColor } from './module/data/settings/Appearance.mjs'; import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/applications/roll.mjs' +import { DhMeasuredTemplate } from './module/placeables/_module.mjs'; +import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs'; +import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs'; +import { registerCountdownHooks } from './module/data/countdowns.mjs'; globalThis.SYSTEM = SYSTEM; @@ -28,10 +33,18 @@ Hooks.once('init', () => { documents }; - CONFIG.TextEditor.enrichers.push({ - pattern: /\[\[\/dr\s?(.*?)\]\]/g, - enricher: dualityRollEnricher - }); + CONFIG.TextEditor.enrichers.push( + ...[ + { + pattern: /\[\[\/dr\s?(.*?)\]\]/g, + enricher: DhDualityRollEnricher + }, + { + pattern: /^@Template\[(.*)\]$/g, + enricher: DhTemplateEnricher + } + ] + ); CONFIG.statusEffects = Object.values(SYSTEM.GENERAL.conditions).map(x => ({ ...x, @@ -47,6 +60,7 @@ Hooks.once('init', () => { }; CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]]; + CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate; CONFIG.Item.documentClass = documents.DhpItem; @@ -75,14 +89,19 @@ Hooks.once('init', () => { Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true }); CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect; - DocumentSheetConfig.unregisterSheet( + foundry.applications.apps.DocumentSheetConfig.unregisterSheet( CONFIG.ActiveEffect.documentClass, 'core', foundry.applications.sheets.ActiveEffectConfig ); - DocumentSheetConfig.registerSheet(CONFIG.ActiveEffect.documentClass, SYSTEM.id, applications.DhActiveEffectConfig, { - makeDefault: true - }); + foundry.applications.apps.DocumentSheetConfig.registerSheet( + CONFIG.ActiveEffect.documentClass, + SYSTEM.id, + applications.DhActiveEffectConfig, + { + makeDefault: true + } + ); CONFIG.Combat.dataModels = { base: models.DhCombat @@ -117,78 +136,50 @@ Hooks.once('init', () => { Hooks.on('ready', () => { ui.resources = new CONFIG.ui.resources(); - if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear) !== 'hide') + if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide') ui.resources.render({ force: true }); + document.body.classList.toggle( 'theme-colorful', game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme === DualityRollColor.colorful.value ); + + registerCountdownHooks(); + registerSocketHooks(); + registerCountdownApplicationHooks(); }); Hooks.once('dicesoniceready', () => {}); -Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => { - if (game.user.isGM) { - const document = uuid ? await fromUuid(uuid) : null; - switch (action) { - case GMUpdateEvent.UpdateDocument: - if (document && update) { - await document.update(update); - } - break; - case GMUpdateEvent.UpdateFear: - if (game.user.isGM) { - await game.settings.set( - SYSTEM.id, - SYSTEM.SETTINGS.gameSettings.Resources.Fear, - Math.max(Math.min(update, 6), 0) - ); - Hooks.callAll(socketEvent.DhpFearUpdate); - await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate }); - } - break; - } - } -}); - -const renderDualityButton = async event => { - const button = event.currentTarget, - traitValue = button.dataset.trait?.toLowerCase(), - target = getCommandTarget(); - if (!target) return; - - const config = { - event: event, - title: button.dataset.title, - roll: { - modifier: traitValue ? target.system.traits[traitValue].value : null, - label: button.dataset.label, - type: button.dataset.actionType ?? null // Need check - }, - chatMessage: { - template: 'systems/daggerheart/templates/chat/duality-roll.hbs' - } - }; - await target.diceRoll(config); -}; - Hooks.on('renderChatMessageHTML', (_, element) => { element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); + + element + .querySelectorAll('.measured-template-button') + .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); }); Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => { element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); + + element + .querySelectorAll('.measured-template-button') + .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); }); Hooks.on('renderHandlebarsApplication', (_, element) => { element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); + + element + .querySelectorAll('.measured-template-button') + .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); }); Hooks.on('chatMessage', (_, message) => { @@ -266,6 +257,29 @@ Hooks.on('chatMessage', (_, message) => { } }); +Hooks.on('renderJournalDirectory', async (tab, html, _, options) => { + if (tab.id === 'journal') { + if (options.parts && !options.parts.includes('footer')) return; + + const buttons = tab.element.querySelector('.directory-footer.action-buttons'); + const title = game.i18n.format('DAGGERHEART.Countdown.Title', { + type: game.i18n.localize('DAGGERHEART.Countdown.Types.narrative') + }); + buttons.insertAdjacentHTML( + 'afterbegin', + ` + ` + ); + + buttons.querySelector('#narrative-countdown-button').onclick = async () => { + new NarrativeCountdowns().open(); + }; + } +}); + const preloadHandlebarsTemplates = async function () { return foundry.applications.handlebars.loadTemplates([ 'systems/daggerheart/templates/sheets/parts/attributes.hbs', @@ -299,6 +313,7 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/views/actionTypes/roll.hbs', 'systems/daggerheart/templates/views/actionTypes/cost.hbs', 'systems/daggerheart/templates/views/actionTypes/range-target.hbs', - 'systems/daggerheart/templates/views/actionTypes/effect.hbs' + 'systems/daggerheart/templates/views/actionTypes/effect.hbs', + 'systems/daggerheart/templates/settings/components/settings-item-line.hbs' ]); }; diff --git a/lang/en.json b/lang/en.json index 4d40c98c..ddd6a3c4 100755 --- a/lang/en.json +++ b/lang/en.json @@ -24,10 +24,7 @@ "Automation": { "Name": "Automation Settings", "Label": "Configure Automation", - "Hint": "Various settings automating resource management and more", - "HopeLabel": "Hope", - "FearLabel": "Fear", - "ActionPointsLabel": "Action Points" + "Hint": "Various settings automating resource management and more" }, "Homebrew": { "Name": "Homebrew Settings", @@ -38,13 +35,7 @@ "Range": { "Name": "Range Settings", "Label": "Configure Range Handling", - "Hint": "System ruler setup for displaying ranges in Daggerheart", - "EnabledLabel": "Enabled", - "MeleeLabel": "Melee", - "VeryCloseLabel": "Very Close", - "CloseLabel": "Close", - "FarLabel": "Far", - "VeryFarLabel": "Very Far" + "Hint": "System ruler setup for displaying ranges in Daggerheart" }, "Appearance": { "title": "Appearance Settings", @@ -69,18 +60,45 @@ "actionTokens": "Action Tokens" } }, - "Automation": { - "Hope": { - "Name": "Hope", - "Hint": "Automatically increase a character's hope on a hope duality roll result." + "Appearance": { + "FIELDS": { + "displayFear": { "label": "Fear Display" } }, + "FearDisplay": { + "Token": "Tokens", + "Bar": "Bar", + "Hide": "Hide" + } + }, + "Automation": { "Fear": { "Name": "Fear", "Hint": "Automatically increase the GM's fear pool on a fear duality roll result." }, - "ActionPoints": { - "Name": "Action Points", - "Hint": "Automatically give and take Action Points as combatants take their turns." + "FIELDS": { + "hope": { + "label": "Hope", + "hint": "Automatically increase a character's hope on a hope duality roll result." + }, + "actionPoints": { + "label": "Action Points", + "hint": "Automatically give and take Action Points as combatants take their turns." + }, + "countdowns": { + "label": "Countdowns", + "hint": "Automatically progress non-custom countdowns" + } + } + }, + "Homebrew": { + "NewDowntimeMove": "Downtime Move", + "DowntimeMoves": "Downtime Moves", + "NrChoices": "# Moves Per Rest", + "ResetMovesTitle": "Reset {type} Downtime Moves", + "ResetMovesText": "Are you sure you want to reset?", + "FIELDS": { + "maxFear": { "label": "Max Fear" }, + "traitArray": { "label": "Initial Trait Modifiers" } } }, "Resources": { @@ -113,6 +131,10 @@ "hint": "Give each player action tokens to use in combat" }, "FIELDS": { + "actionTokens": { + "enabled": { "label": "Enabled" }, + "tokens": { "label": "Tokens" } + }, "useCoins": { "label": "Use Coins", "hint": "test" @@ -163,7 +185,9 @@ "OK": "OK", "Cancel": "Cancel", "Or": "Or", + "Enabled": "Enabled", "Description": "Description", + "Modifier": "Modifier", "Features": "Features", "proficiency": "Proficiency", "unarmored": "Unarmored", @@ -352,39 +376,39 @@ } }, "Domains": { - "Arcana": { + "arcana": { "label": "Arcana", "Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled." }, - "Blade": { + "blade": { "label": "Blade", "Description": "This is the domain of those who dedicate their lives to the mastery of weapons. Whether by blade, bow, or perhaps a more specialized arm, those who follow this path have the skill to cut short the lives of others. Blade requires study and dedication from its followers, in exchange for inexorable power over death." }, - "Bone": { + "bone": { "label": "Bone", "Description": "This is the domain of mastery of swiftness and tactical mastery. Practitioners of this domain have an uncanny control over their own physical abilities, and an eye for predicting the behaviors of others in combat. Bone grants its adherents unparalleled understanding of bodies and their movements in exchange for diligent training." }, - "Codex": { + "codex": { "label": "Codex", "Description": "This is the domain of intensive magical study. Those who seek magical knowledge turn to the recipes of power recorded in books, on scrolls, etched into walls, or tattooed on bodies. Codex offers a commanding and versatile understanding of magic to those devotees who are willing to seek beyond the common knowledge." }, - "Grace": { + "grace": { "label": "Grace", "Description": "This is the domain of charisma. Through rapturous storytelling, clever charm, or a shroud of lies, those who channel this power define the realities of their adversaries, bending perception to their will. Grace offers its wielders raw magnetism and mastery over language." }, - "Midnight": { + "midnight": { "label": "Midnight", "Description": "This is the domain of shadows and secrecy. Whether by clever tricks, or cloak of night those who channel these forces are practiced in that art of obscurity and there is nothing hidden they cannot reach. Midnight offers practitioners the incredible power to control and create enigmas." }, - "Sage": { + "sage": { "label": "Sage", "Description": "This is the domain of the natural world. Those who walk this path tap into the unfettered power of the earth and its creatures to unleash raw magic. Sage grants its adherents the vitality of a blooming flower and ferocity of a hungry predator." }, - "Splendor": { + "splendor": { "label": "Splendor", "Description": "This is the domain of life. Through this magic, followers gain the ability to heal, though such power also grants the wielder some control over death. Splendor offers its disciples the magnificent ability to both give and end life." }, - "Valor": { + "valor": { "label": "Valor", "Description": "This is the domain of protection. Whether through attack or defense, those who choose this discipline channel formidable strength to protect their allies in battle. Valor offers great power to those who raise their shield in defense of others." } @@ -402,6 +426,45 @@ "requestingSpotlight": "Requesting The Spotlight", "combatStarted": "Active" }, + "CharacterCreation": { + "Title": "{actor} - Character Setup", + "TraitIncreases": "Trait Increases", + "SuggestedTraits": "Suggested Traits", + "InitialExperiences": "Initial Experiences", + "Heritage": "Heritage", + "SelectAncestry": "Select Ancestry", + "SelectCommunity": "Select Community", + "SelectClass": "Select Class", + "SelectSubclass": "Select Subclass", + "SelectArmor": "Select Armor", + "SelectPrimaryWeapon": "Select Primary Weapon", + "SelectSecondaryWeapon": "Select Secondary Weapon", + "SuggestedArmor": "Suggested Armor", + "SuggestedWeapons": "Suggested Weapon", + "SuggestedPrimaryWeapon": "Suggested Primary Weapon", + "SuggestedSecondaryWeapon": "Suggested Secondary Weapon", + "StartingItems": "Starting Items", + "Choice": "Choice", + "NewExperience": "New Experience..", + "FinishCreation": "Finish Character Setup", + "Tabs": { + "Optional": "Optional", + "Setup": "Setup", + "Equipment": "Equipment", + "Story": "Story" + }, + "Notifications": { + "SubclassNotInClass": "This subclass does not belong to your selected class.", + "MissingClass": "You don't have a class selected yet.", + "WrongDomain": "The card isn't from one of your class domains.", + "CardTooHighLevel": "The card is too high level!", + "DuplicateCard": "You cannot select the same card more than once.", + "NotPrimary": "The weapon is not a primary weapon!", + "NotSecondary": "The weapon is not a secondary weapon!", + "ItemTooHighTier": "The item must be from Tier1", + "PrimaryIsTwoHanded": "Cannot select a secondary weapon with a two-handed primary!" + } + }, "LevelUp": { "Options": { "trait": "Gain a +1 bonus to two unmarked character traits and mark them.", @@ -445,29 +508,51 @@ } }, "Downtime": { - "TendToWounds": { - "Name": "Tend to Wounds", - "Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead." + "DowntimeHeader": "Downtime Moves ({current}/{max})", + "ShortRest": { + "title": "Short Rest", + "TendToWounds": { + "Name": "Tend to Wounds", + "Description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead." + }, + "ClearStress": { + "Name": "Clear Stress", + "Description": "Describe how you blow off steam or pull yourself together, then clear a number of Stress equal to 1d4 + your tier." + }, + "RepairArmor": { + "Name": "Repair Armor", + "Description": "Describe how you quickly repair your armor, then clear a number of Armor Slots equal to 1d4 + your tier. You can do this to an ally's armor instead." + }, + "Prepare": { + "Name": "Prepare", + "Description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope." + } }, - "ClearStress": { - "Name": "Clear Stress", - "Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress." + "LongRest": { + "title": "Long Rest", + "TendToWounds": { + "Name": "Tend to Wounds", + "Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead." + }, + "ClearStress": { + "Name": "Clear Stress", + "Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress." + }, + "RepairArmor": { + "Name": "Repair Armor", + "Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally's armor instead." + }, + "Prepare": { + "Name": "Prepare", + "Description": "Describe how you are preparing for the next day's adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope." + }, + "WorkOnAProject": { + "Name": "Work on a Project", + "Description": "Establish or continue work on a project." + } }, - "RepairArmor": { - "Name": "Repair Armor", - "Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally’s armor instead." - }, - "Prepare": { - "Name": "Prepare", - "Description": "Describe how you are preparing for the next day’s adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope." - }, - "WorkOnAProject": { - "Name": "Work on a Project", - "Description": "Establish or continue work on a project. The GM might ask for a roll to determine how much to tick down on the completion track." - }, - "Custom": { - "NamePlaceholder": "Custom Activity...", - "Placeholder": "A custom downtime activity description..." + "Notifications": { + "NoMoreMoves": "You cannot select any more downtime moves" } }, "DeathMoves": { @@ -960,6 +1045,45 @@ "Title": "Downtime" } }, + "Countdown": { + "FIELDS": { + "countdowns": { + "element": { + "name": { "label": "Name" }, + "progress": { + "current": { "label": "Current" }, + "max": { "label": "Max" }, + "type": { + "value": { "label": "Value" }, + "label": { "label": "Label", "hint": "Used for custom" } + } + } + } + } + }, + "Type": { + "Spotlight": "Spotlight", + "Custom": "Custom", + "CharacterAttack": "Character Attack" + }, + "NewCountdown": "New Countdown", + "AddCountdown": "Add Countdown", + "RemoveCountdownTitle": "Remove Countdown", + "RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?", + "OpenOwnership": "Edit Player Ownership", + "Title": "{type} Countdowns", + "Types": { + "narrative": "Narrative", + "encounter": "Encounter" + }, + "Notifications": { + "LimitedOwnershipMaximise": "You don't have permission to enter edit view" + } + }, + "OwnershipSelection": { + "Title": "Ownership Selection - {name}", + "Default": "Default Ownership" + }, "Sheets": { "PC": { "Name": "Name", diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index 5ee13189..b1a1d59e 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -13,5 +13,3 @@ export { default as DhpArmor } from './sheets/items/armor.mjs'; export { default as DhpChatMessage } from './chatMessage.mjs'; export { default as DhpEnvironment } from './sheets/environment.mjs'; export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs'; - -export * as pseudoDocumentSheet from './sheets/pseudo-documents/_module.mjs'; diff --git a/module/applications/characterCreation.mjs b/module/applications/characterCreation.mjs new file mode 100644 index 00000000..da76d0a9 --- /dev/null +++ b/module/applications/characterCreation.mjs @@ -0,0 +1,508 @@ +import { abilities } from '../config/actorConfig.mjs'; +import { burden } from '../config/generalConfig.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhCharacterCreation extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(character) { + super({}); + + this.character = character; + + this.setup = { + traits: this.character.system.traits, + ancestry: this.character.system.ancestry ?? {}, + community: this.character.system.community ?? {}, + class: this.character.system.class?.value ?? {}, + subclass: this.character.system.class?.subclass ?? {}, + experiences: { + [foundry.utils.randomID()]: { description: '', value: 2 }, + [foundry.utils.randomID()]: { description: '', value: 2 } + }, + domainCards: { + [foundry.utils.randomID()]: {}, + [foundry.utils.randomID()]: {} + }, + visibility: 1 + }; + + this.equipment = { + armor: {}, + primaryWeapon: {}, + secondaryWeapon: {}, + inventory: { + take: {}, + choiceA: {}, + choiceB: {} + } + }; + + this._dragDrop = this._createDragDropHandlers(); + } + + get title() { + return game.i18n.format('DAGGERHEART.CharacterCreation.Title', { actor: this.character.name }); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'dialog', 'dh-style', 'character-creation'], + position: { width: 800, height: 'auto' }, + actions: { + viewCompendium: this.viewCompendium, + viewItem: this.viewItem, + useSuggestedTraits: this.useSuggestedTraits, + equipmentChoice: this.equipmentChoice, + finish: this.finish + }, + form: { + handler: this.updateForm, + submitOnChange: true, + closeOnSubmit: false + }, + dragDrop: [ + { dragSelector: null, dropSelector: '.ancestry-card' }, + { dragSelector: null, dropSelector: '.community-card' }, + { dragSelector: null, dropSelector: '.class-card' }, + { dragSelector: null, dropSelector: '.subclass-card' }, + { dragSelector: null, dropSelector: '.domain-card' }, + { dragSelector: null, dropSelector: '.armor-card' }, + { dragSelector: null, dropSelector: '.primary-weapon-card' }, + { dragSelector: null, dropSelector: '.secondary-weapon-card' }, + { dragSelector: '.suggestion-inner-container', dropSelector: '.selections-container' } + ] + }; + + static PARTS = { + tabs: { template: 'systems/daggerheart/templates/views/characterCreation/tabs.hbs' }, + setup: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/setup.hbs' }, + equipment: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/equipment.hbs' }, + // story: { template: 'systems/daggerheart/templates/views/characterCreation/tabs/story.hbs' }, + footer: { template: 'systems/daggerheart/templates/views/characterCreation/footer.hbs' } + }; + + static TABS = { + setup: { + active: true, + cssClass: '', + group: 'primary', + id: 'setup', + label: 'DAGGERHEART.CharacterCreation.Tabs.Setup' + }, + equipment: { + active: false, + cssClass: '', + group: 'primary', + id: 'equipment', + label: 'DAGGERHEART.CharacterCreation.Tabs.Equipment', + optional: true + } + // story: { + // active: false, + // cssClass: '', + // group: 'primary', + // id: 'story', + // label: 'DAGGERHEART.CharacterCreation.Tabs.Story', + // optional: true + // } + }; + + _getTabs(tabs) { + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.cssClass = v.active ? 'active' : ''; + + switch (v.id) { + case 'setup': + const classFinished = this.setup.class.uuid && this.setup.subclass.uuid; + const heritageFinished = this.setup.ancestry.uuid && this.setup.community.uuid; + const traitsFinished = Object.values(this.setup.traits).every(x => x.value !== null); + const experiencesFinished = Object.values(this.setup.experiences).every(x => x.description); + const domainCardsFinished = Object.values(this.setup.domainCards).every(x => x.uuid); + v.finished = + classFinished && + heritageFinished && + traitsFinished && + experiencesFinished && + domainCardsFinished; + break; + case 'equipment': + const armorFinished = this.equipment.armor?.uuid; + const primaryFinished = this.equipment.primaryWeapon?.uuid; + const secondaryFinished = + this.equipment.secondaryWeapon?.uuid || + (primaryFinished && this.equipment.primaryWeapon.system.burden == burden.twoHanded.value); + const choiceAFinished = this.equipment.inventory.choiceA?.uuid; + const choiceBFinished = this.equipment.inventory.choiceB?.uuid; + + v.finished = + armorFinished && primaryFinished && secondaryFinished && choiceAFinished && choiceBFinished; + } + } + + tabs.equipment.cssClass = tabs.setup.finished ? tabs.equipment.cssClass : 'disabled'; + // tabs.story.cssClass = tabs.setup.finished ? tabs.story.cssClass : 'disabled'; + + return tabs; + } + + changeTab(tab, group, options) { + super.changeTab(tab, group, options); + + for (var listTab of Object.keys(this.constructor.TABS)) { + const marker = options.navElement.querySelector(`a[data-action="tab"].${listTab} .finish-marker`); + if (listTab === tab) { + marker.classList.add('active'); + } else { + marker.classList.remove('active'); + } + } + } + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + this._dragDrop.forEach(d => d.bind(htmlElement)); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.tabs = this._getTabs(this.constructor.TABS); + + return context; + } + + async _preparePartContext(partId, context) { + switch (partId) { + case 'setup': + const availableTraitModifiers = game.settings + .get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew) + .traitArray.map(trait => ({ key: trait, name: trait })); + for (let trait of Object.values(this.setup.traits).filter(x => x.value !== null)) { + const index = availableTraitModifiers.findIndex(x => x.key === trait.value); + if (index !== -1) { + availableTraitModifiers.splice(index, 1); + } + } + + context.suggestedTraits = this.setup.class.system + ? Object.keys(this.setup.class.system.characterGuide.suggestedTraits).map(traitKey => { + const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey]; + return `${game.i18n.localize(`DAGGERHEART.Abilities.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`; + }) + : []; + context.traits = { + values: Object.keys(this.setup.traits).map(traitKey => { + const trait = this.setup.traits[traitKey]; + const options = [...availableTraitModifiers]; + if (trait.value !== null && !options.some(x => x.key === trait.value)) + options.push({ key: trait.value, name: trait.value }); + + return { + ...trait, + key: traitKey, + name: game.i18n.localize(abilities[traitKey].label), + options: options + }; + }) + }; + context.traits.nrTotal = Object.keys(context.traits.values).length; + context.traits.nrSelected = Object.values(context.traits.values).reduce( + (acc, trait) => acc + (trait.value !== null ? 1 : 0), + 0 + ); + + context.experience = { + values: this.setup.experiences, + nrTotal: Object.keys(this.setup.experiences).length, + nrSelected: Object.values(this.setup.experiences).reduce( + (acc, exp) => acc + (exp.description ? 1 : 0), + 0 + ) + }; + + context.ancestry = { ...this.setup.ancestry, compendium: 'ancestries' }; + context.community = { ...this.setup.community, compendium: 'communities' }; + context.class = { ...this.setup.class, compendium: 'classes' }; + context.subclass = { ...this.setup.subclass, compendium: 'subclasses' }; + context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => { + acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' }; + return acc; + }, {}); + + context.visibility = this.setup.visibility; + break; + case 'equipment': + const suggestions = await this.getEquipmentSuggestions( + this.equipment.inventory.choiceA, + this.equipment.inventory.choiceB + ); + context.armor = { + ...this.equipment.armor, + suggestion: { ...suggestions.armor, taken: suggestions.armor?.uuid === this.equipment.armor?.uuid }, + compendium: 'armors' + }; + context.primaryWeapon = { + ...this.equipment.primaryWeapon, + suggestion: { + ...suggestions.primaryWeapon, + taken: suggestions.primaryWeapon?.uuid === this.equipment.primaryWeapon?.uuid + }, + compendium: 'weapons' + }; + context.secondaryWeapon = { + ...this.equipment.secondaryWeapon, + suggestion: { + ...suggestions.secondaryWeapon, + taken: suggestions.secondaryWeapon?.uuid === this.equipment.secondaryWeapon?.uuid + }, + disabled: this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value, + compendium: 'weapons' + }; + context.inventory = { + take: suggestions.inventory.take, + choiceA: { suggestions: suggestions.inventory.choiceA, compendium: 'consumables' }, + choiceB: { suggestions: suggestions.inventory.choiceB, compendium: 'general-items' } + }; + + break; + } + + return context; + } + + static async updateForm(event, _, formData) { + this.setup = foundry.utils.mergeObject(this.setup, formData.object); + + this.setup.visibility = this.getUpdateVisibility(); + this.render(); + } + + getUpdateVisibility() { + switch (this.setup.visibility) { + case 5: + return 5; + case 4: + return Object.values(this.setup.experiences).every(x => x.description) ? 5 : 4; + case 3: + return Object.values(this.setup.traits).every(x => x.value !== null) ? 4 : 3; + case 2: + return this.setup.ancestry.uuid && this.setup.community.uuid ? 3 : 2; + case 1: + return this.setup.class.uuid && this.setup.subclass.uuid ? 2 : 1; + } + } + + async getEquipmentSuggestions(choiceA, choiceB) { + if (!this.setup.class.uuid) return { inventory: { take: [] } }; + + const { inventory, characterGuide } = this.setup.class.system; + return { + armor: characterGuide.suggestedArmor ?? null, + primaryWeapon: characterGuide.suggestedPrimaryWeapon ?? null, + secondaryWeapon: characterGuide.suggestedSecondaryWeapon + ? { ...characterGuide.suggestedSecondaryWeapon, uuid: characterGuide.suggestedSecondaryWeapon.uuid } + : null, + inventory: { + take: inventory.take ?? [], + choiceA: + inventory.choiceA?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceA?.uuid })) ?? [], + choiceB: inventory.choiceB?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceB?.uuid })) ?? [] + } + }; + } + + _createDragDropHandlers() { + return this.options.dragDrop.map(d => { + d.callbacks = { + dragstart: this._onDragStart.bind(this), + drop: this._onDrop.bind(this) + }; + return new foundry.applications.ux.DragDrop.implementation(d); + }); + } + + static async viewCompendium(_, target) { + (await game.packs.get(`daggerheart.${target.dataset.compendium}`))?.render(true); + } + + static async viewItem(_, target) { + (await foundry.utils.fromUuid(target.dataset.uuid)).sheet.render(true); + } + + static useSuggestedTraits() { + this.setup.traits = Object.keys(this.setup.traits).reduce((acc, traitKey) => { + acc[traitKey] = { + ...this.setup.traits[traitKey], + value: this.setup.class.system.characterGuide.suggestedTraits[traitKey] + }; + return acc; + }, {}); + + this.setup.visibility = this.getUpdateVisibility(); + this.render(); + } + + static async equipmentChoice(_, target) { + this.equipment.inventory[target.dataset.path] = await foundry.utils.fromUuid(target.dataset.uuid); + this.render(); + } + + static async finish() { + const embeddedAncestries = await this.character.createEmbeddedDocuments('Item', [this.setup.ancestry]); + const embeddedCommunities = await this.character.createEmbeddedDocuments('Item', [this.setup.community]); + await this.character.createEmbeddedDocuments('Item', [this.setup.class]); + await this.character.createEmbeddedDocuments('Item', [this.setup.subclass]); + await this.character.createEmbeddedDocuments('Item', Object.values(this.setup.domainCards)); + + if (this.equipment.armor.uuid) + await this.character.createEmbeddedDocuments('Item', [ + { ...this.equipment.armor, system: { ...this.equipment.armor.system, equipped: true } } + ]); + if (this.equipment.primaryWeapon.uuid) + await this.character.createEmbeddedDocuments('Item', [ + { ...this.equipment.primaryWeapon, system: { ...this.equipment.primaryWeapon.system, equipped: true } } + ]); + if (this.equipment.secondaryWeapon.uuid) + await this.character.createEmbeddedDocuments('Item', [ + { + ...this.equipment.secondaryWeapon, + system: { ...this.equipment.secondaryWeapon.system, equipped: true } + } + ]); + if (this.equipment.inventory.choiceA.uuid) + await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceA]); + if (this.equipment.inventory.choiceB.uuid) + await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceB]); + await this.character.createEmbeddedDocuments('Item', this.setup.class.system.inventory.take); + + await this.character.update({ + system: { + traits: this.setup.traits, + experiences: this.setup.experiences, + ancestry: embeddedAncestries[0].uuid, + community: embeddedCommunities[0].uuid + } + }); + + this.close(); + } + + async _onDragStart(event) { + const target = event.currentTarget; + + event.dataTransfer.setData('text/plain', JSON.stringify(target.dataset)); + event.dataTransfer.setDragImage(target, 60, 0); + } + + async _onDrop(event) { + const data = TextEditor.getDragEventData(event); + const item = await foundry.utils.fromUuid(data.uuid); + if (item.type === 'ancestry' && event.target.closest('.ancestry-card')) { + this.setup.ancestry = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; + } else if (item.type === 'community' && event.target.closest('.community-card')) { + this.setup.community = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; + } else if (item.type === 'class' && event.target.closest('.class-card')) { + this.setup.class = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), uuid: item.uuid }; + this.setup.subclass = {}; + this.setup.domainCards = { + [foundry.utils.randomID()]: {}, + [foundry.utils.randomID()]: {} + }; + } else if (item.type === 'subclass' && event.target.closest('.subclass-card')) { + if (this.setup.class.system.subclasses.every(subclass => subclass.uuid !== item.uuid)) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.SubclassNotInClass') + ); + return; + } + + this.setup.subclass = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; + } else if (item.type === 'domainCard' && event.target.closest('.domain-card')) { + if (!this.setup.class.uuid) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.MissingClass')); + return; + } + + if (!this.setup.class.system.domains.includes(item.system.domain)) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.WrongDomain')); + return; + } + + if (item.system.level > 1) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.CardTooHighLevel') + ); + return; + } + + if (Object.values(this.setup.domainCards).some(card => card.uuid === item.uuid)) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.DuplicateCard')); + return; + } + + this.setup.domainCards[event.target.closest('.domain-card').dataset.card] = { ...item, uuid: item.uuid }; + } else if (item.type === 'armor' && event.target.closest('.armor-card')) { + if (item.system.tier > 1) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier') + ); + return; + } + + this.equipment.armor = { ...item, uuid: item.uuid }; + } else if (item.type === 'weapon' && event.target.closest('.primary-weapon-card')) { + if (item.system.secondary) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.NotPrimary')); + return; + } + + if (item.system.tier > 1) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier') + ); + return; + } + + this.equipment.primaryWeapon = { ...item, uuid: item.uuid }; + } else if (item.type === 'weapon' && event.target.closest('.secondary-weapon-card')) { + if (this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.PrimaryIsTwoHanded') + ); + return; + } + + if (!item.system.secondary) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.NotSecondary')); + return; + } + + if (item.system.tier > 1) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.CharacterCreation.Notifications.ItemTooHighTier') + ); + return; + } + + this.equipment.secondaryWeapon = { ...item, uuid: item.uuid }; + } else { + return; + } + + this.setup.visibility = this.getUpdateVisibility(); + this.render(); + } +} diff --git a/module/applications/chatMessage.mjs b/module/applications/chatMessage.mjs index 579fd828..084fc768 100644 --- a/module/applications/chatMessage.mjs +++ b/module/applications/chatMessage.mjs @@ -9,10 +9,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ const html = await super.renderHTML(); - console.log(this.system) - if ( - this.type === 'dualityRoll' - ) { + + if (this.type === 'dualityRoll') { html.classList.add('duality'); /* const dualityResult = this.system.dualityResult; */ switch (this.system.roll.result.duality) { diff --git a/module/applications/countdowns.mjs b/module/applications/countdowns.mjs new file mode 100644 index 00000000..8f7c1cbf --- /dev/null +++ b/module/applications/countdowns.mjs @@ -0,0 +1,339 @@ +import { countdownTypes } from '../config/generalConfig.mjs'; +import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs'; +import OwnershipSelection from './ownershipSelection.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(basePath) { + super({}); + + this.basePath = basePath; + } + + get title() { + return game.i18n.format('DAGGERHEART.Countdown.Title', { + type: game.i18n.localize(`DAGGERHEART.Countdown.Types.${this.basePath}`) + }); + } + + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'dh-style', 'countdown'], + tag: 'form', + position: { width: 740, height: 700 }, + window: { + frame: true, + title: 'Countdowns', + resizable: true, + minimizable: true + }, + actions: { + addCountdown: this.addCountdown, + removeCountdown: this.removeCountdown, + editImage: this.onEditImage, + openOwnership: this.openOwnership, + openCountdownOwnership: this.openCountdownOwnership + }, + form: { handler: this.updateData, submitOnChange: true } + }; + + static PARTS = { + countdowns: { + template: 'systems/daggerheart/templates/views/countdowns.hbs', + scrollable: ['.expanded-view'] + } + }; + + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + htmlElement.querySelectorAll('.mini-countdown-container').forEach(element => { + element.addEventListener('click', event => this.updateCountdownValue.bind(this)(event, true)); + element.addEventListener('contextmenu', event => this.updateCountdownValue.bind(this)(event, false)); + }); + } + + async _onFirstRender(context, options) { + super._onFirstRender(context, options); + + this.element.querySelector('.expanded-view').classList.toggle('hidden'); + this.element.querySelector('.minimized-view').classList.toggle('hidden'); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + const countdownData = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; + + context.isGM = game.user.isGM; + context.base = this.basePath; + + context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; + context.source = { + ...countdownData, + countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { + const countdown = countdownData.countdowns[key]; + + const ownershipValue = countdown.playerOwnership[game.user.id].value; + if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { + acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }; + } + + return acc; + }, {}) + }; + context.systemFields = countdownData.schema.fields; + context.countdownFields = context.systemFields.countdowns.element.fields; + context.minimized = this.minimized || _options.isFirstRender; + + return context; + } + + static async updateData(event, _, formData) { + const data = foundry.utils.expandObject(formData.object); + const newSetting = foundry.utils.mergeObject( + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns).toObject(), + data + ); + + if (game.user.isGM) { + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, newSetting); + this.render(); + } else { + await game.socket.emit(`system.${SYSTEM.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateSetting, + uuid: SYSTEM.SETTINGS.gameSettings.Countdowns, + update: newSetting + } + }); + } + } + + async minimize() { + await super.minimize(); + + this.element.querySelector('.expanded-view').classList.toggle('hidden'); + this.element.querySelector('.minimized-view').classList.toggle('hidden'); + } + + async maximize() { + if (this.minimized) { + const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; + if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) { + ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership')); + return; + } + + this.element.querySelector('.expanded-view').classList.toggle('hidden'); + this.element.querySelector('.minimized-view').classList.toggle('hidden'); + } + + await super.maximize(); + } + + async updateSetting(update) { + if (game.user.isGM) { + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update); + await game.socket.emit(`system.${SYSTEM.id}`, { + action: socketEvent.Refresh, + data: { + refreshType: RefreshType.Countdown, + application: `${this.basePath}-countdowns` + } + }); + + this.render(); + } else { + await game.socket.emit(`system.${SYSTEM.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateSetting, + uuid: SYSTEM.SETTINGS.gameSettings.Countdowns, + update: update, + refresh: { refreshType: RefreshType.Countdown, application: `${this.basePath}-countdowns` } + } + }); + } + } + + static onEditImage(_, target) { + const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; + const current = setting.countdowns[target.dataset.countdown].img; + const fp = new FilePicker({ + current, + type: 'image', + callback: async path => this.updateImage.bind(this)(path, target.dataset.countdown), + top: this.position.top + 40, + left: this.position.left + 10 + }); + return fp.browse(); + } + + async updateImage(path, countdown) { + const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + await setting.updateSource({ + [`${this.basePath}.countdowns.${countdown}.img`]: path + }); + + await this.updateSetting(setting); + } + + static openOwnership(_, target) { + new Promise((resolve, reject) => { + const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; + const ownership = { default: setting.ownership.default, players: setting.playerOwnership }; + new OwnershipSelection(resolve, reject, this.title, ownership).render(true); + }).then(async ownership => { + const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + await setting.updateSource({ + [`${this.basePath}.ownership`]: ownership + }); + + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting.toObject()); + this.render(); + }); + } + + static openCountdownOwnership(_, target) { + const countdownId = target.dataset.countdown; + new Promise((resolve, reject) => { + const countdown = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath] + .countdowns[countdownId]; + const ownership = { default: countdown.ownership.default, players: countdown.playerOwnership }; + new OwnershipSelection(resolve, reject, countdown.name, ownership).render(true); + }).then(async ownership => { + const setting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + await setting.updateSource({ + [`${this.basePath}.countdowns.${countdownId}.ownership`]: ownership + }); + + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, setting); + this.render(); + }); + } + + async updateCountdownValue(event, increase) { + const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown]; + + if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) { + return; + } + + const currentValue = countdown.progress.current; + + if (increase && currentValue === countdown.progress.max) return; + if (!increase && currentValue === 0) return; + + await countdownSetting.updateSource({ + [`${this.basePath}.countdowns.${event.currentTarget.dataset.countdown}.progress.current`]: increase + ? currentValue + 1 + : currentValue - 1 + }); + + await this.updateSetting(countdownSetting.toObject()); + } + + static async addCountdown() { + const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + await countdownSetting.updateSource({ + [`${this.basePath}.countdowns.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.Countdown.NewCountdown'), + ownership: game.user.isGM + ? {} + : { + players: { + [game.user.id]: { type: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER } + } + } + } + }); + + await this.updateSetting(countdownSetting.toObject()); + } + + static async removeCountdown(_, target) { + const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + const countdownName = countdownSetting[this.basePath].countdowns[target.dataset.countdown].name; + + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.Countdown.RemoveCountdownTitle') + }, + content: game.i18n.format('DAGGERHEART.Countdown.RemoveCountdownText', { name: countdownName }) + }); + if (!confirmed) return; + + await countdownSetting.updateSource({ [`${this.basePath}.countdowns.-=${target.dataset.countdown}`]: null }); + + await this.updateSetting(countdownSetting.toObject()); + } + + async open() { + await this.render(true); + if ( + Object.keys(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath].countdowns) + .length > 0 + ) { + this.minimize(); + } + } +} + +export class NarrativeCountdowns extends Countdowns { + constructor() { + super('narrative'); + } + + static DEFAULT_OPTIONS = { + id: 'narrative-countdowns' + }; +} + +export class EncounterCountdowns extends Countdowns { + constructor() { + super('encounter'); + } + + static DEFAULT_OPTIONS = { + id: 'encounter-countdowns' + }; +} + +export const registerCountdownApplicationHooks = () => { + const updateCountdowns = async shouldProgress => { + if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).countdowns) { + const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); + for (let countdownCategoryKey in countdownSetting) { + const countdownCategory = countdownSetting[countdownCategoryKey]; + for (let countdownKey in countdownCategory.countdowns) { + const countdown = countdownCategory.countdowns[countdownKey]; + + if (shouldProgress(countdown)) { + await countdownSetting.updateSource({ + [`${countdownCategoryKey}.countdowns.${countdownKey}.progress.current`]: + countdown.progress.current - 1 + }); + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, countdownSetting); + foundry.applications.instances.get(`${countdownCategoryKey}-countdowns`)?.render(); + } + } + } + } + }; + + Hooks.on(SYSTEM.HOOKS.characterAttack, async () => { + updateCountdowns(countdown => { + return ( + countdown.progress.type.value === countdownTypes.characterAttack.id && countdown.progress.current > 0 + ); + }); + }); + + Hooks.on(SYSTEM.HOOKS.spotlight, async () => { + updateCountdowns(countdown => { + return countdown.progress.type.value === countdownTypes.spotlight.id && countdown.progress.current > 0; + }); + }); +}; diff --git a/module/applications/downtime.mjs b/module/applications/downtime.mjs index 49d8b1ab..d0fc34b6 100644 --- a/module/applications/downtime.mjs +++ b/module/applications/downtime.mjs @@ -1,3 +1,5 @@ +import { actionsTypes } from '../data/_module.mjs'; + const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) { @@ -5,25 +7,25 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV super({}); this.actor = actor; - this.selectedActivity = null; this.shortrest = shortrest; - this.customActivity = SYSTEM.GENERAL.downtime.custom; + const options = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).restMoves; + this.moveData = shortrest ? options.shortRest : options.longRest; } get title() { - return `${this.actor.name} - ${this.shortrest ? 'Short Rest' : 'Long Rest'}`; + return ''; } static DEFAULT_OPTIONS = { tag: 'form', classes: ['daggerheart', 'views', 'downtime'], - position: { width: 800, height: 'auto' }, + position: { width: 680, height: 'auto' }, actions: { - selectActivity: this.selectActivity, + selectMove: this.selectMove, takeDowntime: this.takeDowntime }, - form: { handler: this.updateData, submitOnChange: true } + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } }; static PARTS = { @@ -33,51 +35,63 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV } }; + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + htmlElement + .querySelectorAll('.activity-image') + .forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this))); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.selectedActivity = this.selectedActivity; - context.options = this.shortrest ? SYSTEM.GENERAL.downtime.shortRest : SYSTEM.GENERAL.downtime.longRest; - context.customActivity = this.customActivity; - - context.disabledDowntime = - !this.selectedActivity || - (this.selectedActivity.id === this.customActivity.id && - (!this.customActivity.name || !this.customActivity.description)); + context.moveData = this.moveData; + context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); + context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices; return context; } - static selectActivity(_, button) { - const activity = button.dataset.activity; - this.selectedActivity = - activity === this.customActivity.id - ? this.customActivity - : this.shortrest - ? SYSTEM.GENERAL.downtime.shortRest[activity] - : SYSTEM.GENERAL.downtime.longRest[activity]; + static selectMove(_, button) { + const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); + if (nrSelected === this.moveData.nrChoices) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.Downtime.Notifications.NoMoreMoves')); + return; + } + + const move = button.dataset.move; + this.moveData.moves[move].selected = this.moveData.moves[move].selected + ? this.moveData.moves[move].selected + 1 + : 1; + + this.render(); + } + + deselectMove(event) { + const move = event.currentTarget.dataset.move; + this.moveData.moves[move].selected = this.moveData.moves[move].selected + ? this.moveData.moves[move].selected - 1 + : 0; this.render(); } static async takeDowntime() { - const refreshedFeatures = this.shortrest - ? this.actor.system.refreshableFeatures.shortRest - : [...this.actor.system.refreshableFeatures.shortRest, ...this.actor.system.refreshableFeatures.longRest]; - for (var feature of refreshedFeatures) { - await feature.system.refresh(); - } + const moves = Object.values(this.moveData.moves).filter(x => x.selected); const cls = getDocumentClass('ChatMessage'); const msg = new cls({ user: game.user.id, + system: { + moves: moves, + actor: this.actor.uuid + }, content: await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/chat/downtime.hbs', { - player: this.actor.name, - title: game.i18n.localize(this.selectedActivity.name), - img: this.selectedActivity.img, - description: game.i18n.localize(this.selectedActivity.description), - refreshedFeatures: refreshedFeatures + title: `${this.actor.name} - ${game.i18n.localize(`DAGGERHEART.Downtime.${this.shortRest ? 'ShortRest' : 'LongRest'}.title`)}`, + moves: moves } ) }); diff --git a/module/applications/ownershipSelection.mjs b/module/applications/ownershipSelection.mjs new file mode 100644 index 00000000..380acfe1 --- /dev/null +++ b/module/applications/ownershipSelection.mjs @@ -0,0 +1,72 @@ +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class OwnershipSelection extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(resolve, reject, name, ownership) { + super({}); + + this.resolve = resolve; + this.reject = reject; + this.name = name; + this.ownership = ownership; + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'views', 'ownership-selection'], + position: { + width: 600, + height: 'auto' + }, + form: { handler: this.updateData } + }; + + static PARTS = { + selection: { + template: 'systems/daggerheart/templates/views/ownershipSelection.hbs' + } + }; + + get title() { + return game.i18n.format('DAGGERHEART.OwnershipSelection.Title', { name: this.name }); + } + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.ownershipOptions = Object.keys(CONST.DOCUMENT_OWNERSHIP_LEVELS).map(level => ({ + value: CONST.DOCUMENT_OWNERSHIP_LEVELS[level], + label: game.i18n.localize(`OWNERSHIP.${level}`) + })); + context.ownership = { + default: this.ownership.default, + players: Object.keys(this.ownership.players).reduce((acc, x) => { + const user = game.users.get(x); + if (!user.isGM) { + acc[x] = { + img: user.character?.img ?? 'icons/svg/cowled.svg', + name: user.name, + ownership: this.ownership.players[x].value + }; + } + + return acc; + }, {}) + }; + + return context; + } + + static async updateData(event, _, formData) { + const { ownership } = foundry.utils.expandObject(formData.object); + + this.resolve(ownership); + this.close(true); + } + + async close(fromSave) { + if (!fromSave) { + this.reject(); + } + + await super.close(); + } +} diff --git a/module/applications/resources.mjs b/module/applications/resources.mjs index bbd47fc5..3b8c6ce6 100644 --- a/module/applications/resources.mjs +++ b/module/applications/resources.mjs @@ -50,7 +50,7 @@ export default class Resources extends HandlebarsApplicationMixin(ApplicationV2) } get maxFear() { - return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear); + return game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).maxFear; } /* -------------------------------------------- */ @@ -59,7 +59,7 @@ export default class Resources extends HandlebarsApplicationMixin(ApplicationV2) /** @override */ async _prepareContext(_options) { - const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear), + const display = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear, current = this.currentFear, max = this.maxFear, percent = (current / max) * 100, diff --git a/module/applications/settings.mjs b/module/applications/settings.mjs index 8f5b3446..8cfc5161 100644 --- a/module/applications/settings.mjs +++ b/module/applications/settings.mjs @@ -1,178 +1,122 @@ -import { DualityRollColor } from '../config/settingsConfig.mjs'; import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs'; -import DhAppearance from '../data/settings/Appearance.mjs'; -import DHAppearanceSettings from './settings/appearanceSettings.mjs'; -import DhVariantRules from '../data/settings/VariantRules.mjs'; -import DHVariantRuleSettings from './settings/variantRuleSettings.mjs'; - -class DhpAutomationSettings extends FormApplication { - constructor(object = {}, options = {}) { - super(object, options); - } - - static get defaultOptions() { - const defaults = super.defaultOptions; - const overrides = { - height: 'auto', - width: 400, - id: 'daggerheart-automation-settings', - template: 'systems/daggerheart/templates/views/automation-settings.hbs', - closeOnSubmit: true, - submitOnChange: false, - classes: ['daggerheart', 'views', 'settings'] - }; - - const mergedOptions = foundry.utils.mergeObject(defaults, overrides); - - return mergedOptions; - } - - async getData() { - const context = super.getData(); - context.settings = SYSTEM.SETTINGS.gameSettings.Automation; - context.hope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope); - context.actionPoints = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.ActionPoints); - - return context; - } - - activateListeners(html) { - super.activateListeners(html); - } - - async _updateObject(_, formData) { - const data = foundry.utils.expandObject(formData); - const updateSettingsKeys = Object.keys(data); - for (var i = 0; i < updateSettingsKeys.length; i++) { - await game.settings.set(SYSTEM.id, updateSettingsKeys[i], data[updateSettingsKeys[i]]); - } - } -} - -class DhpHomebrewSettings extends FormApplication { - constructor(object = {}, options = {}) { - super(object, options); - } - - static get defaultOptions() { - const defaults = super.defaultOptions; - const overrides = { - height: 'auto', - width: 400, - id: 'daggerheart-homebrew-settings', - template: 'systems/daggerheart/templates/views/homebrew-settings.hbs', - closeOnSubmit: true, - submitOnChange: false, - classes: ['daggerheart', 'views', 'settings'] - }; - - const mergedOptions = foundry.utils.mergeObject(defaults, overrides); - - return mergedOptions; - } - - async getData() { - const context = super.getData(); - context.settings = SYSTEM.SETTINGS.gameSettings.General; - context.abilityArray = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray); - - return context; - } - - activateListeners(html) { - super.activateListeners(html); - } - - async _updateObject(_, formData) { - const data = foundry.utils.expandObject(formData); - const updateSettingsKeys = Object.keys(data); - for (var i = 0; i < updateSettingsKeys.length; i++) { - await game.settings.set(SYSTEM.id, updateSettingsKeys[i], data[updateSettingsKeys[i]]); - } - } -} - -class DhpRangeSettings extends FormApplication { - constructor(object = {}, options = {}) { - super(object, options); - - this.range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement); - } - - static get defaultOptions() { - const defaults = super.defaultOptions; - const overrides = { - height: 'auto', - width: 400, - id: 'daggerheart-range-settings', - template: 'systems/daggerheart/templates/views/range-settings.hbs', - closeOnSubmit: false, - submitOnChange: true, - classes: ['daggerheart', 'views', 'settings'] - }; - - const mergedOptions = foundry.utils.mergeObject(defaults, overrides); - - return mergedOptions; - } - - async getData() { - const context = super.getData(); - context.settings = SYSTEM.SETTINGS.gameSettings.General; - context.range = this.range; - context.disabled = - context.range.enabled && - [ - context.range.melee, - context.range.veryClose, - context.range.close, - context.range.far, - context.range.veryFar - ].some(x => x === null || x === false); - - return context; - } - - activateListeners(html) { - super.activateListeners(html); - - html.find('.range-reset').click(this.reset.bind(this)); - html.find('.save').click(this.save.bind(this)); - html.find('.close').click(this.close.bind(this)); - } - - async _updateObject(_, formData) { - const data = foundry.utils.expandObject(formData, { disabled: true }); - this.range = foundry.utils.mergeObject(this.range, data); - this.render(true); - } - - reset() { - this.range = { - enabled: false, - melee: 5, - veryClose: 15, - close: 30, - far: 60, - veryFar: 120 - }; - this.render(true); - } - - async save() { - await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement, this.range); - this.close(); - } -} +import DhCountdowns from '../data/countdowns.mjs'; +import { + DhAppearance, + DhAutomation, + DhHomebrew, + DhRangeMeasurement, + DhVariantRules +} from '../data/settings/_module.mjs'; +import { + DhAppearanceSettings, + DhAutomationSettings, + DhHomebrewSettings, + DhRangeMeasurementSettings, + DhVariantRuleSettings +} from './settings/_module.mjs'; export const registerDHSettings = () => { - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray, { - name: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Hint'), + registerMenuSettings(); + registerMenus(); + registerNonConfigSettings(); +}; + +const registerMenuSettings = () => { + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, { scope: 'world', config: false, - type: String, - default: '[2,1,1,0,0,-1]' + type: DhVariantRules + }); + + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation, { + scope: 'world', + config: false, + type: DhAutomation + }); + + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, { + scope: 'world', + config: false, + type: DhHomebrew, + onChange: value => { + if (value.maxFear) { + if (ui.resources) ui.resources.render({ force: true }); + } + } + }); + + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, { + scope: 'client', + config: false, + type: DhAppearance, + onChange: value => { + if (value.displayFear) { + if (ui.resources) { + if (value.displayFear === 'hide') ui.resources.close({ allowed: true }); + else ui.resources.render({ force: true }); + } + } + } + }); + + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement, { + scope: 'client', + config: false, + type: DhRangeMeasurement + }); +}; + +const registerMenus = () => { + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, { + name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'), + label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'), + hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Hint'), + icon: SYSTEM.SETTINGS.menu.Automation.Icon, + type: DhAutomationSettings, + restricted: true + }); + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Homebrew.Name, { + name: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name'), + label: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Label'), + hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Hint'), + icon: SYSTEM.SETTINGS.menu.Homebrew.Icon, + type: DhHomebrewSettings, + restricted: true + }); + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Range.Name, { + name: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Name'), + label: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Label'), + hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Hint'), + icon: SYSTEM.SETTINGS.menu.Range.Icon, + type: DhRangeMeasurementSettings, + restricted: true + }); + + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, { + name: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.title'), + label: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.label'), + hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.hint'), + icon: 'fa-solid fa-palette', + type: DhAppearanceSettings, + restricted: false + }); + + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.VariantRules.Name, { + name: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.title'), + label: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.label'), + hint: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.hint'), + icon: SYSTEM.SETTINGS.menu.VariantRules.Icon, + type: DhVariantRuleSettings, + restricted: false + }); +}; + +const registerNonConfigSettings = () => { + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, { + scope: 'world', + config: false, + type: DhLevelTiers, + default: defaultLevelTiers }); game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear, { @@ -188,143 +132,9 @@ export const registerDHSettings = () => { } }); - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.MaxFear, { - name: game.i18n.localize('DAGGERHEART.Settings.Resources.MaxFear.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.Resources.MaxFear.Hint'), - scope: 'world', - config: true, - type: Number, - default: 12, - onChange: () => { - if (ui.resources) ui.resources.render({ force: true }); - } - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear, { - name: game.i18n.localize('DAGGERHEART.Settings.Resources.DisplayFear.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.Resources.DisplayFear.Hint'), - scope: 'client', - config: true, - type: String, - choices: { - token: 'Tokens', - bar: 'Bar', - hide: 'Hide' - }, - default: 'token', - onChange: value => { - if (ui.resources) { - if (value === 'hide') ui.resources.close({ allowed: true }); - else ui.resources.render({ force: true }); - } - } - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope, { - name: game.i18n.localize('DAGGERHEART.Settings.Automation.Hope.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.Automation.Hope.Hint'), + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, { scope: 'world', config: false, - type: Boolean, - default: false - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.ActionPoints, { - name: game.i18n.localize('DAGGERHEART.Settings.Automation.ActionPoints.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.Automation.ActionPoints.Hint'), - scope: 'world', - config: false, - type: Boolean, - default: true - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement, { - name: game.i18n.localize('DAGGERHEART.Settings.General.RangeMeasurement.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.General.RangeMeasurement.Hint'), - scope: 'world', - config: false, - type: Object, - default: { - enabled: true, - melee: 5, - veryClose: 15, - close: 30, - far: 60, - veryFar: 120 - } - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.variantRules, { - scope: 'world', - config: false, - type: DhVariantRules, - default: DhVariantRules.defaultSchema - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, { - scope: 'client', - config: false, - type: DhAppearance, - default: DhAppearance.defaultSchema - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, { - name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'), - hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'), - scope: 'world', - config: false, - type: Number, - choices: Object.values(DualityRollColor), - default: DualityRollColor.colorful.value - }); - - game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, { - scope: 'world', - config: false, - type: DhLevelTiers, - default: defaultLevelTiers - }); - - game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, { - name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'), - label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'), - hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Hint'), - icon: SYSTEM.SETTINGS.menu.Automation.Icon, - type: DhpAutomationSettings, - restricted: true - }); - game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Homebrew.Name, { - name: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name'), - label: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Label'), - hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Hint'), - icon: SYSTEM.SETTINGS.menu.Homebrew.Icon, - type: DhpHomebrewSettings, - restricted: true - }); - game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Range.Name, { - name: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Name'), - label: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Label'), - hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Range.Hint'), - icon: SYSTEM.SETTINGS.menu.Range.Icon, - type: DhpRangeSettings, - restricted: true - }); - - game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, { - name: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.title'), - label: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.label'), - hint: game.i18n.localize('DAGGERHEART.Settings.Menu.Appearance.hint'), - icon: 'fa-solid fa-palette', - type: DHAppearanceSettings, - restricted: false - }); - - game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.VariantRules.Name, { - name: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.title'), - label: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.label'), - hint: game.i18n.localize('DAGGERHEART.Settings.Menu.VariantRules.hint'), - icon: SYSTEM.SETTINGS.menu.VariantRules.Icon, - type: DHVariantRuleSettings, - restricted: false + type: DhCountdowns }); }; diff --git a/module/applications/settings/_module.mjs b/module/applications/settings/_module.mjs new file mode 100644 index 00000000..b5b5748d --- /dev/null +++ b/module/applications/settings/_module.mjs @@ -0,0 +1,13 @@ +import DhAppearanceSettings from './appearanceSettings.mjs'; +import DhAutomationSettings from './automationSettings.mjs'; +import DhHomebrewSettings from './homebrewSettings.mjs'; +import DhRangeMeasurementSettings from './rangeMeasurementSettings.mjs'; +import DhVariantRuleSettings from './variantRuleSettings.mjs'; + +export { + DhAppearanceSettings, + DhAutomationSettings, + DhHomebrewSettings, + DhRangeMeasurementSettings, + DhVariantRuleSettings +}; diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 5797836b..c11efd0f 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -54,20 +54,11 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App static async save() { await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, this.settings.toObject()); - /* const reload = await foundry.applications.api.DialogV2.confirm({ - id: 'reload-world-confirm', - modal: true, - rejectClose: false, - window: { title: 'SETTINGS.ReloadPromptTitle' }, - position: { width: 400 }, - content: `

${game.i18n.localize('SETTINGS.ReloadPromptBody')}

` - }); - - if (reload) { - await game.socket.emit('reload'); - foundry.utils.debouncedReload(); - } */ - document.body.classList.toggle('theme-colorful', game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme === DualityRollColor.colorful.value); + document.body.classList.toggle( + 'theme-colorful', + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme === + DualityRollColor.colorful.value + ); this.close(); } diff --git a/module/applications/settings/automationSettings.mjs b/module/applications/settings/automationSettings.mjs new file mode 100644 index 00000000..6de21efd --- /dev/null +++ b/module/applications/settings/automationSettings.mjs @@ -0,0 +1,59 @@ +import { DhAutomation } from '../../data/settings/_module.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhAutomationSettings extends HandlebarsApplicationMixin(ApplicationV2) { + constructor() { + super({}); + + this.settings = new DhAutomation( + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).toObject() + ); + } + + get title() { + return game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + id: 'daggerheart-automation-settings', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: '600', height: 'auto' }, + actions: { + reset: this.reset, + save: this.save + }, + form: { handler: this.updateData, submitOnChange: true } + }; + + static PARTS = { + main: { + template: 'systems/daggerheart/templates/settings/automation-settings.hbs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.settingFields = this.settings; + + return context; + } + + static async updateData(event, element, formData) { + const updatedSettings = foundry.utils.expandObject(formData.object); + + await this.settings.updateSource(updatedSettings); + this.render(); + } + + static async reset() { + this.settings = new DhAutomation(); + this.render(); + } + + static async save() { + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation, this.settings.toObject()); + this.close(); + } +} diff --git a/module/applications/settings/components/settingsActionsView.mjs b/module/applications/settings/components/settingsActionsView.mjs new file mode 100644 index 00000000..9b223ec5 --- /dev/null +++ b/module/applications/settings/components/settingsActionsView.mjs @@ -0,0 +1,144 @@ +import { actionsTypes } from '../../../data/_module.mjs'; +import DHActionConfig from '../../config/Action.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(resolve, reject, title, name, img, description, actions) { + super({}); + + this.resolve = resolve; + this.reject = reject; + this.viewTitle = title; + this.name = name; + this.img = img; + this.description = description; + this.actions = actions; + } + + get title() { + return this.viewTitle; + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: '400', height: 'auto' }, + actions: { + editImage: this.onEditImage, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, + saveForm: this.saveForm + }, + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' }, + main: { + template: 'systems/daggerheart/templates/settings/components/action-view.hbs' + }, + footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.name = this.name; + context.img = this.img; + context.description = this.description; + context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description); + context.actions = this.actions; + + return context; + } + + static async updateData(event, element, formData) { + const { name, img, description } = foundry.utils.expandObject(formData.object); + this.name = name; + this.description = description; + + this.render(); + } + + static async saveForm(event) { + this.resolve({ + name: this.name, + img: this.img, + description: this.description, + actions: this.actions + }); + this.close(true); + } + + static close(fromSave) { + if (!fromSave) { + this.reject(); + } + + super.close(); + } + + static onEditImage() { + const fp = new FilePicker({ + current: this.img, + type: 'image', + callback: async path => { + this.img = path; + this.render(); + }, + top: this.position.top + 40, + left: this.position.left + 10 + }); + return fp.browse(); + } + + async selectActionType() { + const content = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/views/actionType.hbs', + { types: SYSTEM.ACTIONS.actionTypes } + ), + title = 'Select Action Type', + type = 'form', + data = {}; + return Dialog.prompt({ + title, + label: title, + content, + type, + callback: html => { + const form = html[0].querySelector('form'), + fd = new foundry.applications.ux.FormDataExtended(form); + foundry.utils.mergeObject(data, fd.object, { inplace: true }); + return data; + }, + rejectClose: false + }); + } + + static async addItem() { + const actionType = await this.selectActionType(); + const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, + action = new cls({ + _id: foundry.utils.randomID(), + type: actionType.type, + name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), + ...cls.getSourceConfig(this.document) + }); + + this.actions.push(action); + this.render(); + } + + static async editItem(_, button) { + await new DHActionConfig(this.actions[button.dataset.id]).render(true); + } + + static removeItem(event, button) { + this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id)); + this.render(); + } + + static resetMoves() {} +} diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs new file mode 100644 index 00000000..d59bc35c --- /dev/null +++ b/module/applications/settings/homebrewSettings.mjs @@ -0,0 +1,157 @@ +import { DhHomebrew } from '../../data/settings/_module.mjs'; +import DhSettingsActionView from './components/settingsActionsView.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { + constructor() { + super({}); + + this.settings = new DhHomebrew(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).toObject()); + } + + get title() { + return game.i18n.localize('DAGGERHEART.Settings.Menu.Homebrew.Name'); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + id: 'daggerheart-homebrew-settings', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: '600', height: 'auto' }, + actions: { + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, + save: this.save + }, + form: { handler: this.updateData, submitOnChange: true } + }; + + static PARTS = { + main: { + template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.settingFields = this.settings; + + return context; + } + + static async updateData(event, element, formData) { + const updatedSettings = foundry.utils.expandObject(formData.object); + + await this.settings.updateSource({ + ...updatedSettings, + traitArray: Object.values(updatedSettings.traitArray) + }); + this.render(); + } + + static async addItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.Settings.Homebrew.NewDowntimeMove'), + img: 'icons/magic/life/cross-worn-green.webp', + description: '', + actions: [] + } + }); + this.render(); + } + + static async editItem(_, target) { + const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; + new Promise((resolve, reject) => { + new DhSettingsActionView( + resolve, + reject, + game.i18n.localize('DAGGERHEART.Settings.Homebrew.DowntimeMoves'), + move.name, + move.img, + move.description, + move.actions + ).render(true); + }).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id)); + } + + async updateAction(data, type, id) { + await this.settings.updateSource({ + [`restMoves.${type}.moves.${id}`]: { + name: data.name, + img: data.img, + description: data.description + } + }); + this.render(); + } + + static async removeItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null + }); + this.render(); + } + + static async resetMoves(_, target) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.Settings.Homebrew.ResetMovesTitle', { + type: game.i18n.localize( + `DAGGERHEART.Downtime.${target.dataset.type === 'shortRest' ? 'ShortRest' : 'LongRest'}.title` + ) + }) + }, + content: game.i18n.localize('DAGGERHEART.Settings.Homebrew.ResetMovesText') + }); + + if (!confirmed) return; + + const fields = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).schema.fields; + + const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => { + acc[`-=${key}`] = null; + + return acc; + }, {}); + + const updateBase = + target.dataset.type === 'shortRest' + ? fields.restMoves.fields.shortRest.fields + : fields.restMoves.fields.longRest.fields; + const update = { + nrChoices: updateBase.nrChoices.initial, + moves: Object.keys(updateBase.moves.initial).reduce((acc, key) => { + const move = updateBase.moves.initial[key]; + acc[key] = { + ...move, + name: game.i18n.localize(move.name), + description: game.i18n.localize(move.description) + }; + + return acc; + }, {}) + }; + + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}`]: { + ...update, + moves: { + ...removeUpdate, + ...update.moves + } + } + }); + + this.render(); + } + + static async save() { + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); + this.close(); + } +} diff --git a/module/applications/settings/rangeMeasurementSettings.mjs b/module/applications/settings/rangeMeasurementSettings.mjs new file mode 100644 index 00000000..8e90b462 --- /dev/null +++ b/module/applications/settings/rangeMeasurementSettings.mjs @@ -0,0 +1,59 @@ +import { DhRangeMeasurement } from '../../data/settings/_module.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhRangeMeasurementSettings extends HandlebarsApplicationMixin(ApplicationV2) { + constructor() { + super({}); + + this.settings = new DhRangeMeasurement( + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement).toObject() + ); + } + + get title() { + return game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + id: 'daggerheart-automation-settings', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: '600', height: 'auto' }, + actions: { + reset: this.reset, + save: this.save + }, + form: { handler: this.updateData, submitOnChange: true } + }; + + static PARTS = { + main: { + template: 'systems/daggerheart/templates/settings/range-measurement-settings.hbs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.settingFields = this.settings; + + return context; + } + + static async updateData(event, element, formData) { + const updatedSettings = foundry.utils.expandObject(formData.object); + + await this.settings.updateSource(updatedSettings); + this.render(); + } + + static async reset() { + this.settings = new DhRangeMeasurement(); + this.render(); + } + + static async save() { + await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement, this.settings.toObject()); + this.close(); + } +} diff --git a/module/applications/sheets/activeEffectConfig.mjs b/module/applications/sheets/activeEffectConfig.mjs index 585086a1..6a629583 100644 --- a/module/applications/sheets/activeEffectConfig.mjs +++ b/module/applications/sheets/activeEffectConfig.mjs @@ -1,4 +1,4 @@ -export default class DhActiveEffectConfig extends ActiveEffectConfig { +export default class DhActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig { static DEFAULT_OPTIONS = { classes: ['daggerheart', 'sheet', 'dh-style'] }; diff --git a/module/applications/sheets/character.mjs b/module/applications/sheets/character.mjs index 209190f8..16d88b5b 100644 --- a/module/applications/sheets/character.mjs +++ b/module/applications/sheets/character.mjs @@ -165,18 +165,18 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { context.config = SYSTEM; const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base); - context.abilityScoreArray = JSON.parse( - await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray) - ).reduce((acc, x) => { - const selectedIndex = selectedAttributes.indexOf(x); - if (selectedIndex !== -1) { - selectedAttributes.splice(selectedIndex, 1); - } else { - acc.push({ name: x, value: x }); - } + context.abilityScoreArray = await game.settings + .get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew) + .traitArray.reduce((acc, x) => { + const selectedIndex = selectedAttributes.indexOf(x); + if (selectedIndex !== -1) { + selectedAttributes.splice(selectedIndex, 1); + } else { + acc.push({ name: x, value: x }); + } - return acc; - }, []); + return acc; + }, []); if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 }); context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0); @@ -370,7 +370,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { static async attackRoll(event, button) { const weapon = await fromUuid(button.dataset.weapon); if (!weapon) return; - weapon.use(event); + + const wasUsed = await weapon.use(event); + if (wasUsed) { + Hooks.callAll(SYSTEM.HOOKS.characterAttack, {}); + } } static openLevelUp() { diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 54f29361..a4659598 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -221,46 +221,53 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) { async _onDrop(event) { const data = TextEditor.getDragEventData(event); const item = await fromUuid(data.uuid); + const target = event.target.closest('fieldset.drop-section'); if (item.type === 'subclass') { await this.document.update({ 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] }); } else if (item.type === 'weapon') { - if (event.currentTarget.classList.contains('primary-weapon-section')) { + if (target.classList.contains('primary-weapon-section')) { if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary) await this.document.update({ 'system.characterGuide.suggestedPrimaryWeapon': item.uuid }); - } else if (event.currentTarget.classList.contains('secondary-weapon-section')) { + } else if (target.classList.contains('secondary-weapon-section')) { if (!this.document.system.characterGuide.suggestedSecondaryWeapon && item.system.secondary) await this.document.update({ 'system.characterGuide.suggestedSecondaryWeapon': item.uuid }); } } else if (item.type === 'armor') { - if (event.currentTarget.classList.contains('armor-section')) { + if (target.classList.contains('armor-section')) { if (!this.document.system.characterGuide.suggestedArmor) await this.document.update({ 'system.characterGuide.suggestedArmor': item.uuid }); } - } else if (event.currentTarget.classList.contains('choice-a-section')) { + } else if (target.classList.contains('choice-a-section')) { if (item.type === 'miscellaneous' || item.type === 'consumable') { if (this.document.system.inventory.choiceA.length < 2) await this.document.update({ - 'system.inventory.choiceA': [...this.document.system.inventory.choiceA, item.uuid] + 'system.inventory.choiceA': [ + ...this.document.system.inventory.choiceA.map(x => x.uuid), + item.uuid + ] }); } } else if (item.type === 'miscellaneous') { - if (event.currentTarget.classList.contains('take-section')) { + if (target.classList.contains('take-section')) { if (this.document.system.inventory.take.length < 3) await this.document.update({ - 'system.inventory.take': [...this.document.system.inventory.take, item.uuid] + 'system.inventory.take': [...this.document.system.inventory.take.map(x => x.uuid), item.uuid] }); - } else if (event.currentTarget.classList.contains('choice-b-section')) { + } else if (target.classList.contains('choice-b-section')) { if (this.document.system.inventory.choiceB.length < 2) await this.document.update({ - 'system.inventory.choiceB': [...this.document.system.inventory.choiceB, item.uuid] + 'system.inventory.choiceB': [ + ...this.document.system.inventory.choiceB.map(x => x.uuid), + item.uuid + ] }); } } diff --git a/module/applications/sheets/pseudo-documents/_module.mjs b/module/applications/sheets/pseudo-documents/_module.mjs deleted file mode 100644 index 9dc4d356..00000000 --- a/module/applications/sheets/pseudo-documents/_module.mjs +++ /dev/null @@ -1 +0,0 @@ -export {default as PseudoDocumentSheet }from "./pseudo-documents-sheet.mjs"; \ No newline at end of file diff --git a/module/applications/sheets/pseudo-documents/pseudo-documents-sheet.mjs b/module/applications/sheets/pseudo-documents/pseudo-documents-sheet.mjs deleted file mode 100644 index 487da420..00000000 --- a/module/applications/sheets/pseudo-documents/pseudo-documents-sheet.mjs +++ /dev/null @@ -1,66 +0,0 @@ -const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; - -export default class PseudoDocumentSheet extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(options) { - super(options); - this.#pseudoDocument = options.document; - } - - /** - * The UUID of the associated pseudo-document - * @type {string} - */ - get pseudoUuid() { - return this.pseudoDocument.uuid; - } - - #pseudoDocument; - - /** - * The pseudo-document instance this sheet represents - * @type {object} - */ - get pseudoDocument() { - return this.#pseudoDocument; - } - - static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'sheet'], - position: { width: 600 }, - form: { - handler: PseudoDocumentSheet.#onSubmitForm, - submitOnChange: true, - closeOnSubmit: false - }, - dragDrop: [{ dragSelector: null, dropSelector: null }], - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/sheets/pseudo-documents/header.hbs' }, - }; - - /** @inheritDoc */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - const document = this.pseudoDocument; - return Object.assign(context, { - document, - source: document._source, - editable: this.isEditable, - user: game.user, - rootId: this.id, - }); - } - - /** - * Form submission handler - * @param {SubmitEvent | Event} event - The originating form submission or input change event - * @param {HTMLFormElement} form - The form element that was submitted - * @param {foundry.applications.ux.FormDataExtended} formData - Processed data for the submitted form - */ - static async #onSubmitForm(event, form, formData) { - const submitData = foundry.utils.expandObject(formData.object); - await this.pseudoDocument.update(submitData); - } -} diff --git a/module/config/domainConfig.mjs b/module/config/domainConfig.mjs index a32091c2..3ecb0bd1 100644 --- a/module/config/domainConfig.mjs +++ b/module/config/domainConfig.mjs @@ -1,56 +1,56 @@ export const domains = { arcana: { id: 'arcana', - label: 'DAGGERHEART.Domains.Arcana.label', + label: 'DAGGERHEART.Domains.arcana.label', src: 'icons/magic/symbols/circled-gem-pink.webp', description: 'DAGGERHEART.Domains.Arcana' }, blade: { id: 'blade', - label: 'DAGGERHEART.Domains.Blade.label', + label: 'DAGGERHEART.Domains.blade.label', src: 'icons/weapons/swords/sword-broad-crystal-paired.webp', description: 'DAGGERHEART.Domains.Blade' }, bone: { id: 'bone', - label: 'DAGGERHEART.Domains.Bone.label', + label: 'DAGGERHEART.Domains.bone.label', src: 'icons/skills/wounds/bone-broken-marrow-red.webp', description: 'DAGGERHEART.Domains.Bone' }, codex: { id: 'codex', - label: 'DAGGERHEART.Domains.Codex.label', + label: 'DAGGERHEART.Domains.codex.label', src: 'icons/sundries/books/book-embossed-jewel-gold-purple.webp', description: 'DAGGERHEART.Domains.Codex' }, grace: { id: 'grace', - label: 'DAGGERHEART.Domains.Grace.label', + label: 'DAGGERHEART.Domains.grace.label', src: 'icons/skills/movement/feet-winged-boots-glowing-yellow.webp', description: 'DAGGERHEART.Domains.Grace' }, midnight: { id: 'midnight', - label: 'DAGGERHEART.Domains.Midnight.label', + label: 'DAGGERHEART.Domains.midnight.label', src: 'icons/environment/settlement/watchtower-castle-night.webp', background: 'systems/daggerheart/assets/backgrounds/MidnightBackground.webp', description: 'DAGGERHEART.Domains.Midnight' }, sage: { id: 'sage', - label: 'DAGGERHEART.Domains.Sage.label', + label: 'DAGGERHEART.Domains.sage.label', src: 'icons/sundries/misc/pipe-wooden-straight-brown.webp', description: 'DAGGERHEART.Domains.Sage' }, splendor: { id: 'splendor', - label: 'DAGGERHEART.Domains.Splendor.label', + label: 'DAGGERHEART.Domains.splendor.label', src: 'icons/magic/control/control-influence-crown-gold.webp', description: 'DAGGERHEART.Domains.Splendor' }, valor: { id: 'valor', - label: 'DAGGERHEART.Domains.Valor.label', + label: 'DAGGERHEART.Domains.valor.label', src: 'icons/magic/control/control-influence-rally-purple.webp', description: 'DAGGERHEART.Domains.Valor' } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 6e2103c5..81784d13 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -1,35 +1,42 @@ export const range = { self: { + id: 'self', + short: 's', label: 'DAGGERHEART.Range.self.name', description: 'DAGGERHEART.Range.self.description', distance: 0 }, melee: { id: 'melee', + short: 'm', label: 'DAGGERHEART.Range.melee.name', description: 'DAGGERHEART.Range.melee.description', distance: 1 }, veryClose: { id: 'veryClose', + short: 'vc', label: 'DAGGERHEART.Range.veryClose.name', description: 'DAGGERHEART.Range.veryClose.description', distance: 3 }, close: { id: 'close', + short: 'c', label: 'DAGGERHEART.Range.close.name', description: 'DAGGERHEART.Range.close.description', distance: 10 }, far: { id: 'far', + short: 'f', label: 'DAGGERHEART.Range.far.name', description: 'DAGGERHEART.Range.far.description', distance: 20 }, veryFar: { id: 'veryFar', + short: 'vf', label: 'DAGGERHEART.Range.veryFar.name', description: 'DAGGERHEART.Range.veryFar.description', distance: 30 @@ -104,65 +111,99 @@ export const conditions = { } }; -export const downtime = { - shortRest: { +export const defaultRestOptions = { + shortRest: () => ({ tendToWounds: { id: 'tendToWounds', - name: 'DAGGERHEART.Downtime.TendToWounds.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'), img: 'icons/magic/life/cross-worn-green.webp', - description: 'DAGGERHEART.Downtime.TendToWounds.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Description'), + actions: [ + { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + healing: { + type: 'health', + value: { + custom: { + enabled: true, + formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param? + } + } + } + } + ] }, clearStress: { id: 'clearStress', - name: 'DAGGERHEART.Downtime.ClearStress.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'), img: 'icons/magic/perception/eye-ringed-green.webp', - description: 'DAGGERHEART.Downtime.ClearStress.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Description'), + actions: [ + { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'), + img: 'icons/magic/perception/eye-ringed-green.webp', + actionType: 'action', + healing: { + type: 'stress', + value: { + custom: { + enabled: true, + formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param? + } + } + } + } + ] }, repairArmor: { id: 'repairArmor', - name: 'DAGGERHEART.Downtime.RepairArmor.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: 'DAGGERHEART.Downtime.RepairArmor.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description') }, prepare: { id: 'prepare', - name: 'DAGGERHEART.Downtime.Prepare.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: 'DAGGERHEART.Downtime.Prepare.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description') } - }, - longRest: { + }), + longRest: () => ({ tendToWounds: { id: 'tendToWounds', - name: 'DAGGERHEART.Downtime.TendToWounds.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'), img: 'icons/magic/life/cross-worn-green.webp', - description: 'DAGGERHEART.Downtime.TendToWounds.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description') }, clearStress: { id: 'clearStress', - name: 'DAGGERHEART.Downtime.ClearStress.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'), img: 'icons/magic/perception/eye-ringed-green.webp', - description: 'DAGGERHEART.Downtime.ClearStress.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description') }, repairArmor: { id: 'repairArmor', - name: 'DAGGERHEART.Downtime.RepairArmor.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: 'DAGGERHEART.Downtime.RepairArmor.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description') }, prepare: { id: 'prepare', - name: 'DAGGERHEART.Downtime.Prepare.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: 'DAGGERHEART.Downtime.Prepare.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description') }, workOnAProject: { id: 'workOnAProject', - name: 'DAGGERHEART.Downtime.WorkOnAProject.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'), img: 'icons/skills/social/thumbsup-approval-like.webp', - description: 'DAGGERHEART.Downtime.WorkOnAProject.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description') } - }, + }), custom: { id: 'customActivity', name: '', @@ -353,6 +394,20 @@ export const abilityCosts = { } }; +export const countdownTypes = { + spotlight: { + id: 'spotlight', + label: 'DAGGERHEART.Countdown.Type.Spotlight' + }, + characterAttack: { + id: 'characterAttack', + label: 'DAGGERHEART.Countdown.Type.CharacterAttack' + }, + custom: { + id: 'custom', + label: 'DAGGERHEART.Countdown.Type.Custom' + } +}; export const rollTypes = { weapon: { id: 'weapon', @@ -367,3 +422,9 @@ export const rollTypes = { label: 'DAGGERHEART.RollTypes.ability.name' } }; + +export const fearDisplay = { + token: { value: 'token', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Token' }, + bar: { value: 'bar', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Bar' }, + hide: { value: 'hide', label: 'DAGGERHEART.Settings.Appearance.FearDisplay.Hide' } +}; diff --git a/module/config/hooksConfig.mjs b/module/config/hooksConfig.mjs new file mode 100644 index 00000000..8410c0de --- /dev/null +++ b/module/config/hooksConfig.mjs @@ -0,0 +1,4 @@ +export const hooks = { + characterAttack: 'characterAttackHook', + spotlight: 'spotlightHook' +}; diff --git a/module/config/pseudoConfig.mjs b/module/config/pseudoConfig.mjs deleted file mode 100644 index 297d42cf..00000000 --- a/module/config/pseudoConfig.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { pseudoDocuments } from "../data/_module.mjs"; -import { pseudoDocumentSheet } from "../applications/_module.mjs"; - -//CONFIG.daggerheart.pseudoDocuments -export default { - sheetClass: pseudoDocumentSheet.PseudoDocumentSheet, - feature: { - label: "DAGGERHEART.Feature.Label", - documentClass: pseudoDocuments.feature.BaseFeatureData, - types: { - weapon: { - label: "DAGGERHEART.Feature.Weapon.Label", - documentClass: pseudoDocuments.feature.WeaponFeature, - } - } - } -}; \ No newline at end of file diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 3a33e61b..95a3de0e 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -18,23 +18,16 @@ export const menu = { }; export const gameSettings = { - Automation: { - Hope: 'AutomationHope', - ActionPoints: 'AutomationActionPoints' - }, - Resources: { - Fear: 'ResourcesFear', - MaxFear: 'ResourcesMaxFear', - DisplayFear: 'DisplayFear' - }, - General: { - AbilityArray: 'AbilityArray', - RangeMeasurement: 'RangeMeasurement' - }, - DualityRollColor: 'DualityRollColor', - LevelTiers: 'LevelTiers', + Automation: 'Automation', + Homebrew: 'Homebrew', + RangeMeasurement: 'RangeMeasurement', appearance: 'Appearance', - variantRules: 'VariantRules' + variantRules: 'VariantRules', + Resources: { + Fear: 'ResourcesFear' + }, + LevelTiers: 'LevelTiers', + Countdowns: 'Countdowns' }; export const DualityRollColor = { diff --git a/module/config/system.mjs b/module/config/system.mjs index be2fc1aa..41d67154 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -3,9 +3,9 @@ import * as DOMAIN from './domainConfig.mjs'; import * as ACTOR from './actorConfig.mjs'; import * as ITEM from './itemConfig.mjs'; import * as SETTINGS from './settingsConfig.mjs'; +import { hooks as HOOKS } from './hooksConfig.mjs'; import * as EFFECTS from './effectConfig.mjs'; import * as ACTIONS from './actionConfig.mjs'; -import pseudoDocuments from "./pseudoConfig.mjs"; export const SYSTEM_ID = 'daggerheart'; @@ -16,7 +16,7 @@ export const SYSTEM = { ACTOR, ITEM, SETTINGS, + HOOKS, EFFECTS, - ACTIONS, - pseudoDocuments + ACTIONS }; diff --git a/module/data/_module.mjs b/module/data/_module.mjs index e43ddb99..4284bc41 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -1,5 +1,3 @@ -export { default as DhClass } from './item/class.mjs'; -export { default as DhSubclass } from './item/subclass.mjs'; export { default as DhCombat } from './combat.mjs'; export { default as DhCombatant } from './combatant.mjs'; @@ -8,4 +6,3 @@ export * as items from './item/_module.mjs'; export { actionsTypes } from './action/_module.mjs'; export * as messages from './chat-message/_modules.mjs'; export * as fields from './fields/_module.mjs'; -export * as pseudoDocuments from './pseudo-documents/_module.mjs'; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 1ebe217c..273b7a72 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -5,7 +5,7 @@ import BaseDataActor from './base.mjs'; const attributeField = () => new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField({ initial: 0, integer: true }), + value: new foundry.data.fields.NumberField({ initial: null, integer: true }), bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }), tierMarked: new foundry.data.fields.BooleanField({ initial: false }) }); @@ -54,13 +54,7 @@ export default class DhCharacter extends BaseDataActor { description: new fields.StringField({}), value: new fields.NumberField({ integer: true, initial: 0 }), bonus: new fields.NumberField({ integer: true, initial: 0 }) - }), - { - initial: { - [foundry.utils.randomID()]: { description: '', value: 2 }, - [foundry.utils.randomID()]: { description: '', value: 2 } - } - } + }) ), gold: new fields.SchemaField({ coins: new fields.NumberField({ initial: 0, integer: true }), @@ -93,6 +87,14 @@ export default class DhCharacter extends BaseDataActor { }; } + get tier() { + return this.levelData.level.current === 1 + ? 1 + : Object.values(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers).tiers).find( + tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end + ).tier; + } + get ancestry() { return this.parent.items.find(x => x.type === 'ancestry') ?? null; } @@ -140,19 +142,6 @@ export default class DhCharacter extends BaseDataActor { : null; } - get refreshableFeatures() { - return this.parent.items.reduce( - (acc, x) => { - if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) { - acc[x.system.refreshData.type].push(x); - } - - return acc; - }, - { shortRest: [], longRest: [] } - ); - } - static async unequipBeforeEquip(itemToEquip) { const primary = this.primaryWeapon, secondary = this.secondaryWeapon; @@ -235,7 +224,7 @@ export default class DhCharacter extends BaseDataActor { for (var traitKey in this.traits) { var trait = this.traits[traitKey]; - trait.total = trait.value + trait.bonus; + trait.total = (trait.value ?? 0) + trait.bonus; } for (var experienceKey in this.experiences) { @@ -248,6 +237,14 @@ export default class DhCharacter extends BaseDataActor { this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus; this.proficiency.total = this.proficiency.value + this.proficiency.bonus; } + + getRollData() { + const data = super.getRollData(); + return { + ...data, + tier: this.tier + }; + } } class DhPCLevelData extends foundry.abstract.DataModel { diff --git a/module/data/chat-message/_modules.mjs b/module/data/chat-message/_modules.mjs index dce35576..84f75c1b 100644 --- a/module/data/chat-message/_modules.mjs +++ b/module/data/chat-message/_modules.mjs @@ -18,4 +18,4 @@ export const config = { damageRoll: DHDamageRoll, dualityRoll: DHDualityRoll, applyEffect: DHApplyEffect -}; \ No newline at end of file +}; diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs new file mode 100644 index 00000000..e71cda55 --- /dev/null +++ b/module/data/countdowns.mjs @@ -0,0 +1,139 @@ +import { countdownTypes } from '../config/generalConfig.mjs'; +import { RefreshType, socketEvent } from '../helpers/socket.mjs'; + +export default class DhCountdowns extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + narrative: new fields.EmbeddedDataField(DhCountdownData), + encounter: new fields.EmbeddedDataField(DhCountdownData) + }; + } + + static CountdownCategories = { narrative: 'narrative', combat: 'combat' }; +} + +class DhCountdownData extends foundry.abstract.DataModel { + static LOCALIZATION_PREFIXES = ['DAGGERHEART.Countdown']; // Nots ure why this won't work. Setting labels manually for now + + static defineSchema() { + const fields = foundry.data.fields; + return { + countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown)), + ownership: new fields.SchemaField({ + default: new fields.NumberField({ + required: true, + choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS), + initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE + }), + players: new fields.TypedObjectField( + new fields.SchemaField({ + type: new fields.NumberField({ + required: true, + choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS), + initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT + }) + }) + ) + }) + }; + } + + get playerOwnership() { + return Array.from(game.users).reduce((acc, user) => { + acc[user.id] = { + value: user.isGM + ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER + : this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1 + ? this.ownership.players[user.id].type + : this.ownership.default, + isGM: user.isGM + }; + + return acc; + }, {}); + } +} + +class DhCountdown extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + return { + name: new fields.StringField({ + required: true, + label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.name.label' + }), + img: new fields.FilePathField({ + categories: ['IMAGE'], + base64: false, + initial: 'icons/magic/time/hourglass-yellow-green.webp' + }), + ownership: new fields.SchemaField({ + default: new fields.NumberField({ + required: true, + choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS), + initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE + }), + players: new fields.TypedObjectField( + new fields.SchemaField({ + type: new fields.NumberField({ + required: true, + choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS), + initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT + }) + }) + ) + }), + progress: new fields.SchemaField({ + current: new fields.NumberField({ + required: true, + integer: true, + initial: 1, + label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.current.label' + }), + max: new fields.NumberField({ + required: true, + integer: true, + initial: 1, + label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.max.label' + }), + type: new fields.SchemaField({ + value: new fields.StringField({ + required: true, + choices: countdownTypes, + initial: countdownTypes.spotlight.id, + label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.type.value.label' + }), + label: new fields.StringField({ + label: 'DAGGERHEART.Countdown.FIELDS.countdowns.element.progress.type.label.label' + }) + }) + }) + }; + } + + get playerOwnership() { + return Array.from(game.users).reduce((acc, user) => { + acc[user.id] = { + value: user.isGM + ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER + : this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1 + ? this.ownership.players[user.id].type + : this.ownership.default, + isGM: user.isGM + }; + + return acc; + }, {}); + } +} + +export const registerCountdownHooks = () => { + Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => { + if (refreshType === RefreshType.Countdown) { + foundry.applications.instances.get(application)?.render(); + return false; + } + }); +}; diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 3a573a0b..682ff1c4 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -1,3 +1,3 @@ export { default as FormulaField } from './formulaField.mjs'; export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs'; -export { default as PseudoDocumentsField } from './pseudoDocumentsField.mjs'; +export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs'; diff --git a/module/data/fields/foreignDocumentUUIDArrayField.mjs b/module/data/fields/foreignDocumentUUIDArrayField.mjs new file mode 100644 index 00000000..1cf120d8 --- /dev/null +++ b/module/data/fields/foreignDocumentUUIDArrayField.mjs @@ -0,0 +1,20 @@ +import ForeignDocumentUUIDField from './foreignDocumentUUIDField.mjs'; +/** + * A subclass of {@link foundry.data.fields.ArrayField} that defines an array of foreign document UUID references. + */ +export default class ForeignDocumentUUIDArrayField extends foundry.data.fields.ArrayField { + /** + * @param {foundry.data.types.DocumentUUIDFieldOptions} [fieldOption] - Options to configure each individual ForeignDocumentUUIDField. + * @param {foundry.data.types.ArrayFieldOptions} [options] - Options to configure the array behavior + * @param {foundry.data.types.DataFieldContext} [context] - Optional context for schema processing + */ + constructor(fieldOption = {}, options = {}, context = {}) { + super(new ForeignDocumentUUIDField(fieldOption), options, context); + } + + /** @inheritdoc */ + initialize(value, model, options = {}) { + const v = super.initialize(value, model, options); + return () => v.map(entry => (typeof entry === 'function' ? entry() : entry)); + } +} diff --git a/module/data/fields/foreignDocumentUUIDField.mjs b/module/data/fields/foreignDocumentUUIDField.mjs index 29cfaff3..0efa60da 100644 --- a/module/data/fields/foreignDocumentUUIDField.mjs +++ b/module/data/fields/foreignDocumentUUIDField.mjs @@ -23,7 +23,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume /**@override */ initialize(value, _model, _options = {}) { if (this.idOnly) return value; - return (() => { + return () => { try { const doc = fromUuidSync(value); return doc; @@ -31,7 +31,7 @@ export default class ForeignDocumentUUIDField extends foundry.data.fields.Docume console.error(error); return value ?? null; } - })(); + }; } /**@override */ diff --git a/module/data/fields/pseudoDocumentsField.mjs b/module/data/fields/pseudoDocumentsField.mjs deleted file mode 100644 index 0f48af5b..00000000 --- a/module/data/fields/pseudoDocumentsField.mjs +++ /dev/null @@ -1,56 +0,0 @@ -import PseudoDocument from '../pseudo-documents/base/pseudoDocument.mjs'; - -const { TypedObjectField, TypedSchemaField } = foundry.data.fields; - -/** - * @typedef _PseudoDocumentsFieldOptions - * @property {Number} [max] - The maximum amount of elements (default: `Infinity`) - * @property {String[]} [validTypes] - Allowed pseudo-documents types (default: `[]`) - * @property {Function} [validateKey] - callback for validate keys of the object; - - * @typedef {foundry.data.types.DataFieldOptions & _PseudoDocumentsFieldOptions} PseudoDocumentsFieldOptions - */ -export default class PseudoDocumentsField extends TypedObjectField { - /** - * @param {PseudoDocument} model - The PseudoDocument of each entry in this collection. - * @param {PseudoDocumentsFieldOptions} [options] - Options which configure the behavior of the field - * @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field - */ - constructor(model, options = {}, context = {}) { - options.validateKey ||= key => foundry.data.validators.isValidId(key); - if (!foundry.utils.isSubclass(model, PseudoDocument)) throw new Error('The model must be a PseudoDocument'); - - const allTypes = model.TYPES; - - const filteredTypes = options.validTypes - ? Object.fromEntries( - Object.entries(allTypes).filter(([key]) => options.validTypes.includes(key)) - ) - : allTypes; - - const field = new TypedSchemaField(filteredTypes); - super(field, options, context); - } - - /** @inheritdoc */ - static get _defaults() { - return Object.assign(super._defaults, { - max: Infinity, - validTypes: [] - }); - } - - /** @override */ - _validateType(value, options = {}) { - if (Object.keys(value).length > this.max) throw new Error(`cannot have more than ${this.max} elements`); - return super._validateType(value, options); - } - - /** @override */ - initialize(value, model, options = {}) { - if (!value) return; - value = super.initialize(value, model, options); - const collection = new foundry.utils.Collection(Object.values(value).map(d => [d._id, d])); - return collection; - } -} diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 3dd174d7..492fcfe1 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -5,52 +5,49 @@ * @property {string} type - The system type that this data model represents. * @property {boolean} hasDescription - Indicates whether items of this type have description field * @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field - * @property {Record} embedded - Record of document names of pseudo-documents and the path to the collection */ const fields = foundry.data.fields; export default class BaseDataItem extends foundry.abstract.TypeDataModel { - /** @returns {ItemDataModelMetadata}*/ - static get metadata() { - return { - label: "Base Item", - type: "base", - hasDescription: false, - isQuantifiable: false, - embedded: {}, - }; - } + /** @returns {ItemDataModelMetadata}*/ + static get metadata() { + return { + label: 'Base Item', + type: 'base', + hasDescription: false, + isQuantifiable: false + }; + } - /** @inheritDoc */ - static defineSchema() { - const schema = {}; + /** @inheritDoc */ + static defineSchema() { + const schema = {}; - if (this.metadata.hasDescription) - schema.description = new fields.HTMLField({ required: true, nullable: true }); + if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true }); - if (this.metadata.isQuantifiable) - schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); + if (this.metadata.isQuantifiable) + schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); - return schema; - } + return schema; + } - /** - * Convenient access to the item's actor, if it exists. - * @returns {foundry.documents.Actor | null} - */ - get actor() { - return this.parent.actor; - } + /** + * Convenient access to the item's actor, if it exists. + * @returns {foundry.documents.Actor | null} + */ + get actor() { + return this.parent.actor; + } - /** - * Obtain a data object used to evaluate any dice rolls associated with this Item Type - * @param {object} [options] - Options which modify the getRollData method. - * @returns {object} - */ - getRollData(options = {}) { - const actorRollData = this.actor?.getRollData() ?? {}; - const data = { ...actorRollData, item: { ...this } }; - return data; - } -} \ No newline at end of file + /** + * Obtain a data object used to evaluate any dice rolls associated with this Item Type + * @param {object} [options] - Options which modify the getRollData method. + * @returns {object} + */ + getRollData(options = {}) { + const actorRollData = this.actor?.getRollData() ?? {}; + const data = { ...actorRollData, item: { ...this } }; + return data; + } +} diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 47acb712..cd69648d 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -1,5 +1,6 @@ import BaseDataItem from './base.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; +import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import ActionField from '../fields/actionField.mjs'; export default class DHClass extends BaseDataItem { @@ -18,23 +19,16 @@ export default class DHClass extends BaseDataItem { return { ...super.defineSchema(), domains: new fields.ArrayField(new fields.StringField(), { max: 2 }), - classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })), + classItems: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), + evasion: new fields.NumberField({ initial: 0, integer: true }), hopeFeatures: new foundry.data.fields.ArrayField(new ActionField()), classFeatures: new foundry.data.fields.ArrayField(new ActionField()), - subclasses: new fields.ArrayField( - new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined }) - ), + subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), inventory: new fields.SchemaField({ - take: new fields.ArrayField( - new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined }) - ), - choiceA: new fields.ArrayField( - new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined }) - ), - choiceB: new fields.ArrayField( - new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined }) - ) + take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), + choiceA: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), + choiceB: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }) }), characterGuide: new fields.SchemaField({ suggestedTraits: new fields.SchemaField({ diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs index aff7eea0..6c8df798 100644 --- a/module/data/item/consumable.mjs +++ b/module/data/item/consumable.mjs @@ -1,14 +1,14 @@ -import BaseDataItem from "./base.mjs"; +import BaseDataItem from './base.mjs'; import ActionField from '../fields/actionField.mjs'; export default class DHConsumable extends BaseDataItem { - /** @inheritDoc */ + /** @inheritDoc */ static get metadata() { return foundry.utils.mergeObject(super.metadata, { - label: "TYPES.Item.consumable", - type: "consumable", + label: 'TYPES.Item.consumable', + type: 'consumable', hasDescription: true, - isQuantifiable: true, + isQuantifiable: true }); } diff --git a/module/data/item/miscellaneous.mjs b/module/data/item/miscellaneous.mjs index 71daad57..d7687dc7 100644 --- a/module/data/item/miscellaneous.mjs +++ b/module/data/item/miscellaneous.mjs @@ -5,10 +5,10 @@ export default class DHMiscellaneous extends BaseDataItem { /** @inheritDoc */ static get metadata() { return foundry.utils.mergeObject(super.metadata, { - label: "TYPES.Item.miscellaneous", - type: "miscellaneous", + label: 'TYPES.Item.miscellaneous', + type: 'miscellaneous', hasDescription: true, - isQuantifiable: true, + isQuantifiable: true }); } diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index 1e236ff4..3c996584 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -1,11 +1,11 @@ import ActionField from '../fields/actionField.mjs'; -import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; +import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import BaseDataItem from './base.mjs'; const featureSchema = () => { return new foundry.data.fields.SchemaField({ name: new foundry.data.fields.StringField({ required: true }), - effects: new foundry.data.fields.ArrayField(new ForeignDocumentUUIDField({ type: 'ActiveEffect' })), + effects: new ForeignDocumentUUIDArrayField({ type: 'ActiveEffect', required: false }), actions: new foundry.data.fields.ArrayField(new ActionField()) }); }; @@ -64,7 +64,7 @@ export default class DHSubclass extends BaseDataItem { } else if (subclassData) { ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassAlreadySelected')); return false; - } else if (classData.system.subclasses.every(x => x.uuid !== `Item.${data._id}`)) { + } else if (classData.system.subclasses.every(x => x.uuid !== (data.uuid ?? `Item.${data._id}`))) { ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.SubclassNotInClass')); return false; } diff --git a/module/data/pseudo-documents/_module.mjs b/module/data/pseudo-documents/_module.mjs deleted file mode 100644 index 6f50a137..00000000 --- a/module/data/pseudo-documents/_module.mjs +++ /dev/null @@ -1,2 +0,0 @@ -export { default as base } from './base/pseudoDocument.mjs'; -export * as feature from './feature/_module.mjs'; diff --git a/module/data/pseudo-documents/base/base.mjs b/module/data/pseudo-documents/base/base.mjs deleted file mode 100644 index 4d5313ba..00000000 --- a/module/data/pseudo-documents/base/base.mjs +++ /dev/null @@ -1,213 +0,0 @@ -/** - * @typedef {object} PseudoDocumentMetadata - * @property {string} name - The document name of this pseudo-document - * @property {Record} embedded - Record of document names and their collection paths - * @property {typeof foundry.applications.api.ApplicationV2} [sheetClass] - The class used to render this pseudo-document - * @property {string} defaultArtwork - The default image used for newly created documents - */ - -/** - * @class Base class for pseudo-documents - * @extends {foundry.abstract.DataModel} - */ -export default class BasePseudoDocument extends foundry.abstract.DataModel { - /** - * Pseudo-document metadata. - * @returns {PseudoDocumentMetadata} - */ - static get metadata() { - return { - name: '', - embedded: {}, - defaultArtwork: foundry.documents.Item.DEFAULT_ICON, - sheetClass: CONFIG.daggerheart.pseudoDocuments.sheetClass, - }; - } - - /** @override */ - static LOCALIZATION_PREFIXES = ['DOCUMENT']; - - /** @inheritdoc */ - static defineSchema() { - const { fields } = foundry.data; - - return { - _id: new fields.DocumentIdField({ initial: () => foundry.utils.randomID() }), - name: new fields.StringField({ required: true, blank: false, textSearch: true }), - img: new fields.FilePathField({ categories: ['IMAGE'], initial: this.metadata.defaultArtwork }), - description: new fields.HTMLField({ textSearch: true }) - }; - } - - /* -------------------------------------------- */ - /* Instance Properties */ - /* -------------------------------------------- */ - - /** - * The id of this pseudo-document. - * @type {string} - */ - get id() { - return this._id; - } - - /* -------------------------------------------- */ - - /** - * The uuid of this document. - * @type {string} - */ - get uuid() { - let parent = this.parent; - while (!(parent instanceof BasePseudoDocument) && !(parent instanceof foundry.abstract.Document)) - parent = parent.parent; - return [parent.uuid, this.constructor.metadata.name, this.id].join('.'); - } - - /* -------------------------------------------- */ - - /** - * The parent document of this pseudo-document. - * @type {foundry.abstract.Document} - */ - get document() { - let parent = this; - while (!(parent instanceof foundry.abstract.Document)) parent = parent.parent; - return parent; - } - - /* -------------------------------------------- */ - - /** - * Item to which this PseudoDocument belongs, if applicable. - * @type {foundry.documents.Item|null} - */ - get item() { - return this.parent?.parent instanceof Item ? this.parent.parent : null; - } - - /* -------------------------------------------- */ - - /** - * Actor to which this PseudoDocument's item belongs, if the item is embedded. - * @type {foundry.documents.Actor|null} - */ - get actor() { - return this.item?.parent ?? null; - } - - /* -------------------------------------------- */ - - /** - * The property path to this pseudo document relative to its parent document. - * @type {string} - */ - get fieldPath() { - const fp = this.schema.fieldPath; - let path = fp.slice(0, fp.lastIndexOf('element') - 1); - - if (this.parent instanceof BasePseudoDocument) { - path = [this.parent.fieldPath, this.parent.id, path].join('.'); - } - - return path; - } - - /* -------------------------------------------- */ - /* Embedded Document Methods */ - /* -------------------------------------------- */ - - /** - * Retrieve an embedded pseudo-document. - * @param {string} embeddedName The document name of the embedded pseudo-document. - * @param {string} id The id of the embedded pseudo-document. - * @param {object} [options] Retrieval options. - * @param {boolean} [options.strinct] Throw an error if the embedded pseudo-document does not exist? - * @returns {PseudoDocument|null} - */ - getEmbeddedDocument(embeddedName, id, { strict = false } = {}) { - const embeds = this.constructor.metadata.embedded ?? {}; - if (embeddedName in embeds) { - return foundry.utils.getProperty(this, embeds[embeddedName]).get(id, { strict }) ?? null; - } - return null; - } - - /* -------------------------------------------- */ - /* CRUD Operations */ - /* -------------------------------------------- */ - - /** - * Does this pseudo-document exist in the document's source? - * @type {boolean} - */ - get isSource() { - const source = foundry.utils.getProperty(this.document._source, this.fieldPath); - if (foundry.utils.getType(source) !== 'Object') { - throw new Error('Source is not an object!'); - } - return this.id in source; - } - - /** - * Create a new instance of this pseudo-document. - * @param {object} [data] The data used for the creation. - * @param {object} operation The context of the update operation. - * @param {foundry.abstract.Document} operation.parent The parent of this document. - * @returns {Promise} A promise that resolves to the updated document. - */ - static async create(data = {}, { parent, ...operation } = {}) { - if (!parent) { - throw new Error('A parent document must be specified for the creation of a pseudo-document!'); - } - const id = - operation.keepId && foundry.data.validators.isValidId(data._id) ? data._id : foundry.utils.randomID(); - - const fieldPath = parent.system.constructor.metadata.embedded?.[this.metadata.name]; - if (!fieldPath) { - throw new Error( - `A ${parent.documentName} of type '${parent.type}' does not support ${this.metadata.name}!` - ); - } - - const update = { [`system.${fieldPath}.${id}`]: { ...data, _id: id } }; - const updatedParent = await parent.update(update, operation); - return foundry.utils.getProperty(updatedParent, `system.${fieldPath}.${id}`); - } - - /** - * Delete this pseudo-document. - * @param {object} [operation] The context of the operation. - * @returns {Promise} A promise that resolves to the updated document. - */ - async delete(operation = {}) { - if (!this.isSource) throw new Error('You cannot delete a non-source pseudo-document!'); - const update = { [`${this.fieldPath}.-=${this.id}`]: null }; - return this.document.update(update, operation); - } - - /** - * Duplicate this pseudo-document. - * @returns {Promise} A promise that resolves to the updated document. - */ - async duplicate() { - if (!this.isSource) throw new Error('You cannot duplicate a non-source pseudo-document!'); - const docData = foundry.utils.mergeObject(this.toObject(), { - name: game.i18n.format('DOCUMENT.CopyOf', { name: this.name }) - }); - return this.constructor.create(docData, { parent: this.document }); - } - - /** - * Update this pseudo-document. - * @param {object} [change] The change to perform. - * @param {object} [operation] The context of the operation. - * @returns {Promise} A promise that resolves to the updated document. - */ - async update(change = {}, operation = {}) { - if (!this.isSource) throw new Error('You cannot update a non-source pseudo-document!'); - const path = [this.fieldPath, this.id].join('.'); - const update = { [path]: change }; - return this.document.update(update, operation); - } -} diff --git a/module/data/pseudo-documents/base/pseudoDocument.mjs b/module/data/pseudo-documents/base/pseudoDocument.mjs deleted file mode 100644 index 2db23ef5..00000000 --- a/module/data/pseudo-documents/base/pseudoDocument.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import BasePseudoDocument from './base.mjs'; -import SheetManagementMixin from './sheetManagementMixin.mjs'; - -/** @extends BasePseudoDocument */ -export default class PseudoDocument extends SheetManagementMixin(BasePseudoDocument) { - static get TYPES() { - const { types } = CONFIG.daggerheart.pseudoDocuments[this.metadata.name]; - const typeEntries = Object.entries(types).map(([key, { documentClass }]) => [key, documentClass]); - return (this._TYPES ??= Object.freeze(Object.fromEntries(typeEntries))); - } - - static _TYPES; - - /** - * The type of this shape. - * @type {string} - */ - static TYPE = ''; - - /* -------------------------------------------- */ - - static getTypesChoices(validTypes) { - const { types } = CONFIG.daggerheart.pseudoDocuments[model.metadata.name]; - const typeEntries = Object.entries(types) - .map(([key, { label }]) => [key, label]) - .filter(([key]) => !validTypes || validTypes.includes(key)); - - return Object.entries(typeEntries); - } - - /* -------------------------------------------- */ - - /** @override */ - static defineSchema() { - const { fields } = foundry.data; - - return Object.assign(super.defineSchema(), { - type: new fields.StringField({ - required: true, - blank: false, - initial: this.TYPE, - validate: value => value === this.TYPE, - validationError: `must be equal to "${this.TYPE}"` - }) - }); - } - - /** @inheritdoc */ - static async create(data = {}, { parent, ...operation } = {}) { - data = foundry.utils.deepClone(data); - if (!data.type) data.type = Object.keys(this.TYPES)[0]; - if (!(data.type in this.TYPES)) { - throw new Error( - `The '${data.type}' type is not a valid type for a '${this.metadata.documentName}' pseudo-document!` - ); - } - return super.create(data, { parent, ...operation }); - } -} diff --git a/module/data/pseudo-documents/base/sheetManagementMixin.mjs b/module/data/pseudo-documents/base/sheetManagementMixin.mjs deleted file mode 100644 index 796faf51..00000000 --- a/module/data/pseudo-documents/base/sheetManagementMixin.mjs +++ /dev/null @@ -1,158 +0,0 @@ -import BasePseudoDocument from './base.mjs'; -const { ApplicationV2 } = foundry.applications.api; - -/** - * A mixin that adds sheet management capabilities to pseudo-documents - * @template {typeof BasePseudoDocument} T - * @param {T} Base - * @returns {T & typeof PseudoDocumentWithSheets} - */ -export default function SheetManagementMixin(Base) { - class PseudoDocumentWithSheets extends Base { - /** - * Reference to the sheet of this pseudo-document. - * @type {ApplicationV2|null} - */ - get sheet() { - if (this._sheet) return this._sheet; - const cls = this.constructor.metadata.sheetClass ?? ApplicationV2; - - if (!foundry.utils.isSubclass(cls, ApplicationV2)) { - return void ui.notifications.error( - 'Daggerheart | Error on PseudoDocument | sheetClass must be ApplicationV2' - ); - } - - const sheet = new cls({ document: this }); - this._sheet = sheet; - return sheet; - } - - /* -------------------------------------------- */ - /* Static Properties */ - /* -------------------------------------------- */ - - /** - * Set of apps what should be re-render. - * @type {Set} - * @internal - */ - _apps = new Set(); - - /* -------------------------------------------- */ - - /** - * Existing sheets of a specific type for a specific document. - * @type {ApplicationV2 | null} - */ - _sheet = null; - - /* -------------------------------------------- */ - /* Display Methods */ - /* -------------------------------------------- */ - - /** - * Render all the Application instances which are connected to this PseudoDocument. - * @param {ApplicationRenderOptions} [options] Rendering options. - */ - render(options) { - for (const app of this._apps ?? []) { - app.render({ window: { title: app.title }, ...options }); - } - } - - /* -------------------------------------------- */ - - /** - * Register an application to respond to updates to a certain document. - * @param {ApplicationV2} app Application to update. - * @internal - */ - _registerApp(app) { - this._apps.add(app); - } - - /* -------------------------------------------- */ - - /** - * Remove an application from the render registry. - * @param {ApplicationV2} app Application to stop watching. - */ - _unregisterApp(app) { - this._apps.delete(app); - } - - /* -------------------------------------------- */ - /* Drag and Drop */ - /* -------------------------------------------- */ - - /** - * Serialize salient information for this PseudoDocument when dragging it. - * @returns {object} An object of drag data. - */ - toDragData() { - const dragData = { type: this.documentName, data: this.toObject() }; - if (this.id) dragData.uuid = this.uuid; - return dragData; - } - - /* -------------------------------------------- */ - /* Dialog Methods */ - /* -------------------------------------------- */ - - /** - * Spawn a dialog for creating a new PseudoDocument. - * @param {object} [data] Data to pre-populate the document with. - * @param {object} context - * @param {foundry.documents.Item} context.parent A parent for the document. - * @param {string[]|null} [context.types] A list of types to restrict the choices to, or null for no restriction. - * @returns {Promise} - */ - static async createDialog(data = {}, { parent, types = null, ...options } = {}) { - // TODO - } - - /** - * Present a Dialog form to confirm deletion of this PseudoDocument. - * @param {object} [options] - Additional options passed to `DialogV2.confirm`; - * @returns {Promise} A Promise which resolves to the deleted PseudoDocument. - */ - async deleteDialog(options = {}) { - const type = game.i18n.localize(this.constructor.metadata.label); - const content = options.content ?? `

- ${game.i18n.localize("AreYouSure")} - ${game.i18n.format("SIDEBAR.DeleteWarning", { type })} -

`; - - return foundry.applications.api.DialogV2.confirm({ - content, - yes: { callback: () => this.delete(operation) }, - window: { - icon: "fa-solid fa-trash", - title: `${game.i18n.format("DOCUMENT.Delete", { type })}: ${this.name}` - }, - ...options - }); - } - - /** - * Gets the default new name for a Document - * @param {object} collection - Collection of Documents - * @returns {string} - */ - static defaultName(collection) { - const documentName = this.metadata.name; - const takenNames = new Set(); - for (const document of collection) takenNames.add(document.name); - - const config = CONFIG.daggerheart.pseudoDocuments[documentName]; - const baseName = game.i18n.localize(config.label); - let name = baseName; - let index = 1; - while (takenNames.has(name)) name = `${baseName} (${++index})`; - return name; - } - } - - return PseudoDocumentWithSheets; -} diff --git a/module/data/pseudo-documents/feature/_module.mjs b/module/data/pseudo-documents/feature/_module.mjs deleted file mode 100644 index 794f5d27..00000000 --- a/module/data/pseudo-documents/feature/_module.mjs +++ /dev/null @@ -1,2 +0,0 @@ -export { default as BaseFeatureData } from './baseFeatureData.mjs'; -export { default as WeaponFeature } from './weaponFeature.mjs'; diff --git a/module/data/pseudo-documents/feature/baseFeatureData.mjs b/module/data/pseudo-documents/feature/baseFeatureData.mjs deleted file mode 100644 index 61d1468d..00000000 --- a/module/data/pseudo-documents/feature/baseFeatureData.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import PseudoDocument from '../base/pseudoDocument.mjs'; - -export default class BaseFeatureData extends PseudoDocument { - /**@inheritdoc */ - static get metadata() { - return foundry.utils.mergeObject( - super.metadata, - { - name: 'feature', - embedded: {}, - //sheetClass: null //TODO: define feature-sheet - }, - { inplace: false } - ); - } - - static defineSchema() { - const { fields } = foundry.data; - const schema = super.defineSchema(); - return Object.assign(schema, { - subtype: new fields.StringField({ initial: 'test' }) - }); - } -} diff --git a/module/data/pseudo-documents/feature/weaponFeature.mjs b/module/data/pseudo-documents/feature/weaponFeature.mjs deleted file mode 100644 index c8039ede..00000000 --- a/module/data/pseudo-documents/feature/weaponFeature.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import BaseFeatureData from './baseFeatureData.mjs'; - -export default class WeaponFeature extends BaseFeatureData { - /**@override */ - static TYPE = 'weapon'; -} diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index fdbd925c..6382b613 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -1,7 +1,15 @@ +import { fearDisplay } from '../../config/generalConfig.mjs'; + export default class DhAppearance extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; return { + displayFear: new fields.StringField({ + required: true, + choices: fearDisplay, + initial: fearDisplay.token.value, + label: 'DAGGERHEART.Settings.Appearance.FIELDS.displayFear.label' + }), dualityColorScheme: new fields.StringField({ required: true, choices: DualityRollColor, @@ -35,8 +43,6 @@ export default class DhAppearance extends foundry.abstract.DataModel { }) }; } - - static defaultSchema = {}; } export const DualityRollColor = { diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs new file mode 100644 index 00000000..bf2aed4b --- /dev/null +++ b/module/data/settings/Automation.mjs @@ -0,0 +1,12 @@ +export default class DhAutomation extends foundry.abstract.DataModel { + static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.Automation']; // Doesn't work for some reason + + static defineSchema() { + const fields = foundry.data.fields; + return { + hope: new fields.BooleanField({ required: true, initial: false }), + actionPoints: new fields.BooleanField({ required: true, initial: false }), + countdowns: new fields.BooleanField({ requireD: true, initial: false }) + }; + } +} diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs new file mode 100644 index 00000000..3aef56d6 --- /dev/null +++ b/module/data/settings/Homebrew.mjs @@ -0,0 +1,55 @@ +import { defaultRestOptions } from '../../config/generalConfig.mjs'; + +export default class DhHomebrew extends foundry.abstract.DataModel { + static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.Homebrew']; // Doesn't work for some reason + + static defineSchema() { + const fields = foundry.data.fields; + return { + maxFear: new fields.NumberField({ + required: true, + integer: true, + min: 0, + initial: 12, + label: 'DAGGERHEART.Settings.Homebrew.FIELDS.maxFear.label' + }), + traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), { + initial: () => [2, 1, 1, 0, 0, -1] + }), + restMoves: new fields.SchemaField({ + longRest: new fields.SchemaField({ + nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), + moves: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + img: new fields.FilePathField({ + initial: 'icons/magic/life/cross-worn-green.webp', + categories: ['IMAGE'], + base64: false + }), + description: new fields.HTMLField(), + actions: new fields.ArrayField(new fields.ObjectField()) + }), + { initial: defaultRestOptions.longRest() } + ) + }), + shortRest: new fields.SchemaField({ + nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), + moves: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + img: new fields.FilePathField({ + initial: 'icons/magic/life/cross-worn-green.webp', + categories: ['IMAGE'], + base64: false + }), + description: new fields.HTMLField(), + actions: new fields.ArrayField(new fields.ObjectField()) + }), + { initial: defaultRestOptions.shortRest() } + ) + }) + }) + }; + } +} diff --git a/module/data/settings/RangeMeasurement.mjs b/module/data/settings/RangeMeasurement.mjs new file mode 100644 index 00000000..5d3e1125 --- /dev/null +++ b/module/data/settings/RangeMeasurement.mjs @@ -0,0 +1,17 @@ +export default class DhRangeMeasurement extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + return { + enabled: new fields.BooleanField({ required: true, initial: false, label: 'DAGGERHEART.General.Enabled' }), + melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.Range.melee.name' }), + veryClose: new fields.NumberField({ + required: true, + initial: 15, + label: 'DAGGERHEART.Range.veryClose.name' + }), + close: new fields.NumberField({ required: true, initial: 30, label: 'DAGGERHEART.Range.close.name' }), + far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.Range.far.name' }), + veryFar: new fields.NumberField({ required: true, initial: 120, label: 'DAGGERHEART.Range.veryFar.name' }) + }; + } +} diff --git a/module/data/settings/VariantRules.mjs b/module/data/settings/VariantRules.mjs index 7d28a1d7..25e012bc 100644 --- a/module/data/settings/VariantRules.mjs +++ b/module/data/settings/VariantRules.mjs @@ -5,12 +5,22 @@ export default class DhVariantRules extends foundry.abstract.DataModel { const fields = foundry.data.fields; return { actionTokens: new fields.SchemaField({ - enabled: new fields.BooleanField({ required: true, initial: false }), - tokens: new fields.NumberField({ required: true, integer: true, initial: 3 }) + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.Settings.VariantRules.FIELDS.actionTokens.enabled.label' + }), + tokens: new fields.NumberField({ + required: true, + integer: true, + initial: 3, + label: 'DAGGERHEART.Settings.VariantRules.FIELDS.actionTokens.tokens.label' + }) }), - useCoins: new fields.BooleanField({ initial: false }) + useCoins: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.Settings.VariantRules.FIELDS.useCoins.label' + }) }; } - - static defaultSchema = {}; } diff --git a/module/data/settings/_module.mjs b/module/data/settings/_module.mjs new file mode 100644 index 00000000..dc99ed36 --- /dev/null +++ b/module/data/settings/_module.mjs @@ -0,0 +1,7 @@ +import DhAppearance from './Appearance.mjs'; +import DhAutomation from './Automation.mjs'; +import DhHomebrew from './Homebrew.mjs'; +import DhRangeMeasurement from './RangeMeasurement.mjs'; +import DhVariantRules from './VariantRules.mjs'; + +export { DhAppearance, DhAutomation, DhHomebrew, DhRangeMeasurement, DhVariantRules }; diff --git a/module/dialogs/selectDialog.mjs b/module/dialogs/selectDialog.mjs deleted file mode 100644 index 484979cc..00000000 --- a/module/dialogs/selectDialog.mjs +++ /dev/null @@ -1,97 +0,0 @@ -export default class SelectDialog extends Dialog { - constructor(data, options) { - super(options); - - this.data = { - title: data.title, - buttons: data.buttons, - content: foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/dialog/item-select.hbs', - { - items: data.choices - } - ) - }; - - this.actor = data.actor; - this.actionCostMax = data.actionCostMax; - this.nrChoices = data.nrChoices; - this.validate = data.validate; - } - - async getData(options = {}) { - let buttons = Object.keys(this.data.buttons).reduce((obj, key) => { - let b = this.data.buttons[key]; - b.cssClass = (this.data.default === key ? [key, 'default', 'bright'] : [key]).join(' '); - if (b.condition !== false) obj[key] = b; - return obj; - }, {}); - - const content = await this.data.content; - - return { - content: content, - buttons: buttons - }; - } - - activateListeners(html) { - super.activateListeners(html); - $(html).find('.item-button').click(this.selectChoice); - } - - selectChoice = async event => { - if (this.validate) { - if (!this.validate(event.currentTarget.dataset.validateProp)) { - return; - } - } - - event.currentTarget.classList.toggle('checked'); - $(event.currentTarget).find('i')[0].classList.toggle('checked'); - - const buttons = $(this.element[0]).find('button.checked'); - if (buttons.length === this.nrChoices) { - $(event.currentTarget).closest('.window-content').find('.confirm')[0].disabled = false; - } else { - $(event.currentTarget).closest('.window-content').find('.confirm')[0].disabled = true; - } - }; - - /** - * - * @param {*} data - * choices, actor, title, cancelMessage, nrChoices, validate - * @returns - */ - static async selectItem(data) { - return this.wait({ - title: data.title ?? 'Selection', - buttons: { - no: { - icon: '', - label: game.i18n.localize('DAGGERHEART.General.Cancel'), - callback: _ => { - if (data.cancelMessage) { - ChatMessage.create({ content: data.cancelMessage }); - } - return []; - } - }, - confirm: { - icon: '', - label: game.i18n.localize('DAGGERHEART.General.OK'), - callback: html => { - const buttons = $(html).find('button.checked'); - return buttons.map(key => Number.parseInt(buttons[key].dataset.index)).toArray(); - }, - disabled: true - } - }, - choices: data.choices, - actor: data.actor, - nrChoices: data.nrChoices ?? 1, - validate: data.validate - }); - } -} diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 3253409b..3b5fe734 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -324,7 +324,7 @@ export default class DhpActor extends Actor { ); if (this.type === 'character') { - const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope); + const automateHope = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope; if (automateHope && result.hopeUsed) { await this.update({ @@ -354,7 +354,7 @@ export default class DhpActor extends Actor { hope = roll.dice[0].results[0].result; fear = roll.dice[1].results[0].result; if ( - game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope) && + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation).hope && config.roll.type === 'action' ) { if (hope > fear) { diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs index 01fbe1af..61884acc 100644 --- a/module/enrichers/DualityRollEnricher.mjs +++ b/module/enrichers/DualityRollEnricher.mjs @@ -1,7 +1,7 @@ import { abilities } from '../config/actorConfig.mjs'; -import { rollCommandToJSON } from '../helpers/utils.mjs'; +import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs'; -export function dualityRollEnricher(match, _options) { +export default function DhDualityRollEnricher(match, _options) { const roll = rollCommandToJSON(match[1]); if (!roll) return match[0]; @@ -39,3 +39,24 @@ export function getDualityMessage(roll) { return dualityElement; } + +export const renderDualityButton = async event => { + const button = event.currentTarget, + traitValue = button.dataset.trait?.toLowerCase(), + target = getCommandTarget(); + if (!target) return; + + const config = { + event: event, + title: button.dataset.title, + roll: { + modifier: traitValue ? target.system.traits[traitValue].value : null, + label: button.dataset.label, + type: button.dataset.actionType ?? null // Need check + }, + chatMessage: { + template: 'systems/daggerheart/templates/chat/duality-roll.hbs' + } + }; + await target.diceRoll(config); +}; diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs new file mode 100644 index 00000000..50b27068 --- /dev/null +++ b/module/enrichers/TemplateEnricher.mjs @@ -0,0 +1,60 @@ +import { range as configRange } from '../config/generalConfig.mjs'; + +export default function DhTemplateEnricher(match, _options) { + const parts = match[1].split('|').map(x => x.trim()); + + let type = null, + range = null; + + parts.forEach(part => { + const split = part.split(':').map(x => x.toLowerCase().trim()); + if (split.length === 2) { + switch (split[0]) { + case 'type': + const matchedType = Object.values(CONST.MEASURED_TEMPLATE_TYPES).find( + x => x.toLowerCase() === split[1] + ); + type = matchedType; + break; + case 'range': + const matchedRange = Object.values(configRange).find( + x => x.id.toLowerCase() === split[1] || x.short === split[1] + ); + range = matchedRange?.id; + break; + } + } + }); + + if (!type || !range) return match[0]; + + const templateElement = document.createElement('span'); + templateElement.innerHTML = ` + + `; + + return templateElement; +} + +export const renderMeasuredTemplate = async event => { + const button = event.currentTarget, + type = button.dataset.type, + range = button.dataset.range; + + if (!type || !range || !game.canvas.scene) return; + + const distance = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement)[range]; + const { width, height } = game.canvas.scene.dimensions; + canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [ + { + x: width / 2, + y: height / 2, + t: type, + distance: distance, + width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined, + angle: type === CONST.MEASURED_TEMPLATE_TYPES.CONE ? CONFIG.MeasuredTemplate.defaults.angle : undefined + } + ]); +}; diff --git a/module/enrichers/_module.mjs b/module/enrichers/_module.mjs new file mode 100644 index 00000000..4ba1bdb6 --- /dev/null +++ b/module/enrichers/_module.mjs @@ -0,0 +1,4 @@ +import DhDualityRollEnricher from './DualityRollEnricher.mjs'; +import DhTemplateEnricher from './TemplateEnricher.mjs'; + +export { DhDualityRollEnricher, DhTemplateEnricher }; diff --git a/module/helpers/socket.mjs b/module/helpers/socket.mjs index fd768d34..6cd041aa 100644 --- a/module/helpers/socket.mjs +++ b/module/helpers/socket.mjs @@ -1,20 +1,68 @@ export function handleSocketEvent({ action = null, data = {} } = {}) { switch (action) { case socketEvent.GMUpdate: - Hooks.callAll(socketEvent.GMUpdate, data.action, data.uuid, data.update); + Hooks.callAll(socketEvent.GMUpdate, data); break; case socketEvent.DhpFearUpdate: Hooks.callAll(socketEvent.DhpFearUpdate); break; + case socketEvent.Refresh: + Hooks.call(socketEvent.Refresh, data); + break; } } export const socketEvent = { - GMUpdate: 'DhpGMUpdate', - DhpFearUpdate: 'DhpFearUpdate' + GMUpdate: 'DhGMUpdate', + Refresh: 'DhRefresh', + DhpFearUpdate: 'DhFearUpdate' }; export const GMUpdateEvent = { - UpdateDocument: 'DhpGMUpdateDocument', - UpdateFear: 'DhpUpdateFear' + UpdateDocument: 'DhGMUpdateDocument', + UpdateSetting: 'DhGMUpdateSetting', + UpdateFear: 'DhGMUpdateFear' +}; + +export const RefreshType = { + Countdown: 'DhCoundownRefresh' +}; + +export const registerSocketHooks = () => { + Hooks.on(socketEvent.GMUpdate, async data => { + if (game.user.isGM) { + const document = data.uuid ? await fromUuid(data.uuid) : null; + switch (data.action) { + case GMUpdateEvent.UpdateDocument: + if (document && data.update) { + await document.update(data.update); + } + break; + case GMUpdateEvent.UpdateSetting: + if (game.user.isGM) { + await game.settings.set(SYSTEM.id, data.uuid, data.update); + } + break; + case GMUpdateEvent.UpdateFear: + if (game.user.isGM) { + await game.settings.set( + SYSTEM.id, + SYSTEM.SETTINGS.gameSettings.Resources.Fear, + Math.max(Math.min(data.update, 6), 0) + ); + Hooks.callAll(socketEvent.DhpFearUpdate); + await game.socket.emit(`system.${SYSTEM.id}`, { action: socketEvent.DhpFearUpdate }); + } + break; + } + + if (data.refresh) { + await game.socket.emit(`system.${SYSTEM.id}`, { + action: socketEvent.Refresh, + data: data.refresh + }); + Hooks.call(socketEvent.Refresh, data.refresh); + } + } + }); }; diff --git a/module/placeables/_module.mjs b/module/placeables/_module.mjs new file mode 100644 index 00000000..52e3dffe --- /dev/null +++ b/module/placeables/_module.mjs @@ -0,0 +1,3 @@ +import DhMeasuredTemplate from './measuredTemplate.mjs'; + +export { DhMeasuredTemplate }; diff --git a/module/placeables/measuredTemplate.mjs b/module/placeables/measuredTemplate.mjs new file mode 100644 index 00000000..385cf81e --- /dev/null +++ b/module/placeables/measuredTemplate.mjs @@ -0,0 +1,35 @@ +export default class DhMeasuredTemplate extends foundry.canvas.placeables.MeasuredTemplate { + _refreshRulerText() { + super._refreshRulerText(); + + const rangeMeasurementSettings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement); + if (rangeMeasurementSettings.enabled) { + const splitRulerText = this.ruler.text.split(' '); + if (splitRulerText.length > 0) { + const rulerValue = Number(splitRulerText[0]); + const vagueLabel = this.constructor.getDistanceLabel(rulerValue, rangeMeasurementSettings); + this.ruler.text = vagueLabel; + } + } + } + + static getDistanceLabel(distance, settings) { + if (distance <= settings.melee) { + return game.i18n.localize('DAGGERHEART.Range.melee.name'); + } + if (distance <= settings.veryClose) { + return game.i18n.localize('DAGGERHEART.Range.veryClose.name'); + } + if (distance <= settings.close) { + return game.i18n.localize('DAGGERHEART.Range.close.name'); + } + if (distance <= settings.far) { + return game.i18n.localize('DAGGERHEART.Range.far.name'); + } + if (distance <= settings.veryFar) { + return game.i18n.localize('DAGGERHEART.Range.veryFar.name'); + } + + return ''; + } +} diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index da3c27e9..4b1ee434 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -1,3 +1,5 @@ +import { actionsTypes } from '../data/_module.mjs'; + export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor() { super(); @@ -44,6 +46,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.ability-use-button').forEach(element => element.addEventListener('click', event => this.abilityUseButton.bind(this)(event, data.message)) ); + html.querySelectorAll('.action-use-button').forEach(element => + element.addEventListener('click', event => this.actionUseButton.bind(this)(event, data.message)) + ); }; setupHooks() { @@ -173,4 +178,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const actor = game.actors.get(message.system.source.actor); await actor.useAction(action); }; + + actionUseButton = async (_, message) => { + const parent = await foundry.utils.fromUuid(message.system.actor); + const testAction = Object.values(message.system.moves)[0].actions[0]; + const cls = actionsTypes[testAction.type]; + const action = new cls( + { ...testAction, _id: foundry.utils.randomID(), name: game.i18n.localize(testAction.name) }, + { parent: parent } + ); + + action.use(); + }; } diff --git a/module/ui/combatTracker.mjs b/module/ui/combatTracker.mjs index 8b71f627..d6d64f98 100644 --- a/module/ui/combatTracker.mjs +++ b/module/ui/combatTracker.mjs @@ -1,9 +1,12 @@ +import { EncounterCountdowns } from '../applications/countdowns.mjs'; + export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker { static DEFAULT_OPTIONS = { actions: { requestSpotlight: this.requestSpotlight, toggleSpotlight: this.toggleSpotlight, - setActionTokens: this.setActionTokens + setActionTokens: this.setActionTokens, + openCountdowns: this.openCountdowns } }; @@ -83,6 +86,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C .map(x => x.id) .indexOf(combatantId); + if (this.viewed.turn !== toggleTurn) Hooks.callAll(SYSTEM.HOOKS.spotlight, {}); + await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn }); await combatant.update({ 'system.spotlight.requesting': false }); } @@ -97,4 +102,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C await combatant.update({ 'system.actionTokens': newIndex }); this.render(); } + + static openCountdowns() { + new EncounterCountdowns().open(); + } } diff --git a/module/ui/ruler.mjs b/module/ui/ruler.mjs index 2c6848d2..655c9c61 100644 --- a/module/ui/ruler.mjs +++ b/module/ui/ruler.mjs @@ -1,34 +1,18 @@ +import DhMeasuredTemplate from '../placeables/measuredTemplate.mjs'; + export default class DhpRuler extends foundry.canvas.interaction.Ruler { _getWaypointLabelContext(waypoint, state) { const context = super._getWaypointLabelContext(waypoint, state); if (!context) return; - const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement); + const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement); if (range.enabled) { - const distance = this.#getRangeLabel(waypoint.measurement.distance.toNearest(0.01), range); + const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); context.cost = { total: distance, units: null }; context.distance = { total: distance, units: null }; } return context; } - - #getRangeLabel(distance, settings) { - if (distance <= settings.melee) { - return game.i18n.localize('DAGGERHEART.Range.Melee.Name'); - } - if (distance <= settings.veryClose) { - return game.i18n.localize('DAGGERHEART.Range.VeryClose.Name'); - } - if (distance <= settings.close) { - return game.i18n.localize('DAGGERHEART.Range.Close.Name'); - } - if (distance <= settings.far) { - return game.i18n.localize('DAGGERHEART.Range.Far.Name'); - } - if (distance <= settings.veryFar) { - return game.i18n.localize('DAGGERHEART.Range.VeryFar.Name'); - } - } } diff --git a/module/ui/tokenRuler.mjs b/module/ui/tokenRuler.mjs index 4faa516e..5c5a275e 100644 --- a/module/ui/tokenRuler.mjs +++ b/module/ui/tokenRuler.mjs @@ -1,34 +1,18 @@ +import DhMeasuredTemplate from '../placeables/measuredTemplate.mjs'; + export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.TokenRuler { _getWaypointLabelContext(waypoint, state) { const context = super._getWaypointLabelContext(waypoint, state); if (!context) return; - const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.RangeMeasurement); + const range = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.RangeMeasurement); if (range.enabled) { - const distance = this.#getRangeLabel(waypoint.measurement.distance.toNearest(0.01), range); + const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range); context.cost = { total: distance, units: null }; context.distance = { total: distance, units: null }; } return context; } - - #getRangeLabel(distance, settings) { - if (distance <= settings.melee) { - return game.i18n.localize('DAGGERHEART.Range.Melee.Name'); - } - if (distance <= settings.veryClose) { - return game.i18n.localize('DAGGERHEART.Range.VeryClose.Name'); - } - if (distance <= settings.close) { - return game.i18n.localize('DAGGERHEART.Range.Close.Name'); - } - if (distance <= settings.far) { - return game.i18n.localize('DAGGERHEART.Range.Far.Name'); - } - if (distance <= settings.veryFar) { - return game.i18n.localize('DAGGERHEART.Range.VeryFar.Name'); - } - } } diff --git a/package-lock.json b/package-lock.json index 1e543065..7b1bed60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "daggerheart", "dependencies": { "@yaireo/tagify": "^4.17.9", "gulp": "^5.0.0", @@ -24,27 +23,27 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", - "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@foundryvtt/foundryvtt-cli": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-1.0.2.tgz", - "integrity": "sha512-pERML7ViBiqwP11NS1kci0Q38t4h557F/Mj+DjYmmgumMJIZqDsVv2XU3bwOJS7+6yzbUmUc2/jRD6EIx+U/fw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-1.1.0.tgz", + "integrity": "sha512-ergKZDUSgQ79168r38ORyN4v/UTliA40rxElaUh5iS27Qw9H8Ep/ll8j3/HfiikO3XUDwYxZLfDJfbcyj2i9TQ==", "dev": true, "dependencies": { - "chalk": "^5.2.0", - "classic-level": "^1.2.0", + "chalk": "^5.4.1", + "classic-level": "^1.4.1", "esm": "^3.2.25", "js-yaml": "^4.1.0", - "mkdirp": "^3.0.0", - "nedb-promises": "^6.2.1", - "yargs": "^17.7.1" + "mkdirp": "^3.0.1", + "nedb-promises": "^6.2.3", + "yargs": "^17.7.2" }, "bin": { "fvtt": "fvtt.mjs" @@ -53,97 +52,6 @@ "node": ">17.0.0" } }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -164,15 +72,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.7", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", - "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", + "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -195,15 +103,14 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", "is-module": "^1.0.0", "resolve": "^1.22.1" }, @@ -220,14 +127,14 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "picomatch": "^4.0.2" }, "engines": { "node": ">=14.0.0" @@ -241,10 +148,238 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", - "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", "cpu": [ "x64" ], @@ -260,9 +395,9 @@ "dev": true }, "node_modules/@seald-io/nedb": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.0.4.tgz", - "integrity": "sha512-CUNcMio7QUHTA+sIJ/DC5JzVNNsHe743TPmC4H5Gij9zDLMbmrCT2li3eVB72/gF63BPS8pWEZrjlAMRKA8FDw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.1.1.tgz", + "integrity": "sha512-u7fVfzKQ/3ZaIOnYQONf2lPZtGUeQtMPjfcaQkCw/GZv5dzn20qKW6sfN0NkVbr0ksJMlWcFXNGcXYsQSb1a1g==", "dev": true, "dependencies": { "@seald-io/binary-search-tree": "^1.0.3", @@ -280,9 +415,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, "node_modules/@types/resolve": { "version": "1.20.2", @@ -291,15 +426,17 @@ "dev": true }, "node_modules/@yaireo/tagify": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/@yaireo/tagify/-/tagify-4.22.2.tgz", - "integrity": "sha512-R2QSHX3eCJiAf7kIoTojlMbjHiS271z5V2XLyVf4VvgPVQpXBNFskI41DyaDiMS5enXZ13j8kmm92GI+QmgdnQ==", + "version": "4.35.1", + "resolved": "https://registry.npmjs.org/@yaireo/tagify/-/tagify-4.35.1.tgz", + "integrity": "sha512-PF6nwp1lHMVS2IVpqZTxx53fQyEBUDjdDQsi7gD9Vx/EQ398gRH8LByEL8lawws9iJHB90on74VWG3pvwznrEg==", "engines": { - "node": ">=14.20.0", - "npm": ">=8.0.0" + "node": ">=16.15.0", + "npm": ">=9.0.0" }, "peerDependencies": { - "prop-types": "^15.7.2" + "prop-types": ">15.5.7", + "react": "*", + "react-dom": "*" } }, "node_modules/abstract-level": { @@ -320,29 +457,6 @@ "node": ">=12" } }, - "node_modules/abstract-level/node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -382,14 +496,12 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -415,6 +527,17 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -577,9 +700,9 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { "balanced-match": "^1.0.0" @@ -597,9 +720,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -616,10 +739,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -651,29 +774,45 @@ "ieee754": "^1.2.1" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -695,9 +834,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001599", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", - "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", + "version": "1.0.30001724", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", + "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", "dev": true, "funding": [ { @@ -724,15 +863,12 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -833,27 +969,48 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -865,6 +1022,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -885,11 +1043,6 @@ "node": ">=0.8" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==" - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -919,12 +1072,12 @@ "dev": true }, "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/commondir": { @@ -969,98 +1122,47 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/concurrently/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/concurrently/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/convert-source-map": { @@ -1246,6 +1348,24 @@ "postcss": "^8.2.15" } }, + "node_modules/cssnano/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssnano/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/csso": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", @@ -1380,6 +1500,20 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/each-props": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", @@ -1393,9 +1527,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.711", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.711.tgz", - "integrity": "sha512-hRg81qzvUEibX2lDxnFlVCHACa+LtrCPIsWAxo161LDYIB3jauf57RGsMZV9mvGwE98yGH06icj3zBEoOkxd/w==", + "version": "1.5.171", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", + "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", "dev": true }, "node_modules/emoji-regex": { @@ -1404,9 +1538,9 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dependencies": { "once": "^1.4.0" } @@ -1445,13 +1579,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -1465,10 +1596,22 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -1489,9 +1632,9 @@ "dev": true }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, "node_modules/execa": { @@ -1533,6 +1676,18 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -1611,12 +1766,18 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/for-in": { @@ -1707,16 +1868,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -1725,6 +1891,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -1741,6 +1920,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1768,9 +1948,9 @@ } }, "node_modules/glob-stream": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.2.tgz", - "integrity": "sha512-R8z6eTB55t3QeZMmU1C+Gv+t5UnNRkA55c5yo67fAVfxODxieTwsjNG7utxS/73NdP1NbDgCrhVEg2h00y4fFw==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", "dependencies": { "@gulpjs/to-absolute-glob": "^4.0.0", "anymatch": "^3.1.3", @@ -1859,12 +2039,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1876,14 +2056,14 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/gulp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.0.tgz", - "integrity": "sha512-S8Z8066SSileaYw1S2N1I64IUc/myI2bqe2ihOBzO6+nKpvNSg7ZcWJt/AwF8LC/NVN+/QZ560Cb/5OPsyhkhg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.1.tgz", + "integrity": "sha512-PErok3DZSA5WGMd6XXV3IRNO0mlB+wW3OzhFJLEec1jSERg2j1bxJ6e5Fh6N6fn3FH2T9AP4UYNb/pYlADB9sA==", "dependencies": { "glob-watcher": "^6.0.0", - "gulp-cli": "^3.0.0", + "gulp-cli": "^3.1.0", "undertaker": "^2.0.0", - "vinyl-fs": "^4.0.0" + "vinyl-fs": "^4.0.2" }, "bin": { "gulp": "bin/gulp.js" @@ -1893,16 +2073,16 @@ } }, "node_modules/gulp-cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.0.0.tgz", - "integrity": "sha512-RtMIitkT8DEMZZygHK2vEuLPqLPAFB4sntSxg4NoDta7ciwGZ18l7JuhCTiS5deOJi2IoK0btE+hs6R4sfj7AA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.1.0.tgz", + "integrity": "sha512-zZzwlmEsTfXcxRKiCHsdyjZZnFvXWM4v1NqBJSYbuApkvVKivjcmOS2qruAJ+PkEHLFavcDKH40DPc1+t12a9Q==", "dependencies": { "@gulpjs/messages": "^1.1.0", "chalk": "^4.1.2", "copy-props": "^4.0.0", "gulplog": "^2.2.0", "interpret": "^3.1.1", - "liftoff": "^5.0.0", + "liftoff": "^5.0.1", "mute-stdout": "^2.0.0", "replace-homedir": "^2.0.0", "semver-greatest-satisfied-range": "^2.0.0", @@ -1917,6 +2097,116 @@ "node": ">=10.13.0" } }, + "node_modules/gulp-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/gulp-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/gulp-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/gulp-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/gulp-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/gulp-less": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-5.0.0.tgz", @@ -1964,22 +2254,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "engines": { "node": ">= 0.4" @@ -2143,6 +2421,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -2180,13 +2459,13 @@ } }, "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2206,19 +2485,27 @@ "node": ">=8" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/is-callable": { @@ -2234,16 +2521,41 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2265,12 +2577,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2329,6 +2644,24 @@ "@types/estree": "*" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -2353,12 +2686,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -2447,9 +2780,9 @@ } }, "node_modules/less": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", - "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.3.0.tgz", + "integrity": "sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -2459,7 +2792,7 @@ "lessc": "bin/lessc" }, "engines": { - "node": ">=6" + "node": ">=14" }, "optionalDependencies": { "errno": "^0.1.1", @@ -2503,9 +2836,9 @@ } }, "node_modules/liftoff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.0.tgz", - "integrity": "sha512-a5BQjbCHnB+cy+gsro8lXJ4kZluzOijzJ1UVVfyJYZC+IP2pLv1h4+aysQeKuTmyO8NAqfyQAk4HWaP/HjcKTg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.1.tgz", + "integrity": "sha512-wwLXMbuxSF8gMvubFcFRp56lkFV69twvbU5vDPbaw+Q+/rF8j0HKjGbIdlSi+LuJm9jf7k9PB+nTxnsLMPcv2Q==", "dependencies": { "extend": "^3.0.2", "findup-sync": "^5.0.0", @@ -2520,12 +2853,15 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lint-staged": { @@ -2555,51 +2891,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/lint-staged/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lint-staged/node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/listr2": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", @@ -2617,16 +2908,10 @@ "node": ">=18.0.0" } }, - "node_modules/listr2/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, "engines": { "node": ">= 12.13.0" @@ -2684,18 +2969,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", @@ -2740,15 +3013,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-dir": { @@ -2772,6 +3042,15 @@ "node": ">=0.10.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -2796,6 +3075,17 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2893,7 +3183,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2933,9 +3222,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -2944,9 +3233,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, "node_modules/normalize-path": { @@ -3100,6 +3389,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "node_modules/p-timeout": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", @@ -3175,17 +3470,18 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3226,53 +3522,19 @@ "node": ">= 0.10" } }, - "node_modules/plugin-error/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plugin-error/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plugin-error/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.37", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz", - "integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -3289,9 +3551,9 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3421,6 +3683,24 @@ } } }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss-merge-longhand": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", @@ -3539,9 +3819,9 @@ } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, "engines": { "node": "^10 || ^12 || >= 14" @@ -3551,13 +3831,13 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", - "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -3567,13 +3847,26 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", - "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.4" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -3582,6 +3875,19 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -3779,9 +4085,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -3889,6 +4195,27 @@ } ] }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "peer": true, + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3919,6 +4246,17 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -3960,17 +4298,20 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4054,11 +4395,11 @@ "dev": true }, "node_modules/rollup": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", - "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -4068,26 +4409,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.41.0", - "@rollup/rollup-android-arm64": "4.41.0", - "@rollup/rollup-darwin-arm64": "4.41.0", - "@rollup/rollup-darwin-x64": "4.41.0", - "@rollup/rollup-freebsd-arm64": "4.41.0", - "@rollup/rollup-freebsd-x64": "4.41.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", - "@rollup/rollup-linux-arm-musleabihf": "4.41.0", - "@rollup/rollup-linux-arm64-gnu": "4.41.0", - "@rollup/rollup-linux-arm64-musl": "4.41.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-musl": "4.41.0", - "@rollup/rollup-linux-s390x-gnu": "4.41.0", - "@rollup/rollup-linux-x64-gnu": "4.41.0", - "@rollup/rollup-linux-x64-musl": "4.41.0", - "@rollup/rollup-win32-arm64-msvc": "4.41.0", - "@rollup/rollup-win32-ia32-msvc": "4.41.0", - "@rollup/rollup-win32-x64-msvc": "4.41.0", + "@rollup/rollup-android-arm-eabi": "4.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, @@ -4118,6 +4459,37 @@ "postcss": "8.x" } }, + "node_modules/rollup-plugin-postcss/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/rollup-plugin-postcss/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/rollup-plugin-postcss/node_modules/pify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", @@ -4130,6 +4502,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/rollup-plugin-postcss/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/rollup-pluginutils": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", @@ -4155,9 +4539,23 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safe-identifier": { "version": "0.4.2", @@ -4165,17 +4563,40 @@ "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", "dev": true }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "optional": true }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "peer": true + }, "node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -4235,9 +4656,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "engines": { "node": ">= 0.4" @@ -4274,18 +4695,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4296,9 +4705,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4339,9 +4748,9 @@ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" }, "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" @@ -4351,11 +4760,11 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, "node_modules/string-argv": { @@ -4463,14 +4872,18 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -4522,6 +4935,15 @@ "node": ">=10.13.0" } }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -4578,9 +5000,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/unc-path-regex": { "version": "0.1.2", @@ -4613,9 +5035,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -4632,8 +5054,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -4677,12 +5099,11 @@ } }, "node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" @@ -4704,12 +5125,12 @@ } }, "node_modules/vinyl-fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz", - "integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", "dependencies": { "fs-mkdirp-stream": "^2.0.1", - "glob-stream": "^8.0.0", + "glob-stream": "^8.0.3", "graceful-fs": "^4.2.11", "iconv-lite": "^0.6.3", "is-valid-glob": "^1.0.0", @@ -4720,7 +5141,7 @@ "streamx": "^2.14.0", "to-through": "^3.0.0", "value-or-function": "^4.0.0", - "vinyl": "^3.0.0", + "vinyl": "^3.0.1", "vinyl-sourcemap": "^2.0.0" }, "engines": { @@ -4775,15 +5196,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -4810,18 +5233,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -4859,37 +5270,42 @@ } }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14.6" } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } } } diff --git a/package.json b/package.json index f8be74b9..d7b51dfd 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "scripts": { "start": "concurrently \"rollup -c --watch\" \"node ../../../../FoundryDev/main.js --dataPath=../../../ --noupnp\" \"gulp\"", "start-test": "node ./resources/app/main.js --dataPath=./ && rollup -c --watch && gulp", + "build": "npm run rollup && npm run gulp", + "rollup": "rollup -c", + "gulp": "gulp less", "pushLDBtoYML": "node ./tools/pushLDBtoYML.mjs", "pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs", "createSymlink": "node ./tools/create-symlink.mjs" diff --git a/styles/application.less b/styles/application.less index b6c21220..66735594 100644 --- a/styles/application.less +++ b/styles/application.less @@ -134,6 +134,12 @@ div.daggerheart.views.multiclass { } .downtime-container { + .downtime-header { + margin: 0; + color: light-dark(@dark-blue, @golden); + text-align: center; + } + .activity-container { display: flex; align-items: center; @@ -150,12 +156,32 @@ div.daggerheart.views.multiclass { } .activity-image { - width: 120px; + width: 80px; + position: relative; + display: flex; + justify-content: center; + margin-right: 8px; border: 2px solid black; border-radius: 50%; - margin-right: 8px; cursor: pointer; + .activity-select-label { + position: absolute; + top: -9px; + font-size: 14px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url(../assets/parchments/dh-parchment-light.png); + padding: 0 8px; + line-height: 1; + font-weight: bold; + } + + img { + border-radius: 50%; + } + &:hover, &.selected { filter: drop-shadow(0 0 6px gold); diff --git a/styles/characterCreation.less b/styles/characterCreation.less new file mode 100644 index 00000000..49f7e74a --- /dev/null +++ b/styles/characterCreation.less @@ -0,0 +1,412 @@ +.theme-light .daggerheart.dh-style.dialog.character-creation { + .tab-navigation nav a .descriptor { + background: red; + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + .main-selections-container { + .traits-container .suggested-traits-container .suggested-trait-container, + .creation-action-footer .footer-section nav a .descriptor, + .equipment-selection .simple-equipment-container .simple-equipment label { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + } +} + +.daggerheart.dh-style.dialog.character-creation { + .window-content { + gap: 16px; + } + + .tab-navigation { + nav { + flex: 1; + + a { + flex: 1; + text-align: center; + display: flex; + justify-content: center; + position: relative; + + &.disabled { + opacity: 0.4; + } + + .nav-section-text { + position: relative; + display: flex; + align-items: center; + } + + .finish-marker { + position: absolute; + align-self: center; + top: -8px; + padding: 4px; + border: 1px solid; + border-radius: 50%; + height: 16px; + width: 16px; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-cool-4); + content: ''; + + &.active { + background-color: var(--color-warm-2); + } + } + + .descriptor { + position: absolute; + bottom: -8px; + font-size: 12px; + border-radius: 8px; + width: 56px; + text-align: center; + line-height: 1; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url(../assets/parchments/dh-parchment-light.png); + } + } + } + } + + .main-selections-container { + display: flex; + flex-direction: column; + gap: 4px; + + .selections-container { + width: 140px; + display: flex; + flex-direction: column; + text-align: center; + + .card-preview-container { + border-color: light-dark(@dark-blue, @golden); + } + } + + .selections-outer-container { + display: flex; + justify-content: space-evenly; + height: 210px; + } + + .section-container { + border-radius: 8px; + border-color: light-dark(@dark-blue, @golden); + + legend { + margin-left: auto; + margin-right: auto; + font-size: 28px; + font-weight: bold; + padding: 0 8px; + } + + .section-inner-container { + position: relative; + border-radius: 8px; + border-color: light-dark(@dark-blue, @golden); + display: flex; + justify-content: center; + + legend { + font-size: 20px; + } + + .action-button { + position: absolute; + bottom: -8px; + height: 16px; + width: 110px; + min-height: unset; + border: 1px solid light-dark(@dark-blue, @golden); + color: light-dark(@beige, @beige); + background-color: light-dark(var(--color-warm-3), var(--color-warm-3)); + + &:hover { + background-color: light-dark(var(--color-warm-2), var(--color-warm-2)); + filter: drop-shadow(0 0 3px light-dark(var(--color-warm-2), var(--color-warm-2))); + } + } + } + } + + .traits-container { + text-align: center; + display: flex; + gap: 16px; + + .suggested-traits-container { + display: flex; + flex-wrap: wrap; + width: 176px; + gap: 4px; + margin-bottom: 8px; + + .suggested-trait-container { + width: 56px; + white-space: nowrap; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + } + + .traits-inner-container { + display: flex; + justify-content: space-evenly; + gap: 8px; + + .trait-container { + border: 1px solid light-dark(@dark-blue, @golden); + padding: 0 4px; + } + } + } + + .experiences-inner-container { + display: flex; + justify-content: space-evenly; + text-align: center; + + .experience-container { + position: relative; + display: flex; + align-items: center; + + .experience-description { + border-color: light-dark(@dark-blue, @golden); + padding-right: 24px; + } + + .experience-value { + position: absolute; + right: 0; + width: 22px; + border-left: 1px solid light-dark(@dark-blue, @golden); + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + } + } + + .creation-action-footer { + display: flex; + align-items: center; + gap: 32px; + + .footer-section { + display: flex; + align-items: center; + gap: 32px; + + nav { + flex: 1; + gap: 8px; + border: 0; + + a { + flex: 1; + text-align: center; + display: flex; + justify-content: center; + position: relative; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + + .nav-section-text { + position: relative; + display: flex; + align-items: center; + } + + .finish-marker { + position: absolute; + align-self: center; + top: -10px; + padding: 4px; + border: 1px solid; + border-radius: 50%; + height: 20px; + width: 20px; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-cool-4); + content: ''; + + &.finished { + background-color: var(--color-warm-2); + } + } + + .descriptor { + position: absolute; + bottom: -8px; + font-size: 12px; + border-radius: 8px; + width: 56px; + text-align: center; + line-height: 1; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url(../assets/parchments/dh-parchment-light.png); + } + } + } + + button { + flex: 1; + height: 100%; + white-space: nowrap; + } + } + } + + .main-equipment-selection { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 16px; + + &.triple { + grid-template-columns: 1fr 1fr 1fr; + } + } + + .equipment-selection { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + border: 2px solid light-dark(@dark-blue, @golden); + border-radius: 8px; + + legend { + margin-left: auto; + margin-right: auto; + font-size: 28px; + font-weight: bold; + padding: 0 8px; + white-space: nowrap; + } + + .equipment-subsection { + display: flex; + align-items: start; + gap: 32px; + } + + .equipment-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + } + + .simple-equipment-container { + display: flex; + flex-direction: column; + justify-content: space-evenly; + gap: 8px; + height: 100%; + + .simple-equipment { + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 8px; + position: relative; + display: flex; + justify-content: center; + + &.selectable { + cursor: pointer; + } + + &.inactive { + opacity: 0.4; + } + + label { + position: absolute; + top: -8px; + font-size: 12px; + white-space: nowrap; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url('../assets/parchments/dh-parchment-light.png'); + padding: 0 2px; + } + + img { + width: 60px; + height: 60px; + border-radius: 8px; + } + } + } + + .suggestion-container { + position: relative; + display: flex; + justify-content: center; + height: min-content; + border: 2px solid light-dark(@dark-blue, @golden); + border-radius: 8px; + + legend { + margin-left: auto; + margin-right: auto; + font-size: 12px; + } + + .suggestion-inner-container { + position: relative; + display: flex; + justify-content: center; + align-items: center; + padding: 6px; + cursor: grab; + + &.taken { + opacity: 0.4; + } + + label { + position: absolute; + top: -2px; + font-size: 12px; + } + + img { + width: 120px; + } + } + } + } + } + + .creation-action-footer { + display: flex; + align-items: center; + gap: 32px; + + button { + flex: 1; + height: 100%; + white-space: nowrap; + } + } +} diff --git a/styles/chat.less b/styles/chat.less index fc046d5c..8dbf8ddc 100644 --- a/styles/chat.less +++ b/styles/chat.less @@ -1,5 +1,7 @@ .chat-message { - .duality-modifiers, .duality-result, .dice-title { + .duality-modifiers, + .duality-result, + .dice-title { display: none; } } @@ -67,7 +69,8 @@ align-items: center; justify-content: center; position: relative; - &.hope, &.fear { + &.hope, + &.fear { .dice-wrapper { clip-path: polygon( 50% 0%, @@ -335,8 +338,10 @@ > * { padding: 0 8px; } - .message-content { - .duality-modifiers, .duality-result, .dice-title { + .message-content { + .duality-modifiers, + .duality-result, + .dice-title { display: flex; } .duality-modifiers { @@ -364,7 +369,7 @@ } .dice-result { .duality-modifiers { - display: flex; // Default => display: none; + display: flex; // Default => display: none; gap: 2px; margin-bottom: 4px; .duality-modifier { @@ -375,7 +380,9 @@ font-size: 12px; } } - .dice-formula, > .dice-total, .part-header { + .dice-formula, + > .dice-total, + .part-header { display: none; } .dice-tooltip { @@ -384,7 +391,7 @@ .tooltip-part { display: flex; align-items: end; - gap: .25rem; + gap: 0.25rem; .dice { .dice-rolls { margin-bottom: 0; diff --git a/styles/countdown.less b/styles/countdown.less new file mode 100644 index 00000000..dd80acc4 --- /dev/null +++ b/styles/countdown.less @@ -0,0 +1,154 @@ +.theme-light { + .daggerheart.dh-style.countdown { + &.minimized .minimized-view .mini-countdown-container { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + } +} + +.daggerheart.dh-style.countdown { + overflow: hidden; + + fieldset { + align-items: center; + margin-top: 5px; + border-radius: 6px; + border-color: light-dark(@dark-blue, @golden); + + legend { + font-family: @font-body; + font-weight: bold; + color: light-dark(@dark-blue, @golden); + + a { + text-shadow: none; + } + } + } + + &.minimized { + height: auto !important; + max-height: unset !important; + max-width: 740px !important; + width: auto !important; + + .window-content { + display: flex; + padding: 4px 8px; + justify-content: center; + } + + .minimized-view { + display: flex; + gap: 8px; + flex-wrap: wrap; + + .mini-countdown-container { + width: fit-content; + display: flex; + align-items: center; + gap: 8px; + border: 2px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + padding: 0 4px 0 0; + background-image: url('../assets/parchments/dh-parchment-light.png'); + color: light-dark(@beige, @dark); + cursor: pointer; + + &.disabled { + cursor: initial; + } + + img { + width: 30px; + height: 30px; + border-radius: 6px 0 0 6px; + } + + .mini-countdown-name { + white-space: nowrap; + } + + .mini-countdown-value { + } + } + } + } + + .hidden { + display: none; + } + + .window-content { + > div { + height: 100%; + + .expanded-view { + height: 100%; + display: flex; + flex-direction: column; + + .countdowns-menu { + display: flex; + gap: 8px; + + .flex { + flex: 1; + } + } + + .countdowns-container { + display: flex; + gap: 8px; + flex-wrap: wrap; + overflow: auto; + max-height: 100%; + + .countdown-fieldset { + width: 340px; + height: min-content; + position: relative; + + .ownership-button { + position: absolute; + top: 8px; + right: 8px; + font-size: 18px; + } + + .countdown-container { + display: flex; + align-items: center; + gap: 16px; + + img { + width: 150px; + height: 150px; + cursor: pointer; + + &.disabled { + cursor: initial; + } + } + + .countdown-inner-container { + display: flex; + flex-direction: column; + gap: 4px; + + .countdown-value-container { + display: flex; + gap: 4px; + + input { + max-width: 80px; + } + } + } + } + } + } + } + } + } +} diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 98b55faa..44e011ee 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -8,7 +8,6 @@ /* Background */ /* Duality */ /* Fear */ -@import '../node_modules/@yaireo/tagify/dist/tagify.css'; .daggerheart.sheet.class .editor { height: 500px; } @@ -1297,25 +1296,38 @@ .combat-sidebar .encounter-controls.combat { justify-content: space-between; } -.combat-sidebar .encounter-controls.combat .encounter-control-fear-container { +.combat-sidebar .encounter-controls.combat .encounter-fear-controls { + display: flex; + align-items: center; + gap: 8px; +} +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container { + display: flex; + gap: 2px; +} +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container { display: flex; position: relative; align-items: center; justify-content: center; color: black; } -.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .dice { - height: 24px; +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .dice { + height: 22px; + width: 22px; } -.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-fear { +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .encounter-control-fear { position: absolute; font-size: 16px; } -.combat-sidebar .encounter-controls.combat .encounter-control-fear-container .encounter-control-counter { +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-fear-dice-container .encounter-control-fear-container .encounter-control-counter { position: absolute; right: -10px; color: var(--color-text-secondary); } +.combat-sidebar .encounter-controls.combat .encounter-fear-controls .encounter-countdowns { + color: var(--content-link-icon-color); +} .combat-sidebar .encounter-controls.combat .control-buttons { width: min-content; } @@ -1891,6 +1903,11 @@ div.daggerheart.views.multiclass { .daggerheart.views.levelup .levelup-section .levelup-container .levelup-inner-container .levelup-posttext { padding: 8px 0; } +.daggerheart.views .downtime-container .downtime-header { + margin: 0; + color: light-dark(#18162e, #f3c267); + text-align: center; +} .daggerheart.views .downtime-container .activity-container { display: flex; align-items: center; @@ -1906,12 +1923,30 @@ div.daggerheart.views.multiclass { font-weight: bold; } .daggerheart.views .downtime-container .activity-container .activity-title .activity-image { - width: 120px; + width: 80px; + position: relative; + display: flex; + justify-content: center; + margin-right: 8px; border: 2px solid black; border-radius: 50%; - margin-right: 8px; cursor: pointer; } +.daggerheart.views .downtime-container .activity-container .activity-title .activity-image .activity-select-label { + position: absolute; + top: -9px; + font-size: 14px; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url(../assets/parchments/dh-parchment-light.png); + padding: 0 8px; + line-height: 1; + font-weight: bold; +} +.daggerheart.views .downtime-container .activity-container .activity-title .activity-image img { + border-radius: 50%; +} .daggerheart.views .downtime-container .activity-container .activity-title .activity-image:hover, .daggerheart.views .downtime-container .activity-container .activity-title .activity-image.selected { filter: drop-shadow(0 0 6px gold); @@ -2496,6 +2531,354 @@ div.daggerheart.views.multiclass { .item-button .item-icon.checked { opacity: 1; } +.theme-light .daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor { + background: red; + background-image: url('../assets/parchments/dh-parchment-dark.png'); +} +.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container, +.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .descriptor, +.theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment label { + background-image: url('../assets/parchments/dh-parchment-dark.png'); +} +.daggerheart.dh-style.dialog.character-creation .window-content { + gap: 16px; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav { + flex: 1; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a { + flex: 1; + text-align: center; + display: flex; + justify-content: center; + position: relative; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a.disabled { + opacity: 0.4; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .nav-section-text { + position: relative; + display: flex; + align-items: center; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .finish-marker { + position: absolute; + align-self: center; + top: -8px; + padding: 4px; + border: 1px solid; + border-radius: 50%; + height: 16px; + width: 16px; + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-cool-4); + content: ''; +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .finish-marker.active { + background-color: var(--color-warm-2); +} +.daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor { + position: absolute; + bottom: -8px; + font-size: 12px; + border-radius: 8px; + width: 56px; + text-align: center; + line-height: 1; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url(../assets/parchments/dh-parchment-light.png); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container { + display: flex; + flex-direction: column; + gap: 4px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-container { + width: 140px; + display: flex; + flex-direction: column; + text-align: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-container .card-preview-container { + border-color: light-dark(#18162e, #f3c267); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .selections-outer-container { + display: flex; + justify-content: space-evenly; + height: 210px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container { + border-radius: 8px; + border-color: light-dark(#18162e, #f3c267); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container legend { + margin-left: auto; + margin-right: auto; + font-size: 28px; + font-weight: bold; + padding: 0 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container { + position: relative; + border-radius: 8px; + border-color: light-dark(#18162e, #f3c267); + display: flex; + justify-content: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container legend { + font-size: 20px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container .action-button { + position: absolute; + bottom: -8px; + height: 16px; + width: 110px; + min-height: unset; + border: 1px solid light-dark(#18162e, #f3c267); + color: light-dark(#efe6d8, #efe6d8); + background-color: light-dark(var(--color-warm-3), var(--color-warm-3)); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .section-container .section-inner-container .action-button:hover { + background-color: light-dark(var(--color-warm-2), var(--color-warm-2)); + filter: drop-shadow(0 0 3px light-dark(var(--color-warm-2), var(--color-warm-2))); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container { + text-align: center; + display: flex; + gap: 16px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container { + display: flex; + flex-wrap: wrap; + width: 176px; + gap: 4px; + margin-bottom: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container { + width: 56px; + white-space: nowrap; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url('../assets/parchments/dh-parchment-light.png'); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .traits-inner-container { + display: flex; + justify-content: space-evenly; + gap: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .traits-inner-container .trait-container { + border: 1px solid light-dark(#18162e, #f3c267); + padding: 0 4px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container { + display: flex; + justify-content: space-evenly; + text-align: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container { + position: relative; + display: flex; + align-items: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container .experience-description { + border-color: light-dark(#18162e, #f3c267); + padding-right: 24px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .experiences-inner-container .experience-container .experience-value { + position: absolute; + right: 0; + width: 22px; + border-left: 1px solid light-dark(#18162e, #f3c267); + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer { + display: flex; + align-items: center; + gap: 32px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section { + display: flex; + align-items: center; + gap: 32px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav { + flex: 1; + gap: 8px; + border: 0; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a { + flex: 1; + text-align: center; + display: flex; + justify-content: center; + position: relative; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .nav-section-text { + position: relative; + display: flex; + align-items: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .finish-marker { + position: absolute; + align-self: center; + top: -10px; + padding: 4px; + border: 1px solid; + border-radius: 50%; + height: 20px; + width: 20px; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-cool-4); + content: ''; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .finish-marker.finished { + background-color: var(--color-warm-2); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section nav a .descriptor { + position: absolute; + bottom: -8px; + font-size: 12px; + border-radius: 8px; + width: 56px; + text-align: center; + line-height: 1; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url(../assets/parchments/dh-parchment-light.png); +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .creation-action-footer .footer-section button { + flex: 1; + height: 100%; + white-space: nowrap; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .main-equipment-selection { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 16px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .main-equipment-selection.triple { + grid-template-columns: 1fr 1fr 1fr; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + border: 2px solid light-dark(#18162e, #f3c267); + border-radius: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection legend { + margin-left: auto; + margin-right: auto; + font-size: 28px; + font-weight: bold; + padding: 0 8px; + white-space: nowrap; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .equipment-subsection { + display: flex; + align-items: start; + gap: 32px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .equipment-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container { + display: flex; + flex-direction: column; + justify-content: space-evenly; + gap: 8px; + height: 100%; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment { + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 8px; + position: relative; + display: flex; + justify-content: center; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment.selectable { + cursor: pointer; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment.inactive { + opacity: 0.4; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment label { + position: absolute; + top: -8px; + font-size: 12px; + white-space: nowrap; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url('../assets/parchments/dh-parchment-light.png'); + padding: 0 2px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .simple-equipment-container .simple-equipment img { + width: 60px; + height: 60px; + border-radius: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container { + position: relative; + display: flex; + justify-content: center; + height: min-content; + border: 2px solid light-dark(#18162e, #f3c267); + border-radius: 8px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container legend { + margin-left: auto; + margin-right: auto; + font-size: 12px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container { + position: relative; + display: flex; + justify-content: center; + align-items: center; + padding: 6px; + cursor: grab; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container.taken { + opacity: 0.4; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container label { + position: absolute; + top: -2px; + font-size: 12px; +} +.daggerheart.dh-style.dialog.character-creation .main-selections-container .equipment-selection .suggestion-container .suggestion-inner-container img { + width: 120px; +} +.daggerheart.dh-style.dialog.character-creation .creation-action-footer { + display: flex; + align-items: center; + gap: 32px; +} +.daggerheart.dh-style.dialog.character-creation .creation-action-footer button { + flex: 1; + height: 100%; + white-space: nowrap; +} .theme-light .daggerheart.levelup .tiers-container .tier-container { background-image: url('../assets/parchments/dh-parchment-light.png'); } @@ -2709,6 +3092,27 @@ div.daggerheart.views.multiclass { .daggerheart.levelup .levelup-footer { display: flex; } +.daggerheart.views.ownership-selection .ownership-outer-container { + display: flex; + flex-direction: column; + gap: 8px; +} +.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container { + display: flex; + border: 2px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + padding: 0 4px 0 0; + align-items: center; + gap: 8px; +} +.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container img { + height: 40px; + width: 40px; + border-radius: 6px 0 0 6px; +} +.daggerheart.views.ownership-selection .ownership-outer-container .ownership-container select { + margin: 4px 0; +} :root { --shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; --fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease; @@ -2823,6 +3227,228 @@ div.daggerheart.views.multiclass { #resources:has(.fear-bar) { min-width: 200px; } +.theme-light .daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { + background-image: url('../assets/parchments/dh-parchment-dark.png'); +} +.daggerheart.dh-style.countdown { + overflow: hidden; +} +.daggerheart.dh-style.countdown fieldset { + align-items: center; + margin-top: 5px; + border-radius: 6px; + border-color: light-dark(#18162e, #f3c267); +} +.daggerheart.dh-style.countdown fieldset legend { + font-family: 'Montserrat', sans-serif; + font-weight: bold; + color: light-dark(#18162e, #f3c267); +} +.daggerheart.dh-style.countdown fieldset legend a { + text-shadow: none; +} +.daggerheart.dh-style.countdown.minimized { + height: auto !important; + max-height: unset !important; + max-width: 740px !important; + width: auto !important; +} +.daggerheart.dh-style.countdown.minimized .window-content { + display: flex; + padding: 4px 8px; + justify-content: center; +} +.daggerheart.dh-style.countdown.minimized .minimized-view { + display: flex; + gap: 8px; + flex-wrap: wrap; +} +.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { + width: fit-content; + display: flex; + align-items: center; + gap: 8px; + border: 2px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + padding: 0 4px 0 0; + background-image: url('../assets/parchments/dh-parchment-light.png'); + color: light-dark(#efe6d8, #222); + cursor: pointer; +} +.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container.disabled { + cursor: initial; +} +.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container img { + width: 30px; + height: 30px; + border-radius: 6px 0 0 6px; +} +.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container .mini-countdown-name { + white-space: nowrap; +} +.daggerheart.dh-style.countdown .hidden { + display: none; +} +.daggerheart.dh-style.countdown .window-content > div { + height: 100%; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view { + height: 100%; + display: flex; + flex-direction: column; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-menu { + display: flex; + gap: 8px; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-menu .flex { + flex: 1; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container { + display: flex; + gap: 8px; + flex-wrap: wrap; + overflow: auto; + max-height: 100%; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset { + width: 340px; + height: min-content; + position: relative; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .ownership-button { + position: absolute; + top: 8px; + right: 8px; + font-size: 18px; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container { + display: flex; + align-items: center; + gap: 16px; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container img { + width: 150px; + height: 150px; + cursor: pointer; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container img.disabled { + cursor: initial; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container { + display: flex; + flex-direction: column; + gap: 4px; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container .countdown-value-container { + display: flex; + gap: 4px; +} +.daggerheart.dh-style.countdown .window-content > div .expanded-view .countdowns-container .countdown-fieldset .countdown-container .countdown-inner-container .countdown-value-container input { + max-width: 80px; +} +.daggerheart.dh-style.setting fieldset { + display: flex; + flex-direction: column; + gap: 4px; +} +.daggerheart.dh-style.setting fieldset.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; +} +.daggerheart.dh-style.setting fieldset.two-columns.even { + grid-template-columns: 1fr 1fr; +} +.daggerheart.dh-style.setting .setting-group-field { + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items { + display: flex; + flex-direction: column; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item img { + width: 60px; + border-radius: 8px 0 0 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item i { + font-size: 18px; +} +.daggerheart.dh-style.setting .settings-item-header { + display: flex; + align-items: center; +} +.daggerheart.dh-style.setting .settings-item-header .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; +} +.daggerheart.dh-style.setting .settings-item-header .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:hover[type='text'], +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(#18162e, #f3c267); +} +.daggerheart.dh-style.setting .settings-col { + display: flex; + flex-direction: column; + gap: 4px; +} +.daggerheart.dh-style.setting .trait-array-container { + display: flex; + justify-content: space-evenly; + gap: 8px; + margin-bottom: 16px; +} +.daggerheart.dh-style.setting .trait-array-container .trait-array-item { + position: relative; + display: flex; + justify-content: center; +} +.daggerheart.dh-style.setting .trait-array-container .trait-array-item label { + position: absolute; + top: -7px; + font-size: 12px; + font-variant: petite-caps; +} +.daggerheart.dh-style.setting .trait-array-container .trait-array-item input { + text-align: center; +} .application.sheet.daggerheart.actor.dh-style.adversary .window-content { overflow: auto; } diff --git a/styles/daggerheart.less b/styles/daggerheart.less index 0b0f3926..9094d312 100755 --- a/styles/daggerheart.less +++ b/styles/daggerheart.less @@ -8,9 +8,12 @@ @import './application.less'; @import './sheets/sheets.less'; @import './dialog.less'; +@import './characterCreation.less'; @import './levelup.less'; -@import '../node_modules/@yaireo/tagify/dist/tagify.css'; +@import './ownershipSelection.less'; @import './resources.less'; +@import './countdown.less'; +@import './settings.less'; // new styles imports @import './less/actors/character.less'; diff --git a/styles/ownershipSelection.less b/styles/ownershipSelection.less new file mode 100644 index 00000000..f0b05937 --- /dev/null +++ b/styles/ownershipSelection.less @@ -0,0 +1,26 @@ +.daggerheart.views.ownership-selection { + .ownership-outer-container { + display: flex; + flex-direction: column; + gap: 8px; + + .ownership-container { + display: flex; + border: 2px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + padding: 0 4px 0 0; + align-items: center; + gap: 8px; + + img { + height: 40px; + width: 40px; + border-radius: 6px 0 0 6px; + } + + select { + margin: 4px 0; + } + } + } +} diff --git a/styles/settings.less b/styles/settings.less new file mode 100644 index 00000000..d3e63a2d --- /dev/null +++ b/styles/settings.less @@ -0,0 +1,122 @@ +.daggerheart.dh-style.setting { + fieldset { + display: flex; + flex-direction: column; + gap: 4px; + + &.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; + + &.even { + grid-template-columns: 1fr 1fr; + } + } + } + + .setting-group-field { + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; + } + + .settings-items { + display: flex; + flex-direction: column; + gap: 8px; + + .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; + + .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; + + img { + width: 60px; + border-radius: 8px 0 0 8px; + } + + i { + font-size: 18px; + } + } + } + } + + .settings-item-header { + display: flex; + align-items: center; + + .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; + } + + .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; + + .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } + } + } + } + + .settings-col { + display: flex; + flex-direction: column; + gap: 4px; + } + + .trait-array-container { + display: flex; + justify-content: space-evenly; + gap: 8px; + margin-bottom: 16px; + + .trait-array-item { + position: relative; + display: flex; + justify-content: center; + + label { + position: absolute; + top: -7px; + font-size: 12px; + font-variant: petite-caps; + } + + input { + text-align: center; + } + } + } +} diff --git a/styles/ui.less b/styles/ui.less index c54b0b3b..09a3511f 100644 --- a/styles/ui.less +++ b/styles/ui.less @@ -2,26 +2,42 @@ .encounter-controls.combat { justify-content: space-between; - .encounter-control-fear-container { + .encounter-fear-controls { display: flex; - position: relative; align-items: center; - justify-content: center; - color: black; + gap: 8px; - .dice { - height: 24px; + .encounter-fear-dice-container { + display: flex; + gap: 2px; + + .encounter-control-fear-container { + display: flex; + position: relative; + align-items: center; + justify-content: center; + color: black; + + .dice { + height: 22px; + width: 22px; + } + + .encounter-control-fear { + position: absolute; + font-size: 16px; + } + + .encounter-control-counter { + position: absolute; + right: -10px; + color: var(--color-text-secondary); + } + } } - .encounter-control-fear { - position: absolute; - font-size: 16px; - } - - .encounter-control-counter { - position: absolute; - right: -10px; - color: var(--color-text-secondary); + .encounter-countdowns { + color: var(--content-link-icon-color); } } diff --git a/system.json b/system.json index 56c8c7e7..073315eb 100644 --- a/system.json +++ b/system.json @@ -5,7 +5,7 @@ "version": "0.0.1", "compatibility": { "minimum": "13", - "verified": "13.344", + "verified": "13.345", "maximum": "13" }, "authors": [ diff --git a/templates/chat/downtime.hbs b/templates/chat/downtime.hbs index 2c7c200e..98a7a227 100644 --- a/templates/chat/downtime.hbs +++ b/templates/chat/downtime.hbs @@ -1,16 +1,11 @@

-
{{this.player}} {{localize "DAGGERHEART.Chat.Downtime.Title"}}
-
{{this.title}}
+
{{title}}

- -
{{{this.description}}}
-
-
Refreshed Features
- {{#each this.refreshedFeatures}} -
-
{{this.name}}
-
- {{/each}} -
+ {{#each moves}} + {{this.name}} + +
{{{this.description}}}
+ {{#if (gt this.actions.length 0)}}{{/if}} + {{/each}}
\ No newline at end of file diff --git a/templates/components/card-preview.hbs b/templates/components/card-preview.hbs index ba817371..da2abb77 100644 --- a/templates/components/card-preview.hbs +++ b/templates/components/card-preview.hbs @@ -1,4 +1,7 @@ -
+
{{#if this.img}}
{{this.name}}
diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs index e0a16235..6464f9c9 100644 --- a/templates/settings/appearance-settings.hbs +++ b/templates/settings/appearance-settings.hbs @@ -1,4 +1,6 @@
+ {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} +
{{localize "DAGGERHEART.Settings.Menu.Appearance.duality"}} diff --git a/templates/settings/automation-settings.hbs b/templates/settings/automation-settings.hbs new file mode 100644 index 00000000..8db980c7 --- /dev/null +++ b/templates/settings/automation-settings.hbs @@ -0,0 +1,32 @@ +
+
+ +
+ {{formInput settingFields.schema.fields.hope value=settingFields._source.hope }} +
+
+
+ +
+ {{formInput settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints }} +
+
+
+ +
+ {{formInput settingFields.schema.fields.countdowns value=settingFields._source.countdowns }} +
+
+ +
+ + +
+
+ \ No newline at end of file diff --git a/templates/settings/components/action-view-footer.hbs b/templates/settings/components/action-view-footer.hbs new file mode 100644 index 00000000..309f383f --- /dev/null +++ b/templates/settings/components/action-view-footer.hbs @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/settings/components/action-view-header.hbs b/templates/settings/components/action-view-header.hbs new file mode 100644 index 00000000..a5269faf --- /dev/null +++ b/templates/settings/components/action-view-header.hbs @@ -0,0 +1,6 @@ +
+ +
+

+
+
\ No newline at end of file diff --git a/templates/settings/components/action-view.hbs b/templates/settings/components/action-view.hbs new file mode 100644 index 00000000..6c01b072 --- /dev/null +++ b/templates/settings/components/action-view.hbs @@ -0,0 +1,18 @@ +
+
+ {{localize "Description"}} + + + {{{ enrichedDescription }}} + +
+ {{!--
+ {{localize "Actions"}} + +
+ {{#each this.actions as |action index|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" action type="actions" id=index }} + {{/each}} +
+
--}} +
\ No newline at end of file diff --git a/templates/settings/components/settings-item-line.hbs b/templates/settings/components/settings-item-line.hbs new file mode 100644 index 00000000..039301ec --- /dev/null +++ b/templates/settings/components/settings-item-line.hbs @@ -0,0 +1,22 @@ +
+
+ +
{{this.name}}
+
+ +
\ No newline at end of file diff --git a/templates/settings/homebrew-settings.hbs b/templates/settings/homebrew-settings.hbs new file mode 100644 index 00000000..2e6ddb10 --- /dev/null +++ b/templates/settings/homebrew-settings.hbs @@ -0,0 +1,71 @@ +
+ {{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}} + +

{{localize "DAGGERHEART.Settings.Homebrew.FIELDS.traitArray.label"}}

+
+ {{#each settingFields._source.traitArray as |trait index|}} +
+ + +
+ {{/each}} +
+ +
+ {{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}} + +
+ + {{localize "DAGGERHEART.Downtime.LongRest.title"}} + + + + +
+ +
+ +
+
+ +
+ {{#each settingFields._source.restMoves.longRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="longRest" id=id }} + {{/each}} +
+
+ +
+ + {{localize "DAGGERHEART.Downtime.ShortRest.title"}} + + + + +
+ +
+ +
+
+ +
+ {{#each settingFields._source.restMoves.shortRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="shortRest" id=id }} + {{/each}} +
+
+
+ +
+ + +
+
+ \ No newline at end of file diff --git a/templates/settings/range-measurement-settings.hbs b/templates/settings/range-measurement-settings.hbs new file mode 100644 index 00000000..36357d57 --- /dev/null +++ b/templates/settings/range-measurement-settings.hbs @@ -0,0 +1,22 @@ +
+
+ {{formGroup settingFields.schema.fields.enabled value=settingFields._source.enabled localize=true}} + {{formGroup settingFields.schema.fields.melee value=settingFields._source.melee localize=true}} + {{formGroup settingFields.schema.fields.veryClose value=settingFields._source.veryClose localize=true}} + {{formGroup settingFields.schema.fields.close value=settingFields._source.close localize=true}} + {{formGroup settingFields.schema.fields.far value=settingFields._source.far localize=true}} + {{formGroup settingFields.schema.fields.veryFar value=settingFields._source.veryFar localize=true}} +
+ +
+ + +
+
+ \ No newline at end of file diff --git a/templates/settings/variant-rules.hbs b/templates/settings/variant-rules.hbs index 2c4d7d30..f4fa3fdf 100644 --- a/templates/settings/variant-rules.hbs +++ b/templates/settings/variant-rules.hbs @@ -3,8 +3,8 @@
- {{formInput settingFields.schema.fields.actionTokens.fields.enabled value=settingFields._source.actionTokens.enabled}} - {{formInput settingFields.schema.fields.actionTokens.fields.tokens value=settingFields._source.actionTokens.tokens disabled=(not settingFields._source.actionTokens.enabled)}} + {{formGroup settingFields.schema.fields.actionTokens.fields.enabled value=settingFields._source.actionTokens.enabled localize=true}} + {{formGroup settingFields.schema.fields.actionTokens.fields.tokens value=settingFields._source.actionTokens.tokens disabled=(not settingFields._source.actionTokens.enabled) localize=true}}
diff --git a/templates/sheets/items/armor/settings.hbs b/templates/sheets/items/armor/settings.hbs index 5f3d749b..6a2341d9 100644 --- a/templates/sheets/items/armor/settings.hbs +++ b/templates/sheets/items/armor/settings.hbs @@ -3,7 +3,6 @@ data-tab='{{tabs.settings.id}}' data-group='{{tabs.settings.group}}' > -
{{localize tabs.settings.label}} {{localize "DAGGERHEART.Tiers.singular"}} diff --git a/templates/ui/combat/combatTrackerHeader.hbs b/templates/ui/combat/combatTrackerHeader.hbs index a2f5d557..3d36f571 100644 --- a/templates/ui/combat/combatTrackerHeader.hbs +++ b/templates/ui/combat/combatTrackerHeader.hbs @@ -52,10 +52,15 @@
{{#if hasCombat}} -
- - -
{{fear}}
+
+
+
+ + +
+
{{fear}}
+
+
{{/if}} diff --git a/templates/views/automation-settings.hbs b/templates/views/automation-settings.hbs deleted file mode 100644 index de4f1131..00000000 --- a/templates/views/automation-settings.hbs +++ /dev/null @@ -1,19 +0,0 @@ -
-
- -
- -
-
-
- -
- -
-
- -
- - -
-
\ No newline at end of file diff --git a/templates/views/characterCreation/footer.hbs b/templates/views/characterCreation/footer.hbs new file mode 100644 index 00000000..3cc3aa5e --- /dev/null +++ b/templates/views/characterCreation/footer.hbs @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/templates/views/characterCreation/tabs.hbs b/templates/views/characterCreation/tabs.hbs new file mode 100644 index 00000000..44065d4e --- /dev/null +++ b/templates/views/characterCreation/tabs.hbs @@ -0,0 +1,13 @@ +
+ + + +
\ No newline at end of file diff --git a/templates/views/characterCreation/tabs/equipment.hbs b/templates/views/characterCreation/tabs/equipment.hbs new file mode 100644 index 00000000..75f2631e --- /dev/null +++ b/templates/views/characterCreation/tabs/equipment.hbs @@ -0,0 +1,107 @@ +
+
+
+
+ {{localize "DAGGERHEART.CharacterCreation.SuggestedArmor"}} + +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" armor }} + {{localize "DAGGERHEART.CharacterCreation.SelectArmor"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{#if armor.suggestion}} +
+ {{armor.suggestion.name}} +
+ +
+
+ {{/if}} +
+ +
+ {{localize "DAGGERHEART.CharacterCreation.SuggestedWeapons"}} + +
+
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" primaryWeapon }} + {{localize "DAGGERHEART.CharacterCreation.SelectPrimaryWeapon"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{#if primaryWeapon.suggestion}} +
+ {{primaryWeapon.suggestion.name}} +
+ +
+
+ {{/if}} +
+
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" secondaryWeapon }} + {{localize "DAGGERHEART.CharacterCreation.SelectSecondaryWeapon"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{#if secondaryWeapon.suggestion}} +
+ {{secondaryWeapon.suggestion.name}} +
+ +
+
+ {{/if}} +
+
+
+
+
+
+ {{localize "DAGGERHEART.CharacterCreation.StartingItems"}} + +
+ {{#each inventory.take}} +
+ + +
+ {{/each}} +
+
+ +
+ {{localize "DAGGERHEART.CharacterCreation.Choice"}} + +
+ {{#each inventory.choiceA.suggestions}} +
+ + +
+ {{/each}} +
+
+ +
+ {{localize "DAGGERHEART.CharacterCreation.Choice"}} + +
+ {{#each inventory.choiceB.suggestions}} +
+ + +
+ {{/each}} +
+
+
+
+
\ No newline at end of file diff --git a/templates/views/characterCreation/tabs/setup.hbs b/templates/views/characterCreation/tabs/setup.hbs new file mode 100644 index 00000000..4d6e638c --- /dev/null +++ b/templates/views/characterCreation/tabs/setup.hbs @@ -0,0 +1,101 @@ +
+
+
+ {{localize "TYPES.Item.class"}} +
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" class }} + {{localize "DAGGERHEART.CharacterCreation.SelectClass"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" subclass disabled=(not class.img) }} + {{localize "DAGGERHEART.CharacterCreation.SelectSubclass"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+
+
+ + {{#if (gte visibility 2)}} +
+ {{localize "DAGGERHEART.CharacterCreation.Heritage"}} +
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" ancestry }} + {{localize "DAGGERHEART.CharacterCreation.SelectAncestry"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" community }} + {{localize "DAGGERHEART.CharacterCreation.SelectCommunity"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+
+
+ {{/if}} + + {{#if (gte visibility 3)}} +
+ {{localize "DAGGERHEART.CharacterCreation.TraitIncreases"}} {{traits.nrSelected}}/{{traits.nrTotal}} +
+
+ {{localize "DAGGERHEART.CharacterCreation.SuggestedTraits"}} +
+ {{#each suggestedTraits}} +
{{this}}
+ {{/each}} +
+ +
+
+ {{#each traits.values}} +
+
{{this.name}}
+ +
+ {{/each}} +
+
+
+ {{/if}} + + {{#if (gte visibility 4)}} +
+ {{localize "DAGGERHEART.CharacterCreation.InitialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}} +
+ {{#each experience.values as |experience id|}} +
+ +
{{signedNumber this.value}}
+
+ {{/each}} +
+
+ {{/if}} + + {{#if (gte visibility 5)}} +
+ {{localize "TYPES.Item.domainCard"}} +
+ {{#each domainCards as |domainCard id|}} +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" domainCard }} + {{#each @root.class.system.domains }} +
{{localize (concat "DAGGERHEART.Domains." this ".label")}}
+ {{/each}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{/each}} +
+
+ {{/if}} +
+
\ No newline at end of file diff --git a/templates/views/characterCreation/tabs/story.hbs b/templates/views/characterCreation/tabs/story.hbs new file mode 100644 index 00000000..c2fb7400 --- /dev/null +++ b/templates/views/characterCreation/tabs/story.hbs @@ -0,0 +1,9 @@ +
+
+ Story +
+
\ No newline at end of file diff --git a/templates/views/countdowns.hbs b/templates/views/countdowns.hbs new file mode 100644 index 00000000..293b84b9 --- /dev/null +++ b/templates/views/countdowns.hbs @@ -0,0 +1,42 @@ +
+
+
+ {{#if canCreate}}{{/if}} + {{#if isGM}}{{/if}} +
+ +
+ {{#each source.countdowns}} +
+ + {{this.name}} + {{#if this.canEdit}}{{/if}} + {{#if @root.isGM}}{{/if}} + + + +
+ +
+ {{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}} +
+ {{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}} + {{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}} +
+ {{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}} +
+
+
+ {{/each}} +
+
+ +
\ No newline at end of file diff --git a/templates/views/downtime.hbs b/templates/views/downtime.hbs index 1623b1c7..486e565a 100644 --- a/templates/views/downtime.hbs +++ b/templates/views/downtime.hbs @@ -1,9 +1,14 @@
- {{#each this.options as |option key|}} +

{{localize "DAGGERHEART.Downtime.DowntimeHeader" current=nrCurrentChoices max=moveData.nrChoices}}

+ {{#each moveData.moves as |move key|}}
- +
+ {{#if this.selected}}
{{move.selected}}
{{/if}} + +
+ {{localize this.name}}
@@ -11,18 +16,9 @@
{{/each}} -
-
- - -
-
- -
-
- - + +
\ No newline at end of file diff --git a/templates/views/homebrew-settings.hbs b/templates/views/homebrew-settings.hbs deleted file mode 100644 index 6d9c9a90..00000000 --- a/templates/views/homebrew-settings.hbs +++ /dev/null @@ -1,13 +0,0 @@ -
-
- -
- -
-
- -
- - -
-
\ No newline at end of file diff --git a/templates/views/ownershipSelection.hbs b/templates/views/ownershipSelection.hbs new file mode 100644 index 00000000..d049a981 --- /dev/null +++ b/templates/views/ownershipSelection.hbs @@ -0,0 +1,22 @@ +
+
+
+ + +
+
+ {{#each ownership.players as |player id|}} +
+ +
{{player.name}}
+ +
+ {{/each}} +
+ +
+
\ No newline at end of file diff --git a/templates/views/range-settings.hbs b/templates/views/range-settings.hbs deleted file mode 100644 index fec4f7fb..00000000 --- a/templates/views/range-settings.hbs +++ /dev/null @@ -1,44 +0,0 @@ -
-
- -
- - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- - -
\ No newline at end of file