intelligent-system-design-language 0.3.21 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.claude/agents/langium-language-designer.md +38 -38
  2. package/.claude/agents/typescript-vscode-expert.md +29 -29
  3. package/.claude/agents/ui-ux-designer.md +36 -36
  4. package/.claude/settings.local.json +33 -33
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -6
  6. package/.idea/isdl.iml +13 -13
  7. package/.idea/modules.xml +8 -8
  8. package/.idea/vcs.xml +6 -6
  9. package/.idea/watcherTasks.xml +3 -3
  10. package/.vscodeignore +18 -18
  11. package/LICENSE +673 -673
  12. package/README.md +86 -86
  13. package/bin/cli.js +4 -4
  14. package/bin/lsp.js +8 -8
  15. package/out/_backgrounds.scss +91 -91
  16. package/out/_handlebars.scss +497 -497
  17. package/out/_isdlStyles.scss +1444 -1381
  18. package/out/_vuetifyOverrides.scss +425 -425
  19. package/out/_vuetifyStyles.scss +31957 -31957
  20. package/out/cli/components/_backgrounds.scss +91 -91
  21. package/out/cli/components/_handlebars.scss +497 -497
  22. package/out/cli/components/_isdlStyles.scss +1444 -1381
  23. package/out/cli/components/_vuetifyOverrides.scss +425 -425
  24. package/out/cli/components/_vuetifyStyles.scss +31957 -31957
  25. package/out/cli/components/active-effect-sheet-generator.js +453 -453
  26. package/out/cli/components/chat-card-generator.js +654 -651
  27. package/out/cli/components/chat-card-generator.js.map +1 -1
  28. package/out/cli/components/css-generator.js +4 -4
  29. package/out/cli/components/damage-roll-generator.js +160 -160
  30. package/out/cli/components/datamodel-generator.js +264 -257
  31. package/out/cli/components/datamodel-generator.js.map +1 -1
  32. package/out/cli/components/derived-data-generator.js +923 -923
  33. package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
  34. package/out/cli/components/init-hook-generator.js +495 -495
  35. package/out/cli/components/language-generator.js +1 -1
  36. package/out/cli/components/language-generator.js.map +1 -1
  37. package/out/cli/components/measured-template-preview.js +221 -221
  38. package/out/cli/components/method-generator.js +979 -887
  39. package/out/cli/components/method-generator.js.map +1 -1
  40. package/out/cli/components/ready-hook-generator.js +404 -404
  41. package/out/cli/components/token-generator.js +116 -116
  42. package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
  43. package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
  44. package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
  45. package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
  46. package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
  47. package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
  48. package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
  49. package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
  50. package/out/cli/components/vue/base-components/vue-dice.js +98 -98
  51. package/out/cli/components/vue/base-components/vue-die.js +73 -73
  52. package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
  53. package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
  54. package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
  55. package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
  56. package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
  57. package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
  58. package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
  59. package/out/cli/components/vue/base-components/vue-money.js +483 -483
  60. package/out/cli/components/vue/base-components/vue-number.js +174 -174
  61. package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
  62. package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
  63. package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
  64. package/out/cli/components/vue/base-components/vue-resource.js +136 -136
  65. package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
  66. package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
  67. package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
  68. package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
  69. package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
  70. package/out/cli/components/vue/base-components/vue-string.js +60 -60
  71. package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
  72. package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
  73. package/out/cli/components/vue/vue-action-component-generator.js +64 -64
  74. package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
  75. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
  76. package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
  77. package/out/cli/components/vue/vue-document-creation-app.js +121 -121
  78. package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
  79. package/out/cli/components/vue/vue-generator.js +40 -40
  80. package/out/cli/components/vue/vue-mixin.js +296 -296
  81. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
  82. package/out/cli/components/vue/vue-prompt-generator.js +91 -76
  83. package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
  84. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
  85. package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
  86. package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
  87. package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
  88. package/out/cli/generator.js +438 -433
  89. package/out/cli/generator.js.map +1 -1
  90. package/out/extension/github/githubAuthProvider.js +71 -29
  91. package/out/extension/github/githubAuthProvider.js.map +1 -1
  92. package/out/extension/github/githubGistManager.js +4 -3
  93. package/out/extension/github/githubGistManager.js.map +1 -1
  94. package/out/extension/github/githubManager.js +40 -38
  95. package/out/extension/github/githubManager.js.map +1 -1
  96. package/out/extension/github/githubQuickActions.js +120 -120
  97. package/out/extension/github/system-workflow.yml +47 -47
  98. package/out/extension/main.cjs +909 -532
  99. package/out/extension/main.cjs.map +3 -3
  100. package/out/extension/package.json +419 -419
  101. package/out/language/generated/ast.js +51 -2
  102. package/out/language/generated/ast.js.map +1 -1
  103. package/out/language/generated/grammar.js +14240 -13991
  104. package/out/language/generated/grammar.js.map +1 -1
  105. package/out/language/intelligent-system-design-language-validator.js +32 -2
  106. package/out/language/intelligent-system-design-language-validator.js.map +1 -1
  107. package/out/language/isdl-scope-provider.js +14 -1
  108. package/out/language/isdl-scope-provider.js.map +1 -1
  109. package/out/language/main.cjs +913 -569
  110. package/out/language/main.cjs.map +3 -3
  111. package/out/package.json +419 -419
  112. package/out/progressbar.min.js +6 -6
  113. package/out/styles.scss +762 -747
  114. package/out/test/validating/diagnostics.test.js +40 -0
  115. package/out/test/validating/diagnostics.test.js.map +1 -1
  116. package/package.json +419 -419
