| 1 | import { MacroRegistry, MacroCategory, MacroValueType } from '../engine/MacroRegistry.js'; |
| 2 | import { isMobile } from '../../RossAscends-mods.js'; |
| 3 | import { parseMesExamples, main_api } from '../../../script.js'; |
| 4 | import { power_user } from '../../power-user.js'; |
| 5 | import { formatInstructModeExamples } from '../../instruct-mode.js'; |
| 6 | |
| 7 | /** @typedef {import('../engine/MacroEnv.types.js').MacroEnv} MacroEnv */ |
| 8 | |
| 9 | /** |
| 10 | * Registers macros that mostly act as simple accessors to MacroEnv fields |
| 11 | * (names, character card fields, system metadata, extras) or basic |
| 12 | * environment flags. |
| 13 | */ |
| 14 | export function registerEnvMacros() { |
| 15 | // Names and participant macros (from MacroEnv.names) |
| 16 | MacroRegistry.registerMacro('user', { |
| 17 | category: MacroCategory.NAMES, |
| 18 | description: 'Your current Persona username.', |
| 19 | returns: 'Persona username.', |
| 20 | handler: ({ env }) => env.names.user, |
| 21 | }); |
| 22 | |
| 23 | MacroRegistry.registerMacro('char', { |
| 24 | category: MacroCategory.NAMES, |
| 25 | description: 'The character\'s name.', |
| 26 | returns: 'Character name.', |
| 27 | handler: ({ env }) => env.names.char, |
| 28 | }); |
| 29 | |
| 30 | MacroRegistry.registerMacro('group', { |
| 31 | aliases: [{ alias: 'charIfNotGroup', visible: false }], |
| 32 | category: MacroCategory.NAMES, |
| 33 | description: 'Comma-separated list of group member names (including muted) or the character name in solo chats.', |
| 34 | returns: 'List of group member names.', |
| 35 | handler: ({ env }) => env.names.group ?? '', |
| 36 | }); |
| 37 | |
| 38 | MacroRegistry.registerMacro('groupNotMuted', { |
| 39 | category: MacroCategory.NAMES, |
| 40 | description: 'Comma-separated list of group member names excluding muted members.', |
| 41 | returns: 'List of group member names excluding muted members.', |
| 42 | handler: ({ env }) => env.names.groupNotMuted ?? '', |
| 43 | }); |
| 44 | |
| 45 | MacroRegistry.registerMacro('notChar', { |
| 46 | category: MacroCategory.NAMES, |
| 47 | description: 'Comma-separated list of all participants except the current speaker.', |
| 48 | returns: 'List of all participants except the current speaker.', |
| 49 | handler: ({ env }) => env.names.notChar ?? '', |
| 50 | }); |
| 51 | |
| 52 | // Character card field macros (from MacroEnv.character) |
| 53 | MacroRegistry.registerMacro('charPrompt', { |
| 54 | category: MacroCategory.CHARACTER, |
| 55 | description: 'The character\'s Main Prompt override.', |
| 56 | returns: 'Character Main Prompt override.', |
| 57 | handler: ({ env }) => env.character.charPrompt ?? '', |
| 58 | }); |
| 59 | |
| 60 | MacroRegistry.registerMacro('charInstruction', { |
| 61 | category: MacroCategory.CHARACTER, |
| 62 | description: 'The character\'s Post-History Instructions override.', |
| 63 | returns: 'Character Post-History Instructions override.', |
| 64 | handler: ({ env }) => env.character.charInstruction ?? '', |
| 65 | }); |
| 66 | |
| 67 | MacroRegistry.registerMacro('charDescription', { |
| 68 | aliases: [{ alias: 'description' }], |
| 69 | category: MacroCategory.CHARACTER, |
| 70 | description: 'The character\'s description.', |
| 71 | returns: 'Character description.', |
| 72 | handler: ({ env }) => env.character.description ?? '', |
| 73 | }); |
| 74 | |
| 75 | MacroRegistry.registerMacro('charPersonality', { |
| 76 | aliases: [{ alias: 'personality' }], |
| 77 | category: MacroCategory.CHARACTER, |
| 78 | description: 'The character\'s personality.', |
| 79 | returns: 'Character personality.', |
| 80 | handler: ({ env }) => env.character.personality ?? '', |
| 81 | }); |
| 82 | |
| 83 | MacroRegistry.registerMacro('charScenario', { |
| 84 | aliases: [{ alias: 'scenario' }], |
| 85 | category: MacroCategory.CHARACTER, |
| 86 | description: 'The character\'s scenario.', |
| 87 | returns: 'Character scenario.', |
| 88 | handler: ({ env }) => env.character.scenario ?? '', |
| 89 | }); |
| 90 | |
| 91 | MacroRegistry.registerMacro('persona', { |
| 92 | category: MacroCategory.CHARACTER, |
| 93 | description: 'Your current Persona description.', |
| 94 | returns: 'Persona description.', |
| 95 | handler: ({ env }) => env.character.persona ?? '', |
| 96 | }); |
| 97 | |
| 98 | MacroRegistry.registerMacro('mesExamplesRaw', { |
| 99 | category: MacroCategory.CHARACTER, |
| 100 | description: 'Unformatted dialogue examples from the character card.', |
| 101 | returns: 'Unformatted dialogue examples.', |
| 102 | handler: ({ env }) => env.character.mesExamplesRaw ?? '', |
| 103 | }); |
| 104 | |
| 105 | MacroRegistry.registerMacro('mesExamples', { |
| 106 | category: MacroCategory.CHARACTER, |
| 107 | description: 'The character\'s dialogue examples, formatted for instruct mode when enabled.', |
| 108 | returns: 'Formatted dialogue examples.', |
| 109 | handler: ({ env }) => { |
| 110 | const raw = env.character.mesExamplesRaw ?? ''; |
| 111 | if (!raw) return ''; |
| 112 | |
| 113 | const isInstruct = !!power_user?.instruct?.enabled && main_api !== 'openai'; |
| 114 | const parsed = parseMesExamples(raw, isInstruct); |
| 115 | |
| 116 | if (!Array.isArray(parsed) || parsed.length === 0) { |
| 117 | return ''; |
| 118 | } |
| 119 | if (!isInstruct) { |
| 120 | return parsed.join(''); |
| 121 | } |
| 122 | |
| 123 | const formatted = formatInstructModeExamples(parsed, env.names.user, env.names.char); |
| 124 | return Array.isArray(formatted) ? formatted.join('') : ''; |
| 125 | }, |
| 126 | }); |
| 127 | |
| 128 | MacroRegistry.registerMacro('charDepthPrompt', { |
| 129 | category: MacroCategory.CHARACTER, |
| 130 | description: 'The character\'s @ Depth Note.', |
| 131 | returns: 'Character @ Depth Note.', |
| 132 | handler: ({ env }) => env.character.charDepthPrompt ?? '', |
| 133 | }); |
| 134 | |
| 135 | MacroRegistry.registerMacro('charCreatorNotes', { |
| 136 | aliases: [{ alias: 'creatorNotes' }], |
| 137 | category: MacroCategory.CHARACTER, |
| 138 | description: 'Creator notes from the character card.', |
| 139 | returns: 'Creator notes.', |
| 140 | handler: ({ env }) => env.character.creatorNotes ?? '', |
| 141 | }); |
| 142 | |
| 143 | MacroRegistry.registerMacro('charFirstMessage', { |
| 144 | aliases: [{ alias: 'greeting' }], |
| 145 | category: MacroCategory.CHARACTER, |
| 146 | unnamedArgs: [ |
| 147 | { |
| 148 | name: 'index', |
| 149 | optional: true, |
| 150 | defaultValue: '0', |
| 151 | type: MacroValueType.INTEGER, |
| 152 | description: '0-based index. 0 (default) returns the main greeting, 1 and up return alternate greetings.', |
| 153 | }, |
| 154 | ], |
| 155 | description: 'The character\'s first message / greeting. Optionally specify an index to access alternate greetings.', |
| 156 | returns: 'Character greeting at the given index, or empty string if out of bounds.', |
| 157 | exampleUsage: ['{{greeting}}', '{{greeting::0}}', '{{greeting::1}}'], |
| 158 | handler: ({ env, unnamedArgs: [index] }) => { |
| 159 | const i = Number(index ?? 0); |
| 160 | if (i === 0) return env.character.firstMessage ?? ''; |
| 161 | const altGreetings = env.character.alternateGreetings; |
| 162 | if (!Array.isArray(altGreetings)) return ''; |
| 163 | return altGreetings[i - 1] ?? ''; |
| 164 | }, |
| 165 | }); |
| 166 | |
| 167 | // Character version macros (legacy variants and documented {{charVersion}}) |
| 168 | MacroRegistry.registerMacro('charVersion', { |
| 169 | aliases: [ |
| 170 | { alias: 'version', visible: false }, // Legacy alias |
| 171 | { alias: 'char_version', visible: false }, // Legacy underscore variant |
| 172 | ], |
| 173 | category: MacroCategory.CHARACTER, |
| 174 | description: 'The character\'s version number.', |
| 175 | returns: 'Character version number.', |
| 176 | handler: ({ env }) => env.character.version ?? '', |
| 177 | }); |
| 178 | |
| 179 | // System / env extras macros (from MacroEnv.system / MacroEnv.extra) |
| 180 | MacroRegistry.registerMacro('model', { |
| 181 | category: MacroCategory.STATE, |
| 182 | description: 'Model name for the currently selected API (Chat Completion or Chat Completion).', |
| 183 | returns: 'Model name.', |
| 184 | handler: ({ env }) => env.system.model, |
| 185 | }); |
| 186 | |
| 187 | MacroRegistry.registerMacro('original', { |
| 188 | category: MacroCategory.CHARACTER, |
| 189 | description: 'Original message content for {{original}} substitution in in character prompt overrides.', |
| 190 | returns: 'Original message content.', |
| 191 | handler: ({ env }) => { |
| 192 | const value = env.functions.original(); |
| 193 | return value; |
| 194 | }, |
| 195 | }); |
| 196 | |
| 197 | // Device / environment macros |
| 198 | MacroRegistry.registerMacro('isMobile', { |
| 199 | category: MacroCategory.STATE, |
| 200 | description: '"true" if currently running in a mobile environment, "false" otherwise.', |
| 201 | returns: 'Whether the environment is mobile.', |
| 202 | returnType: MacroValueType.BOOLEAN, |
| 203 | handler: () => String(isMobile()), |
| 204 | }); |
| 205 | } |