@@ -20,32 +20,32 @@ export function generateBaseActiveEffectBaseSheet(entry, id, destination) {
20
20
  if (property.modifier == "locked")
21
21
  return;
22
22
  if (isResourceExp(property)) {
23
- return expandToNode `
24
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value", 1);
25
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
23
+ return expandToNode `
24
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value", 1);
25
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
26
26
  `;
27
27
  }
28
28
  if (isTrackerExp(property)) {
29
- return expandToNode `
30
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min");
31
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value", 1);
32
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp", 1);
33
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
29
+ return expandToNode `
30
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min");
31
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value", 1);
32
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp", 1);
33
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
34
34
  `;
35
35
  }
36
36
  if (isAttributeExp(property)) {
37
- return expandToNode `
38
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value");
39
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
37
+ return expandToNode `
38
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value");
39
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max");
40
40
  `;
41
41
  }
42
42
  if (isNumberExp(property)) {
43
- return expandToNode `
44
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}", 1);
43
+ return expandToNode `
44
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${property.name.toLowerCase()}", 1);
45
45
  `;
46
46
  }
47
- return expandToNode `
48
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.${getSystemPath(property)}");
47
+ return expandToNode `
48
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.${getSystemPath(property)}");
49
49
  `;
50
50
  }
51
51
  function generateDamageTypeActiveEffectFields(entry, document) {
@@ -57,16 +57,16 @@ export function generateBaseActiveEffectBaseSheet(entry, id, destination) {
57
57
  }
58
58
  const damageTypeFields = damageTypes.map(damageType => {
59
59
  const safeTypeName = damageType.toLowerCase().replace(/[^a-z0-9]/g, '');
60
- return expandToNode `
61
- // ${damageType} damage type Active Effect fields
62
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}bonusdamage");
63
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}damageresistanceflat");
64
- addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}damageresistancepercent");
60
+ return expandToNode `
61
+ // ${damageType} damage type Active Effect fields
62
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}bonusdamage");
63
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}damageresistanceflat");
64
+ addChange("${document.name.toLowerCase()}", "${document.name.toLowerCase()}.system.${safeTypeName}damageresistancepercent");
65
65
  `;
66
66
  });
67
- return expandToNode `
68
- // Auto-generated damage type Active Effect fields
69
- ${joinToNode(damageTypeFields, field => field, { appendNewLineIfNotEmpty: true })}
67
+ return expandToNode `
68
+ // Auto-generated damage type Active Effect fields
69
+ ${joinToNode(damageTypeFields, field => field, { appendNewLineIfNotEmpty: true })}
70
70
  `;
71
71
  }
72
72
  // Collect all damage types from the entry (same function as in datamodel-generator)
@@ -120,297 +120,297 @@ export function generateBaseActiveEffectBaseSheet(entry, id, destination) {
120
120
  // };
121
121
  // `;
122
122
  // }
123
- const fileNode = expandToNode `
124
- import VueRenderingMixin from './VueRenderingMixin.mjs';
125
- import { ActiveEffectApp } from "./components/components.vue.es.mjs";
126
- const { DOCUMENT_OWNERSHIP_LEVELS } = CONST;
127
- export default class ${entry.config.name}EffectVueSheet extends VueRenderingMixin(foundry.applications.api.DocumentSheetV2) {
128
-
129
- vueParts = {
130
- "active-effect": {
131
- component: ActiveEffectApp,
132
- template: "<active-effect :context=\\"context\\">Vue rendering for sheet failed.</active-effect>"
133
- }
134
- };
135
-
136
- _arrayEntryKey = 0;
137
- _renderKey = 0;
138
-
139
-
140
- /** @override */
141
- static DEFAULT_OPTIONS = {
142
- classes: ["${id}", "sheet", "vue-sheet", "active-effect", "active-effect-sheet"],
143
- viewPermission: DOCUMENT_OWNERSHIP_LEVELS.LIMITED,
144
- editPermission: DOCUMENT_OWNERSHIP_LEVELS.OWNER,
145
- position: {
146
- width: 600,
147
- height: 600,
148
- },
149
- window: {
150
- resizable: true,
151
- title: "Active Effect"
152
- },
153
- tag: "form",
154
- actions: {
155
- onEditImage: this._onEditImage
156
- },
157
- changeActions: {
158
- },
159
- // Custom property that's merged into this.options
160
- dragDrop: [
161
- ],
162
- form: {
163
- handler: ${entry.config.name}EffectVueSheet._onSubmitForm,
164
- submitOnChange: false,
165
- submitOnClose: true,
166
- closeOnSubmit: false
167
- }
168
- };
169
-
170
- /* -------------------------------------------- */
171
-
172
- static async _onEditImage(event, target) {
173
- const current = foundry.utils.getProperty(this.document, target.dataset.edit);
174
- const fp = new FilePicker({
175
- current,
176
- type: "image",
177
- callback: (path) => {
178
- this.document.update({ [target.dataset.edit]: path });
179
- },
180
- top: this.position.top + 40,
181
- left: this.position.left + 10
182
- });
183
- return fp.browse();
184
- }
185
-
186
- /* -------------------------------------------- */
187
-
188
- async _prepareContext(options) {
189
- const context = await super._prepareContext(options);
190
- this.object = this.document.toObject();
191
- context.effect = this.object;
192
- context.descriptionHTML = await TextEditor.enrichHTML(this.object.description, {secrets: this.object.isOwner});
193
-
194
- // Status Conditions
195
- const statuses = CONFIG.statusEffects.map(s => {
196
- return {
197
- id: s.id,
198
- label: game.i18n.localize(s.name),
199
- selected: context.effect.statuses.includes(s.id) ? "selected" : ""
200
- };
201
- });
202
- context.statuses = statuses;
203
- if ( context.effect.origin ) {
204
- context.originLink = await TextEditor.enrichHTML("@UUID[" + context.effect.origin + "]");
205
- }
206
-
207
- function setValue(obj, access, value, mode) {
208
- if ( typeof(access)=='string' ) {
209
- access = access.split('.');
210
- }
211
- // Split up an access path into sub-objects, such as "system.attribute.value" => "system": {"attribute": {"value": ...}}
212
- if ( access.length > 1 ) {
213
- const key = access.shift();
214
- if ( !obj[key] ) obj[key] = {};
215
- setValue(obj[key], access, value, mode);
216
- }
217
- else {
218
- obj[access[0]] = value;
219
- obj[access[0] + "-mode"] = mode;
220
- }
221
- }
222
-
223
- // Turn the changes into the friendlier format
224
- for ( const change of context.effect.changes ) {
225
- setValue(context, change.key, change.value, change.mode);
226
- }
227
-
228
- // Output initialization
229
- const vueContext = {
230
- // Validates both permissions and compendium status
231
- editable: this.isEditable,
232
- owner: this.document.isOwner,
233
- limited: this.document.limited,
234
-
235
- // Add the document.
236
- object: context.effect,
237
- document: this.document,
238
-
239
- // Add the data to context.data for easier access, as well as flags.
240
- system: this.document.system,
241
- flags: this.document.flags,
242
-
243
- // Editors
244
- editors: {},
245
-
246
- // Force re-renders. Defined in the vue mixin.
247
- _renderKey: this._renderKey ?? 0,
248
- _arrayEntryKey: this._arrayEntryKey ?? 0,
249
- isItemEffect: this.document.parent.documentName === "Item",
250
- originLink: context.originLink
251
- // tabs: this._getTabs(options.parts),
252
- };
253
-
254
- await this._enrichEditor(vueContext, "description");
255
-
256
- const numberTypes = ['multiply', 'add', 'downgrade', 'upgrade', 'override'];
257
- const stringTypes = ['override'];
258
- const booleanTypes = ['override'];
259
-
260
- vueContext.numberModes = numberTypes.map(type => ({
261
- value: type,
262
- label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
263
- }));
264
- vueContext.numberModes.push({
265
- value: 'custom',
266
- label: game.i18n.localize("EFFECTS.AddOnce")
267
- });
268
- vueContext.basicNumberModes = numberTypes.map(type => ({
269
- value: type,
270
- label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
271
- }));
272
- vueContext.stringModes = stringTypes.map(type => ({
273
- value: type,
274
- label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
275
- }));
276
- vueContext.booleanModes = booleanTypes.map(type => ({
277
- value: type,
278
- label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
279
- }));
280
- vueContext.resourceModes = [
281
- { value: 'custom', label: game.i18n.localize("EFFECTS.AddOnce") }
282
- ];
283
- vueContext.trackerModes = [
284
- { value: 'custom', label: game.i18n.localize("EFFECTS.AddOnce") }
285
- ];
286
-
287
- // Copy current change values into vueContext so the Vue component can read them
288
- for ( const change of context.effect.changes ) {
289
- setValue(vueContext, change.key, change.value, change.mode);
290
- }
291
-
292
- console.dir("Vue Active Effect Context", vueContext);
293
- return vueContext;
294
- }
295
-
296
-
297
- async _enrichEditor(context, field) {
298
- const enrichmentOptions = {
299
- // Whether to show secret blocks in the finished html
300
- secrets: this.document.isOwner,
301
- // Data to fill in for inline rolls
302
- rollData: {},
303
- // Relative UUID resolution
304
- relativeTo: this.document
305
- };
306
-
307
- const editorOptions = {
308
- toggled: true,
309
- collaborate: true,
310
- documentUUID: this.document.uuid,
311
- height: 300
312
- };
313
-
314
- const editorValue = this.document.system?.[field] ?? foundry.utils.getProperty(this.document.system, field);
315
- context.editors[\`\${field}\`] = {
316
- enriched: await TextEditor.enrichHTML(editorValue, enrichmentOptions),
317
- element: foundry.applications.elements.HTMLProseMirrorElement.create({
318
- ...editorOptions,
319
- name: \`system.\${field}\`,
320
- value: editorValue ?? ""
321
- })
322
- };
323
- }
324
-
325
- /* -------------------------------------------- */
326
-
327
- _prepareSubmitData(event, form, formData) {
328
- // We don't modify the image via the sheet itself, so we can remove it from the submit data to avoid errors.
329
- delete formData.object.img;
330
- return super._prepareSubmitData(event, form, formData);
331
- }
332
-
333
- /* -------------------------------------------- */
334
-
335
- /**
336
- * Process form submission for the sheet
337
- * @this {${entry.config.name}EffectVueSheet} The handler is called with the application as its bound scope
338
- * @param {SubmitEvent} event The originating form submission event
339
- * @param {HTMLFormElement} form The form element that was submitted
340
- * @param {FormDataExtended} formData Processed data for the submitted form
341
- * @returns {Promise<void>}
342
- */
343
- static async _onSubmitForm(event, form, formData) {
344
- console.log("Updating Active Effect", formData);
345
- const flags = foundry.utils.duplicate(this.document.flags);
346
- if ( !flags["${id}"] ) {
347
- flags["${id}"] = {};
348
- }
349
-
350
- // Retrieve the existing effects.
351
- let changes = this.document.changes ? [...this.document.changes] : [];
352
-
353
- // Build an array of effects from the form data
354
- let newChanges = [];
355
-
356
- function addChange(documentName, key, customMode) {
357
- const value = foundry.utils.getProperty(formData.object, key);
358
- if ( value === undefined ) {
359
- // Field not rendered in the form - preserve any existing change.
360
- return;
361
- }
362
- if ( !value ) {
363
- // Field explicitly cleared (empty string / zero) - remove the change.
364
- changes = changes.filter(c => c.key !== key);
365
- return;
366
- }
367
- const type = foundry.utils.getProperty(formData.object, key + "-type") ?? 'add';
368
- newChanges.push({
369
- key: key,
370
- value: value,
371
- type: type
372
- });
373
- if ( customMode ) flags["${id}"][key + "-custommode"] = customMode;
374
- }
375
-
376
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => joinToNode(document.body, property => generateAddValue(document, property), { appendNewLineIfNotEmpty: true }))}
377
-
378
- // Auto-generated damage type Active Effect fields
379
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateDamageTypeActiveEffectFields(entry, document), { appendNewLineIfNotEmpty: true })}
380
-
381
- // Update the existing changes to replace duplicates.
382
- for (let i = 0; i < changes.length; i++) {
383
- const newChange = newChanges.find(c => c.key == changes[i].key);
384
- if (newChange) {
385
- // Replace with the new change and update the array to prevent duplicates.
386
- changes[i] = newChange;
387
- newChanges = newChanges.filter(c => c.key != changes[i].key);
388
- }
389
- }
390
-
391
- // Filter changes for empty form fields.
392
- const finalChanges = changes.concat(newChanges).filter(c => c.value !== null && c.value !== undefined && c.value !== '');
393
- console.log("Active Effect Changes", finalChanges);
394
- await this.document.update({
395
- name: formData.object.name,
396
- description: formData.object.description ?? '',
397
- disabled: formData.object.disabled ?? false,
398
- transfer: formData.object.transfer ?? false,
399
- changes: finalChanges,
400
- flags: flags
401
- });
402
-
403
- // Rerender the parent sheets to update the effect lists.
404
- this.document.parent?.sheet?.render();
405
- if ( this.document.parent?.documentName === "Item" ) {
406
- this.document.parent?.parent?.applyActiveEffects();
407
-
408
- // Wait half a second
409
- await new Promise(r => setTimeout(r, 500));
410
- this.document.parent?.parent?.sheet?.render();
411
- }
412
- }
413
- }
123
+ const fileNode = expandToNode `
124
+ import VueRenderingMixin from './VueRenderingMixin.mjs';
125
+ import { ActiveEffectApp } from "./components/components.vue.es.mjs";
126
+ const { DOCUMENT_OWNERSHIP_LEVELS } = CONST;
127
+ export default class ${entry.config.name}EffectVueSheet extends VueRenderingMixin(foundry.applications.api.DocumentSheetV2) {
128
+
129
+ vueParts = {
130
+ "active-effect": {
131
+ component: ActiveEffectApp,
132
+ template: "<active-effect :context=\\"context\\">Vue rendering for sheet failed.</active-effect>"
133
+ }
134
+ };
135
+
136
+ _arrayEntryKey = 0;
137
+ _renderKey = 0;
138
+
139
+
140
+ /** @override */
141
+ static DEFAULT_OPTIONS = {
142
+ classes: ["${id}", "sheet", "vue-sheet", "active-effect", "active-effect-sheet"],
143
+ viewPermission: DOCUMENT_OWNERSHIP_LEVELS.LIMITED,
144
+ editPermission: DOCUMENT_OWNERSHIP_LEVELS.OWNER,
145
+ position: {
146
+ width: 600,
147
+ height: 600,
148
+ },
149
+ window: {
150
+ resizable: true,
151
+ title: "Active Effect"
152
+ },
153
+ tag: "form",
154
+ actions: {
155
+ onEditImage: this._onEditImage
156
+ },
157
+ changeActions: {
158
+ },
159
+ // Custom property that's merged into this.options
160
+ dragDrop: [
161
+ ],
162
+ form: {
163
+ handler: ${entry.config.name}EffectVueSheet._onSubmitForm,
164
+ submitOnChange: false,
165
+ submitOnClose: true,
166
+ closeOnSubmit: false
167
+ }
168
+ };
169
+
170
+ /* -------------------------------------------- */
171
+
172
+ static async _onEditImage(event, target) {
173
+ const current = foundry.utils.getProperty(this.document, target.dataset.edit);
174
+ const fp = new FilePicker({
175
+ current,
176
+ type: "image",
177
+ callback: (path) => {
178
+ this.document.update({ [target.dataset.edit]: path });
179
+ },
180
+ top: this.position.top + 40,
181
+ left: this.position.left + 10
182
+ });
183
+ return fp.browse();
184
+ }
185
+
186
+ /* -------------------------------------------- */
187
+
188
+ async _prepareContext(options) {
189
+ const context = await super._prepareContext(options);
190
+ this.object = this.document.toObject();
191
+ context.effect = this.object;
192
+ context.descriptionHTML = await TextEditor.enrichHTML(this.object.description, {secrets: this.object.isOwner});
193
+
194
+ // Status Conditions
195
+ const statuses = CONFIG.statusEffects.map(s => {
196
+ return {
197
+ id: s.id,
198
+ label: game.i18n.localize(s.name),
199
+ selected: context.effect.statuses.includes(s.id) ? "selected" : ""
200
+ };
201
+ });
202
+ context.statuses = statuses;
203
+ if ( context.effect.origin ) {
204
+ context.originLink = await TextEditor.enrichHTML("@UUID[" + context.effect.origin + "]");
205
+ }
206
+
207
+ function setValue(obj, access, value, mode) {
208
+ if ( typeof(access)=='string' ) {
209
+ access = access.split('.');
210
+ }
211
+ // Split up an access path into sub-objects, such as "system.attribute.value" => "system": {"attribute": {"value": ...}}
212
+ if ( access.length > 1 ) {
213
+ const key = access.shift();
214
+ if ( !obj[key] ) obj[key] = {};
215
+ setValue(obj[key], access, value, mode);
216
+ }
217
+ else {
218
+ obj[access[0]] = value;
219
+ obj[access[0] + "-mode"] = mode;
220
+ }
221
+ }
222
+
223
+ // Turn the changes into the friendlier format
224
+ for ( const change of context.effect.changes ) {
225
+ setValue(context, change.key, change.value, change.mode);
226
+ }
227
+
228
+ // Output initialization
229
+ const vueContext = {
230
+ // Validates both permissions and compendium status
231
+ editable: this.isEditable,
232
+ owner: this.document.isOwner,
233
+ limited: this.document.limited,
234
+
235
+ // Add the document.
236
+ object: context.effect,
237
+ document: this.document,
238
+
239
+ // Add the data to context.data for easier access, as well as flags.
240
+ system: this.document.system,
241
+ flags: this.document.flags,
242
+
243
+ // Editors
244
+ editors: {},
245
+
246
+ // Force re-renders. Defined in the vue mixin.
247
+ _renderKey: this._renderKey ?? 0,
248
+ _arrayEntryKey: this._arrayEntryKey ?? 0,
249
+ isItemEffect: this.document.parent.documentName === "Item",
250
+ originLink: context.originLink
251
+ // tabs: this._getTabs(options.parts),
252
+ };
253
+
254
+ await this._enrichEditor(vueContext, "description");
255
+
256
+ const numberTypes = ['multiply', 'add', 'downgrade', 'upgrade', 'override'];
257
+ const stringTypes = ['override'];
258
+ const booleanTypes = ['override'];
259
+
260
+ vueContext.numberModes = numberTypes.map(type => ({
261
+ value: type,
262
+ label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
263
+ }));
264
+ vueContext.numberModes.push({
265
+ value: 'custom',
266
+ label: game.i18n.localize("EFFECTS.AddOnce")
267
+ });
268
+ vueContext.basicNumberModes = numberTypes.map(type => ({
269
+ value: type,
270
+ label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
271
+ }));
272
+ vueContext.stringModes = stringTypes.map(type => ({
273
+ value: type,
274
+ label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
275
+ }));
276
+ vueContext.booleanModes = booleanTypes.map(type => ({
277
+ value: type,
278
+ label: game.i18n.localize(\`EFFECT.CHANGES.TYPES.\${type}\`)
279
+ }));
280
+ vueContext.resourceModes = [
281
+ { value: 'custom', label: game.i18n.localize("EFFECTS.AddOnce") }
282
+ ];
283
+ vueContext.trackerModes = [
284
+ { value: 'custom', label: game.i18n.localize("EFFECTS.AddOnce") }
285
+ ];
286
+
287
+ // Copy current change values into vueContext so the Vue component can read them
288
+ for ( const change of context.effect.changes ) {
289
+ setValue(vueContext, change.key, change.value, change.mode);
290
+ }
291
+
292
+ console.dir("Vue Active Effect Context", vueContext);
293
+ return vueContext;
294
+ }
295
+
296
+
297
+ async _enrichEditor(context, field) {
298
+ const enrichmentOptions = {
299
+ // Whether to show secret blocks in the finished html
300
+ secrets: this.document.isOwner,
301
+ // Data to fill in for inline rolls
302
+ rollData: {},
303
+ // Relative UUID resolution
304
+ relativeTo: this.document
305
+ };
306
+
307
+ const editorOptions = {
308
+ toggled: true,
309
+ collaborate: true,
310
+ documentUUID: this.document.uuid,
311
+ height: 300
312
+ };
313
+
314
+ const editorValue = this.document.system?.[field] ?? foundry.utils.getProperty(this.document.system, field);
315
+ context.editors[\`\${field}\`] = {
316
+ enriched: await TextEditor.enrichHTML(editorValue, enrichmentOptions),
317
+ element: foundry.applications.elements.HTMLProseMirrorElement.create({
318
+ ...editorOptions,
319
+ name: \`system.\${field}\`,
320
+ value: editorValue ?? ""
321
+ })
322
+ };
323
+ }
324
+
325
+ /* -------------------------------------------- */
326
+
327
+ _prepareSubmitData(event, form, formData) {
328
+ // We don't modify the image via the sheet itself, so we can remove it from the submit data to avoid errors.
329
+ delete formData.object.img;
330
+ return super._prepareSubmitData(event, form, formData);
331
+ }
332
+
333
+ /* -------------------------------------------- */
334
+
335
+ /**
336
+ * Process form submission for the sheet
337
+ * @this {${entry.config.name}EffectVueSheet} The handler is called with the application as its bound scope
338
+ * @param {SubmitEvent} event The originating form submission event
339
+ * @param {HTMLFormElement} form The form element that was submitted
340
+ * @param {FormDataExtended} formData Processed data for the submitted form
341
+ * @returns {Promise<void>}
342
+ */
343
+ static async _onSubmitForm(event, form, formData) {
344
+ console.log("Updating Active Effect", formData);
345
+ const flags = foundry.utils.duplicate(this.document.flags);
346
+ if ( !flags["${id}"] ) {
347
+ flags["${id}"] = {};
348
+ }
349
+
350
+ // Retrieve the existing effects.
351
+ let changes = this.document.changes ? [...this.document.changes] : [];
352
+
353
+ // Build an array of effects from the form data
354
+ let newChanges = [];
355
+
356
+ function addChange(documentName, key, customMode) {
357
+ const value = foundry.utils.getProperty(formData.object, key);
358
+ if ( value === undefined ) {
359
+ // Field not rendered in the form - preserve any existing change.
360
+ return;
361
+ }
362
+ if ( !value ) {
363
+ // Field explicitly cleared (empty string / zero) - remove the change.
364
+ changes = changes.filter(c => c.key !== key);
365
+ return;
366
+ }
367
+ const type = foundry.utils.getProperty(formData.object, key + "-type") ?? 'add';
368
+ newChanges.push({
369
+ key: key,
370
+ value: value,
371
+ type: type
372
+ });
373
+ if ( customMode ) flags["${id}"][key + "-custommode"] = customMode;
374
+ }
375
+
376
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => joinToNode(document.body, property => generateAddValue(document, property), { appendNewLineIfNotEmpty: true }))}
377
+
378
+ // Auto-generated damage type Active Effect fields
379
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateDamageTypeActiveEffectFields(entry, document), { appendNewLineIfNotEmpty: true })}
380
+
381
+ // Update the existing changes to replace duplicates.
382
+ for (let i = 0; i < changes.length; i++) {
383
+ const newChange = newChanges.find(c => c.key == changes[i].key);
384
+ if (newChange) {
385
+ // Replace with the new change and update the array to prevent duplicates.
386
+ changes[i] = newChange;
387
+ newChanges = newChanges.filter(c => c.key != changes[i].key);
388
+ }
389
+ }
390
+
391
+ // Filter changes for empty form fields.
392
+ const finalChanges = changes.concat(newChanges).filter(c => c.value !== null && c.value !== undefined && c.value !== '');
393
+ console.log("Active Effect Changes", finalChanges);
394
+ await this.document.update({
395
+ name: formData.object.name,
396
+ description: formData.object.description ?? '',
397
+ disabled: formData.object.disabled ?? false,
398
+ transfer: formData.object.transfer ?? false,
399
+ changes: finalChanges,
400
+ flags: flags
401
+ });
402
+
403
+ // Rerender the parent sheets to update the effect lists.
404
+ this.document.parent?.sheet?.render();
405
+ if ( this.document.parent?.documentName === "Item" ) {
406
+ this.document.parent?.parent?.applyActiveEffects();
407
+
408
+ // Wait half a second
409
+ await new Promise(r => setTimeout(r, 500));
410
+ this.document.parent?.parent?.sheet?.render();
411
+ }
412
+ }
413
+ }
414
414
  `.appendNewLineIfNotEmpty();
415
415
  fs.writeFileSync(generatedFilePath, toString(fileNode));
416
416
  }
@@ -434,12 +434,12 @@ export function generateActiveEffectHandlebars(id, entry, destination) {
434
434
  }
435
435
  if (fields.length == 0)
436
436
  return undefined;
437
- return expandToNode `
438
- <!-- ${property.name} Section -->
439
- <fieldset>
440
- <legend>{{localize "${property.name}"}}</legend>
441
- ${joinToNode(fields, field => field, { appendNewLineIfNotEmpty: true })}
442
- </fieldset>
437
+ return expandToNode `
438
+ <!-- ${property.name} Section -->
439
+ <fieldset>
440
+ <legend>{{localize "${property.name}"}}</legend>
441
+ ${joinToNode(fields, field => field, { appendNewLineIfNotEmpty: true })}
442
+ </fieldset>
443
443
  `;
444
444
  }
445
445
  if (isLayout(property)) {
@@ -453,14 +453,14 @@ export function generateActiveEffectHandlebars(id, entry, destination) {
453
453
  // If this is calculated, it's implicitly locked
454
454
  if (property.params.find(x => isNumberParamValue(x)))
455
455
  return;
456
- return expandToNode `
457
- <div class="form-group">
458
- <label>{{localize "${property.name}"}}</label>
459
- <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Number">
460
- {{selectOptions numberModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
461
- </select>
462
- <input type="number" name="${document.name.toLowerCase()}.${getSystemPath(property)}" value="{{${document.name.toLowerCase()}.${getSystemPath(property)}}}" />
463
- </div>
456
+ return expandToNode `
457
+ <div class="form-group">
458
+ <label>{{localize "${property.name}"}}</label>
459
+ <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Number">
460
+ {{selectOptions numberModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
461
+ </select>
462
+ <input type="number" name="${document.name.toLowerCase()}.${getSystemPath(property)}" value="{{${document.name.toLowerCase()}.${getSystemPath(property)}}}" />
463
+ </div>
464
464
  `;
465
465
  }
466
466
  if (isAttributeExp(property)) {
@@ -480,64 +480,64 @@ export function generateActiveEffectHandlebars(id, entry, destination) {
480
480
  // <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max}}" />
481
481
  // </div>
482
482
  // `;
483
- return expandToNode `
484
- <div class="form-group">
485
- <label>{{localize "${property.name}"}} Value</label>
486
- <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Number">
487
- {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
488
- </select>
489
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
490
- </div>
483
+ return expandToNode `
484
+ <div class="form-group">
485
+ <label>{{localize "${property.name}"}} Value</label>
486
+ <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Number">
487
+ {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
488
+ </select>
489
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
490
+ </div>
491
491
  `;
492
492
  }
493
493
  if (isResourceExp(property)) {
494
- return expandToNode `
495
- <div class="form-group">
496
- <label>{{localize "${property.name}"}} Current</label>
497
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode" data-dtype="Number">
498
- {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
499
- </select>
500
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
501
- </div>
502
- <div class="form-group">
503
- <label>{{localize "${property.name}"}} Max</label>
504
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode" data-dtype="Number">
505
- {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode}}
506
- </select>
507
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max}}" />
508
- </div>
494
+ return expandToNode `
495
+ <div class="form-group">
496
+ <label>{{localize "${property.name}"}} Current</label>
497
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode" data-dtype="Number">
498
+ {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
499
+ </select>
500
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
501
+ </div>
502
+ <div class="form-group">
503
+ <label>{{localize "${property.name}"}} Max</label>
504
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode" data-dtype="Number">
505
+ {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode}}
506
+ </select>
507
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max}}" />
508
+ </div>
509
509
  `;
510
510
  }
511
511
  if (isTrackerExp(property)) {
512
- return expandToNode `
513
- <div class="form-group">
514
- <label>{{localize "${property.name}"}} Min</label>
515
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min-mode" data-dtype="Number">
516
- {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min-mode}}
517
- </select>
518
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min}}" />
519
- </div>
520
- <div class="form-group">
521
- <label>{{localize "${property.name}"}} Current</label>
522
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode" data-dtype="Number">
523
- {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
524
- </select>
525
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
526
- </div>
527
- <div class="form-group">
528
- <label>{{localize "${property.name}"}} Temp</label>
529
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp-mode" data-dtype="Number">
530
- {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp-mode}}
531
- </select>
532
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp}}" />
533
- </div>
534
- <div class="form-group">
535
- <label>{{localize "${property.name}"}} Max</label>
536
- <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode" data-dtype="Number">
537
- {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode}}
538
- </select>
539
- <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max}}" />
540
- </div>
512
+ return expandToNode `
513
+ <div class="form-group">
514
+ <label>{{localize "${property.name}"}} Min</label>
515
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min-mode" data-dtype="Number">
516
+ {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min-mode}}
517
+ </select>
518
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.min}}" />
519
+ </div>
520
+ <div class="form-group">
521
+ <label>{{localize "${property.name}"}} Current</label>
522
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode" data-dtype="Number">
523
+ {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value-mode}}
524
+ </select>
525
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.value}}" />
526
+ </div>
527
+ <div class="form-group">
528
+ <label>{{localize "${property.name}"}} Temp</label>
529
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp-mode" data-dtype="Number">
530
+ {{selectOptions resourceModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp-mode}}
531
+ </select>
532
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.temp}}" />
533
+ </div>
534
+ <div class="form-group">
535
+ <label>{{localize "${property.name}"}} Max</label>
536
+ <select name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode" data-dtype="Number">
537
+ {{selectOptions numberModes selected=${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max-mode}}
538
+ </select>
539
+ <input type="number" name="${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max" value="{{${document.name.toLowerCase()}.system.${property.name.toLowerCase()}.max}}" />
540
+ </div>
541
541
  `;
542
542
  }
543
543
  if (isStringExp(property)) {
@@ -554,89 +554,89 @@ export function generateActiveEffectHandlebars(id, entry, destination) {
554
554
  // </div>
555
555
  // `;
556
556
  // }
557
- return expandToNode `
558
- <div class="form-group">
559
- <label>{{localize "${property.name}"}}</label>
560
- <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="String">
561
- {{selectOptions stringModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
562
- </select>
563
- <input type="text" name="${document.name.toLowerCase()}.${getSystemPath(property)}" value="{{${document.name.toLowerCase()}.${getSystemPath(property)}}}" />
564
- </div>
557
+ return expandToNode `
558
+ <div class="form-group">
559
+ <label>{{localize "${property.name}"}}</label>
560
+ <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="String">
561
+ {{selectOptions stringModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
562
+ </select>
563
+ <input type="text" name="${document.name.toLowerCase()}.${getSystemPath(property)}" value="{{${document.name.toLowerCase()}.${getSystemPath(property)}}}" />
564
+ </div>
565
565
  `;
566
566
  }
567
567
  if (isBooleanExp(property)) {
568
- return expandToNode `
569
- <div class="form-group">
570
- <label>{{localize "${property.name}"}}</label>
571
- <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Boolean">
572
- {{selectOptions booleanModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
573
- </select>
574
- <input type="checkbox" name="${document.name.toLowerCase()}.${getSystemPath(property)}" {{checked ${document.name.toLowerCase()}.${getSystemPath(property)}}} />
575
- </div>
568
+ return expandToNode `
569
+ <div class="form-group">
570
+ <label>{{localize "${property.name}"}}</label>
571
+ <select name="${document.name.toLowerCase()}.${getSystemPath(property)}-mode" data-dtype="Boolean">
572
+ {{selectOptions booleanModes selected=${document.name.toLowerCase()}.${getSystemPath(property)}-mode}}
573
+ </select>
574
+ <input type="checkbox" name="${document.name.toLowerCase()}.${getSystemPath(property)}" {{checked ${document.name.toLowerCase()}.${getSystemPath(property)}}} />
575
+ </div>
576
576
  `;
577
577
  }
578
578
  return;
579
579
  }
580
580
  function generateDocumentTab(document) {
581
- return expandToNode `
582
- <!-- ${document.name} Tab -->
583
- <section class="tab" data-tab="${document.name.toLowerCase()}">
584
- ${joinToNode(document.body, property => generateField(document, property), { appendNewLineIfNotEmpty: true })}
585
- </section>
581
+ return expandToNode `
582
+ <!-- ${document.name} Tab -->
583
+ <section class="tab" data-tab="${document.name.toLowerCase()}">
584
+ ${joinToNode(document.body, property => generateField(document, property), { appendNewLineIfNotEmpty: true })}
585
+ </section>
586
586
  `;
587
587
  }
588
- const fileNode = expandToNode `
589
- <form class="{{cssClass}}" autocomplete="off">
590
- <!-- Effect Header -->
591
- <header class="sheet-header">
592
- <img class="effect-icon effect-img" src="{{ effect.img }}" data-edit="img">
593
- <h1 class="effect-title"><input type="text" name="name" value="{{effect.name}}" placeholder="{{localize 'ARCHMAGE.name'}}"/></h1>
594
- </header>
595
-
596
- <article class="sheet-content">
597
- <section class="sheet-body">
598
- <!-- Effect Configuration Tabs -->
599
- <nav class="sheet-tabs tabs">
600
- <a class="item" data-tab="info"><i class="fas fa-book"></i> {{localize "Info"}}</a>
601
- ${joinToNode(entry.documents.filter(d => isActor(d)), property => `<a class="item" data-tab="${property.name.toLowerCase()}">{{localize "${property.name}"}}</a>`, { appendNewLineIfNotEmpty: true })}
602
- </nav>
603
-
604
- <div class="sheet-tabs-content">
605
- <!-- Info Tab -->
606
- <section class="tab" data-tab="info">
607
- <div class="form-group stacked">
608
- <label>{{ localize "EFFECT.Description" }}</label>
609
- {{editor descriptionHTML target="description" button=false editable=editable engine="prosemirror" collaborate=false}}
610
- </div>
611
-
612
- <div class="form-group">
613
- <label>{{ localize "EFFECT.Disabled" }}</label>
614
- <input type="checkbox" name="disabled" {{ checked effect.disabled }}/>
615
- </div>
616
-
617
- {{#if originLink}}
618
- <div class="form-group">
619
- <label>{{ localize "EFFECT.Origin" }}</label>
620
- {{{originLink}}}
621
- </div>
622
- {{/if}}
623
-
624
- {{#if isItemEffect}}
625
- <div class="form-group">
626
- <label>{{ labels.transfer.name }}</label>
627
- <div class="form-fields">
628
- <input type="checkbox" name="transfer" {{checked data.transfer}}/>
629
- </div>
630
- <p class="hint">{{ labels.transfer.hint }}</p>
631
- </div>
632
- {{/if}}
633
- </div>
634
-
635
- ${joinToNode(entry.documents, property => generateDocumentTab(property), { appendNewLineIfNotEmpty: true })}
636
- </section>
637
- </section>
638
- </article>
639
- </form>
588
+ const fileNode = expandToNode `
589
+ <form class="{{cssClass}}" autocomplete="off">
590
+ <!-- Effect Header -->
591
+ <header class="sheet-header">
592
+ <img class="effect-icon effect-img" src="{{ effect.img }}" data-edit="img">
593
+ <h1 class="effect-title"><input type="text" name="name" value="{{effect.name}}" placeholder="{{localize 'ARCHMAGE.name'}}"/></h1>
594
+ </header>
595
+
596
+ <article class="sheet-content">
597
+ <section class="sheet-body">
598
+ <!-- Effect Configuration Tabs -->
599
+ <nav class="sheet-tabs tabs">
600
+ <a class="item" data-tab="info"><i class="fas fa-book"></i> {{localize "Info"}}</a>
601
+ ${joinToNode(entry.documents.filter(d => isActor(d)), property => `<a class="item" data-tab="${property.name.toLowerCase()}">{{localize "${property.name}"}}</a>`, { appendNewLineIfNotEmpty: true })}
602
+ </nav>
603
+
604
+ <div class="sheet-tabs-content">
605
+ <!-- Info Tab -->
606
+ <section class="tab" data-tab="info">
607
+ <div class="form-group stacked">
608
+ <label>{{ localize "EFFECT.Description" }}</label>
609
+ {{editor descriptionHTML target="description" button=false editable=editable engine="prosemirror" collaborate=false}}
610
+ </div>
611
+
612
+ <div class="form-group">
613
+ <label>{{ localize "EFFECT.Disabled" }}</label>
614
+ <input type="checkbox" name="disabled" {{ checked effect.disabled }}/>
615
+ </div>
616
+
617
+ {{#if originLink}}
618
+ <div class="form-group">
619
+ <label>{{ localize "EFFECT.Origin" }}</label>
620
+ {{{originLink}}}
621
+ </div>
622
+ {{/if}}
623
+
624
+ {{#if isItemEffect}}
625
+ <div class="form-group">
626
+ <label>{{ labels.transfer.name }}</label>
627
+ <div class="form-fields">
628
+ <input type="checkbox" name="transfer" {{checked data.transfer}}/>
629
+ </div>
630
+ <p class="hint">{{ labels.transfer.hint }}</p>
631
+ </div>
632
+ {{/if}}
633
+ </div>
634
+
635
+ ${joinToNode(entry.documents, property => generateDocumentTab(property), { appendNewLineIfNotEmpty: true })}
636
+ </section>
637
+ </section>
638
+ </article>
639
+ </form>
640
640
  `.appendNewLineIfNotEmpty();
641
641
  fs.writeFileSync(generatedFilePath, toString(fileNode));
642
642
  }