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.
- package/.claude/agents/langium-language-designer.md +38 -38
- package/.claude/agents/typescript-vscode-expert.md +29 -29
- package/.claude/agents/ui-ux-designer.md +36 -36
- package/.claude/settings.local.json +33 -33
- package/.idea/inspectionProfiles/Project_Default.xml +6 -6
- package/.idea/isdl.iml +13 -13
- package/.idea/modules.xml +8 -8
- package/.idea/vcs.xml +6 -6
- package/.idea/watcherTasks.xml +3 -3
- package/.vscodeignore +18 -18
- package/LICENSE +673 -673
- package/README.md +86 -86
- package/bin/cli.js +4 -4
- package/bin/lsp.js +8 -8
- package/out/_backgrounds.scss +91 -91
- package/out/_handlebars.scss +497 -497
- package/out/_isdlStyles.scss +1444 -1381
- package/out/_vuetifyOverrides.scss +425 -425
- package/out/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/_backgrounds.scss +91 -91
- package/out/cli/components/_handlebars.scss +497 -497
- package/out/cli/components/_isdlStyles.scss +1444 -1381
- package/out/cli/components/_vuetifyOverrides.scss +425 -425
- package/out/cli/components/_vuetifyStyles.scss +31957 -31957
- package/out/cli/components/active-effect-sheet-generator.js +453 -453
- package/out/cli/components/chat-card-generator.js +654 -651
- package/out/cli/components/chat-card-generator.js.map +1 -1
- package/out/cli/components/css-generator.js +4 -4
- package/out/cli/components/damage-roll-generator.js +160 -160
- package/out/cli/components/datamodel-generator.js +264 -257
- package/out/cli/components/datamodel-generator.js.map +1 -1
- package/out/cli/components/derived-data-generator.js +923 -923
- package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
- package/out/cli/components/init-hook-generator.js +495 -495
- package/out/cli/components/language-generator.js +1 -1
- package/out/cli/components/language-generator.js.map +1 -1
- package/out/cli/components/measured-template-preview.js +221 -221
- package/out/cli/components/method-generator.js +979 -887
- package/out/cli/components/method-generator.js.map +1 -1
- package/out/cli/components/ready-hook-generator.js +404 -404
- package/out/cli/components/token-generator.js +116 -116
- package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
- package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
- package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
- package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
- package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
- package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
- package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
- package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
- package/out/cli/components/vue/base-components/vue-dice.js +98 -98
- package/out/cli/components/vue/base-components/vue-die.js +73 -73
- package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
- package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
- package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
- package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
- package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
- package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
- package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
- package/out/cli/components/vue/base-components/vue-money.js +483 -483
- package/out/cli/components/vue/base-components/vue-number.js +174 -174
- package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
- package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
- package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
- package/out/cli/components/vue/base-components/vue-resource.js +136 -136
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
- package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
- package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
- package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
- package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
- package/out/cli/components/vue/base-components/vue-string.js +60 -60
- package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
- package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
- package/out/cli/components/vue/vue-action-component-generator.js +64 -64
- package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
- package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
- package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
- package/out/cli/components/vue/vue-document-creation-app.js +121 -121
- package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
- package/out/cli/components/vue/vue-generator.js +40 -40
- package/out/cli/components/vue/vue-mixin.js +296 -296
- package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
- package/out/cli/components/vue/vue-prompt-generator.js +91 -76
- package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
- package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
- package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
- package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
- package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
- package/out/cli/generator.js +438 -433
- package/out/cli/generator.js.map +1 -1
- package/out/extension/github/githubAuthProvider.js +71 -29
- package/out/extension/github/githubAuthProvider.js.map +1 -1
- package/out/extension/github/githubGistManager.js +4 -3
- package/out/extension/github/githubGistManager.js.map +1 -1
- package/out/extension/github/githubManager.js +40 -38
- package/out/extension/github/githubManager.js.map +1 -1
- package/out/extension/github/githubQuickActions.js +120 -120
- package/out/extension/github/system-workflow.yml +47 -47
- package/out/extension/main.cjs +909 -532
- package/out/extension/main.cjs.map +3 -3
- package/out/extension/package.json +419 -419
- package/out/language/generated/ast.js +51 -2
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.js +14240 -13991
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/intelligent-system-design-language-validator.js +32 -2
- package/out/language/intelligent-system-design-language-validator.js.map +1 -1
- package/out/language/isdl-scope-provider.js +14 -1
- package/out/language/isdl-scope-provider.js.map +1 -1
- package/out/language/main.cjs +913 -569
- package/out/language/main.cjs.map +3 -3
- package/out/package.json +419 -419
- package/out/progressbar.min.js +6 -6
- package/out/styles.scss +762 -747
- package/out/test/validating/diagnostics.test.js +40 -0
- package/out/test/validating/diagnostics.test.js.map +1 -1
- package/package.json +419 -419
|
@@ -7,106 +7,106 @@ export function generateCanvasToken(entry, id, destination) {
|
|
|
7
7
|
if (!fs.existsSync(generatedFileDir)) {
|
|
8
8
|
fs.mkdirSync(generatedFileDir, { recursive: true });
|
|
9
9
|
}
|
|
10
|
-
const fileNode = expandToNode `
|
|
11
|
-
export default class ${entry.config.name}Token extends Token {
|
|
12
|
-
/** @inheritDoc */
|
|
13
|
-
_drawBar(number, bar, data) {
|
|
14
|
-
const resource = foundry.utils.getProperty(this.document.actor.system, data.attribute);
|
|
15
|
-
|
|
16
|
-
if (resource.temp <= 0) {
|
|
17
|
-
return super._drawBar(number, bar, data);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const displayMax = resource.max;
|
|
21
|
-
|
|
22
|
-
const tempPct = Math.clamp(resource.temp, 0, displayMax) / displayMax;
|
|
23
|
-
const colorPct = Math.clamp(resource.value, 0, displayMax) / displayMax;
|
|
24
|
-
|
|
25
|
-
// Determine the container size (logic borrowed from core)
|
|
26
|
-
const w = this.w;
|
|
27
|
-
let h = Math.max((canvas.dimensions.size / 12), 8);
|
|
28
|
-
if (this.document.height >= 2) h *= 1.6;
|
|
29
|
-
const bs = Math.clamp(h / 8, 1, 2);
|
|
30
|
-
const bs1 = bs + 1;
|
|
31
|
-
|
|
32
|
-
// Determine the color to use
|
|
33
|
-
const blk = 0x000000;
|
|
34
|
-
let color;
|
|
35
|
-
if ( number === 0 ) color = Color.fromRGB([(1-(colorPct/2)), colorPct, 0]);
|
|
36
|
-
else color = Color.fromRGB([(0.5 * colorPct), (0.7 * colorPct), 0.5 + (colorPct / 2)]);
|
|
37
|
-
|
|
38
|
-
// Overall bar container
|
|
39
|
-
bar.clear();
|
|
40
|
-
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
|
41
|
-
|
|
42
|
-
// Health bar
|
|
43
|
-
bar.beginFill(color, 1.0).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, colorPct*w, h, 2);
|
|
44
|
-
|
|
45
|
-
function getBlendedContrast(color, blendFactor = 0.3) {
|
|
46
|
-
// Convert color to hex string
|
|
47
|
-
let hex = color.toString(16).padStart(6, "0");
|
|
48
|
-
|
|
49
|
-
// Extract RGB components
|
|
50
|
-
let r = parseInt(hex.substring(0, 2), 16);
|
|
51
|
-
let g = parseInt(hex.substring(2, 4), 16);
|
|
52
|
-
let b = parseInt(hex.substring(4, 6), 16);
|
|
53
|
-
|
|
54
|
-
// Calculate luminance
|
|
55
|
-
let luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
56
|
-
|
|
57
|
-
// Determine base contrast color (black or white)
|
|
58
|
-
let baseContrast = luminance > 0.5 ? 0x000000 : 0xFFFFFF;
|
|
59
|
-
|
|
60
|
-
// Extract RGB from base contrast
|
|
61
|
-
let bcR = (baseContrast >> 16) & 0xFF;
|
|
62
|
-
let bcG = (baseContrast >> 8) & 0xFF;
|
|
63
|
-
let bcB = baseContrast & 0xFF;
|
|
64
|
-
|
|
65
|
-
// Blend original color with contrast color
|
|
66
|
-
let blendedR = Math.round(r * (1 - blendFactor) + bcR * blendFactor);
|
|
67
|
-
let blendedG = Math.round(g * (1 - blendFactor) + bcG * blendFactor);
|
|
68
|
-
let blendedB = Math.round(b * (1 - blendFactor) + bcB * blendFactor);
|
|
69
|
-
|
|
70
|
-
// Return new blended color
|
|
71
|
-
return (blendedR << 16) | (blendedG << 8) | blendedB;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Calculate a temporary color that is a lighter shade than color
|
|
75
|
-
const tempColor = number === 0 ? 0x33AAFF : getBlendedContrast(color);
|
|
76
|
-
|
|
77
|
-
// Temporary health bar
|
|
78
|
-
|
|
79
|
-
const blur = new PIXI.filters.BlurFilter();
|
|
80
|
-
blur.blur = 1; // Adjust for stronger glow
|
|
81
|
-
|
|
82
|
-
const colorMatrix = new PIXI.filters.ColorMatrixFilter();
|
|
83
|
-
colorMatrix.brightness(1.2); // Increase brightness
|
|
84
|
-
|
|
85
|
-
const tempBar = new PIXI.Graphics();
|
|
86
|
-
tempBar.filters = [blur, colorMatrix];
|
|
87
|
-
tempBar.beginFill(tempColor, 1.0).lineStyle(0).drawRoundedRect(bs1, bs1, (tempPct*w)-(2*bs1), h-(2*bs1), 1)
|
|
88
|
-
|
|
89
|
-
bar.addChild(tempBar);
|
|
90
|
-
|
|
91
|
-
// Animation - Flashing (Opacity Pulse)
|
|
92
|
-
canvas.app.ticker.add((delta) => {
|
|
93
|
-
tempBar.alpha = 0.7 + 0.3 * Math.sin(performance.now() / 1000);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Set position
|
|
97
|
-
let posY = number === 0 ? this.h - h : 0;
|
|
98
|
-
bar.position.set(0, posY);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/* -------------------------------------------- */
|
|
102
|
-
|
|
103
|
-
static onTargetToken(user, token, targeted) {
|
|
104
|
-
if ( !targeted ) return;
|
|
105
|
-
if ( !token.hasDynamicRing ) return;
|
|
106
|
-
const color = Color.from(user.color);
|
|
107
|
-
token.ring.flashColor(color, { duration: 500, easing: token.ring.constructor.easeTwoPeaks });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
10
|
+
const fileNode = expandToNode `
|
|
11
|
+
export default class ${entry.config.name}Token extends Token {
|
|
12
|
+
/** @inheritDoc */
|
|
13
|
+
_drawBar(number, bar, data) {
|
|
14
|
+
const resource = foundry.utils.getProperty(this.document.actor.system, data.attribute);
|
|
15
|
+
|
|
16
|
+
if (resource.temp <= 0) {
|
|
17
|
+
return super._drawBar(number, bar, data);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const displayMax = resource.max;
|
|
21
|
+
|
|
22
|
+
const tempPct = Math.clamp(resource.temp, 0, displayMax) / displayMax;
|
|
23
|
+
const colorPct = Math.clamp(resource.value, 0, displayMax) / displayMax;
|
|
24
|
+
|
|
25
|
+
// Determine the container size (logic borrowed from core)
|
|
26
|
+
const w = this.w;
|
|
27
|
+
let h = Math.max((canvas.dimensions.size / 12), 8);
|
|
28
|
+
if (this.document.height >= 2) h *= 1.6;
|
|
29
|
+
const bs = Math.clamp(h / 8, 1, 2);
|
|
30
|
+
const bs1 = bs + 1;
|
|
31
|
+
|
|
32
|
+
// Determine the color to use
|
|
33
|
+
const blk = 0x000000;
|
|
34
|
+
let color;
|
|
35
|
+
if ( number === 0 ) color = Color.fromRGB([(1-(colorPct/2)), colorPct, 0]);
|
|
36
|
+
else color = Color.fromRGB([(0.5 * colorPct), (0.7 * colorPct), 0.5 + (colorPct / 2)]);
|
|
37
|
+
|
|
38
|
+
// Overall bar container
|
|
39
|
+
bar.clear();
|
|
40
|
+
bar.beginFill(blk, 0.5).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, w, h, 3);
|
|
41
|
+
|
|
42
|
+
// Health bar
|
|
43
|
+
bar.beginFill(color, 1.0).lineStyle(bs, blk, 1.0).drawRoundedRect(0, 0, colorPct*w, h, 2);
|
|
44
|
+
|
|
45
|
+
function getBlendedContrast(color, blendFactor = 0.3) {
|
|
46
|
+
// Convert color to hex string
|
|
47
|
+
let hex = color.toString(16).padStart(6, "0");
|
|
48
|
+
|
|
49
|
+
// Extract RGB components
|
|
50
|
+
let r = parseInt(hex.substring(0, 2), 16);
|
|
51
|
+
let g = parseInt(hex.substring(2, 4), 16);
|
|
52
|
+
let b = parseInt(hex.substring(4, 6), 16);
|
|
53
|
+
|
|
54
|
+
// Calculate luminance
|
|
55
|
+
let luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
56
|
+
|
|
57
|
+
// Determine base contrast color (black or white)
|
|
58
|
+
let baseContrast = luminance > 0.5 ? 0x000000 : 0xFFFFFF;
|
|
59
|
+
|
|
60
|
+
// Extract RGB from base contrast
|
|
61
|
+
let bcR = (baseContrast >> 16) & 0xFF;
|
|
62
|
+
let bcG = (baseContrast >> 8) & 0xFF;
|
|
63
|
+
let bcB = baseContrast & 0xFF;
|
|
64
|
+
|
|
65
|
+
// Blend original color with contrast color
|
|
66
|
+
let blendedR = Math.round(r * (1 - blendFactor) + bcR * blendFactor);
|
|
67
|
+
let blendedG = Math.round(g * (1 - blendFactor) + bcG * blendFactor);
|
|
68
|
+
let blendedB = Math.round(b * (1 - blendFactor) + bcB * blendFactor);
|
|
69
|
+
|
|
70
|
+
// Return new blended color
|
|
71
|
+
return (blendedR << 16) | (blendedG << 8) | blendedB;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Calculate a temporary color that is a lighter shade than color
|
|
75
|
+
const tempColor = number === 0 ? 0x33AAFF : getBlendedContrast(color);
|
|
76
|
+
|
|
77
|
+
// Temporary health bar
|
|
78
|
+
|
|
79
|
+
const blur = new PIXI.filters.BlurFilter();
|
|
80
|
+
blur.blur = 1; // Adjust for stronger glow
|
|
81
|
+
|
|
82
|
+
const colorMatrix = new PIXI.filters.ColorMatrixFilter();
|
|
83
|
+
colorMatrix.brightness(1.2); // Increase brightness
|
|
84
|
+
|
|
85
|
+
const tempBar = new PIXI.Graphics();
|
|
86
|
+
tempBar.filters = [blur, colorMatrix];
|
|
87
|
+
tempBar.beginFill(tempColor, 1.0).lineStyle(0).drawRoundedRect(bs1, bs1, (tempPct*w)-(2*bs1), h-(2*bs1), 1)
|
|
88
|
+
|
|
89
|
+
bar.addChild(tempBar);
|
|
90
|
+
|
|
91
|
+
// Animation - Flashing (Opacity Pulse)
|
|
92
|
+
canvas.app.ticker.add((delta) => {
|
|
93
|
+
tempBar.alpha = 0.7 + 0.3 * Math.sin(performance.now() / 1000);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Set position
|
|
97
|
+
let posY = number === 0 ? this.h - h : 0;
|
|
98
|
+
bar.position.set(0, posY);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* -------------------------------------------- */
|
|
102
|
+
|
|
103
|
+
static onTargetToken(user, token, targeted) {
|
|
104
|
+
if ( !targeted ) return;
|
|
105
|
+
if ( !token.hasDynamicRing ) return;
|
|
106
|
+
const color = Color.from(user.color);
|
|
107
|
+
token.ring.flashColor(color, { duration: 500, easing: token.ring.constructor.easeTwoPeaks });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
110
|
`.appendNewLineIfNotEmpty();
|
|
111
111
|
fs.writeFileSync(generatedFilePath, toString(fileNode), 'utf-8');
|
|
112
112
|
}
|
|
@@ -116,22 +116,22 @@ export function generateTokenDocument(entry, id, destination) {
|
|
|
116
116
|
if (!fs.existsSync(generatedFileDir)) {
|
|
117
117
|
fs.mkdirSync(generatedFileDir, { recursive: true });
|
|
118
118
|
}
|
|
119
|
-
const fileNode = expandToNode `
|
|
120
|
-
export default class ${entry.config.name}TokenDocument extends TokenDocument {
|
|
121
|
-
/** @inheritDoc */
|
|
122
|
-
getBarAttribute(barName, options={}) {
|
|
123
|
-
const data = super.getBarAttribute(barName, options);
|
|
124
|
-
if ( data === null ) return;
|
|
125
|
-
const resource = foundry.utils.getProperty(this.actor.system, data.attribute);
|
|
126
|
-
data.value += resource.temp;
|
|
127
|
-
return data;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
_onUpdateBaseActor(update={}, options={}) {
|
|
131
|
-
if (foundry.utils.isEmpty(update)) return;
|
|
132
|
-
if (!this.isEditable) return;
|
|
133
|
-
super._onUpdateBaseActor(update, options);
|
|
134
|
-
}
|
|
119
|
+
const fileNode = expandToNode `
|
|
120
|
+
export default class ${entry.config.name}TokenDocument extends TokenDocument {
|
|
121
|
+
/** @inheritDoc */
|
|
122
|
+
getBarAttribute(barName, options={}) {
|
|
123
|
+
const data = super.getBarAttribute(barName, options);
|
|
124
|
+
if ( data === null ) return;
|
|
125
|
+
const resource = foundry.utils.getProperty(this.actor.system, data.attribute);
|
|
126
|
+
data.value += resource.temp;
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_onUpdateBaseActor(update={}, options={}) {
|
|
131
|
+
if (foundry.utils.isEmpty(update)) return;
|
|
132
|
+
if (!this.isEditable) return;
|
|
133
|
+
super._onUpdateBaseActor(update, options);
|
|
134
|
+
}
|
|
135
135
|
}`.appendNewLineIfNotEmpty();
|
|
136
136
|
fs.writeFileSync(generatedFilePath, toString(fileNode), 'utf-8');
|
|
137
137
|
}
|
|
@@ -7,144 +7,144 @@ export default function generateAttributeComponent(destination) {
|
|
|
7
7
|
if (!fs.existsSync(generatedFileDir)) {
|
|
8
8
|
fs.mkdirSync(generatedFileDir, { recursive: true });
|
|
9
9
|
}
|
|
10
|
-
const fileNode = expandToNode `
|
|
11
|
-
<script setup>
|
|
12
|
-
import { ref, computed, inject } from "vue";
|
|
13
|
-
|
|
14
|
-
const props = defineProps({
|
|
15
|
-
label: String,
|
|
16
|
-
icon: String,
|
|
17
|
-
hasMod: Boolean,
|
|
18
|
-
mod: Number,
|
|
19
|
-
systemPath: String,
|
|
20
|
-
context: Object,
|
|
21
|
-
min: Number,
|
|
22
|
-
disabled: Boolean,
|
|
23
|
-
primaryColor: String,
|
|
24
|
-
secondaryColor: String,
|
|
25
|
-
editMode: Boolean,
|
|
26
|
-
attributeStyle: String, // plain or box
|
|
27
|
-
roll: Function,
|
|
28
|
-
hasRoll: Boolean,
|
|
29
|
-
// Icon shown in the click overlay. Defaults to a die for roll: handlers; function: handlers
|
|
30
|
-
// pass the attribute's own icon so the affordance matches what the click actually does.
|
|
31
|
-
clickIcon: { type: String, default: "fa-solid fa-dice" }
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const document = inject("rawDocument");
|
|
35
|
-
|
|
36
|
-
const value = computed({
|
|
37
|
-
get: () => foundry.utils.getProperty(props.context, props.systemPath),
|
|
38
|
-
set: (newValue) => foundry.utils.setProperty(props.context, props.systemPath, newValue)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Vuetify's up/down stepper buttons update the model without firing a
|
|
42
|
-
// native change event, so Foundry's submitOnChange form handler never
|
|
43
|
-
// persists them. When the value changes while focus is NOT on a text
|
|
44
|
-
// input (i.e. a stepper click, not typing), persist directly. Typing
|
|
45
|
-
// still persists via the input's native change on blur/enter.
|
|
46
|
-
// ('document' is the injected Foundry document; DOM access uses window.)
|
|
47
|
-
const persistOnStep = (path, newValue) => {
|
|
48
|
-
if (document && window.document.activeElement?.tagName !== 'INPUT') {
|
|
49
|
-
document.update({ [path]: newValue });
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const getStyle = computed(() => {
|
|
54
|
-
const p = props.primaryColor || "#92aed9";
|
|
55
|
-
|
|
56
|
-
// Get either black or white text color based on the primary color brightness
|
|
57
|
-
const brightness = (parseInt(p.slice(1, 3), 16) * 299 + parseInt(p.slice(3, 5), 16) * 587 + parseInt(p.slice(5, 7), 16) * 114) / 1000;
|
|
58
|
-
let textColor = "#ffffff"; // Default to white text
|
|
59
|
-
if (brightness > 128) {
|
|
60
|
-
textColor = "#000000"; // Use black text for brighter colors
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
backgroundColor: p,
|
|
65
|
-
color: textColor,
|
|
66
|
-
borderColor: p
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const getLabel = computed(() => {
|
|
71
|
-
const localized = game.i18n.localize(props.label);
|
|
72
|
-
if (props.icon) {
|
|
73
|
-
return \`<i class="fa-solid \${props.icon}"></i> \${localized}\`;
|
|
74
|
-
}
|
|
75
|
-
return localized;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const isHovering = ref(false);
|
|
79
|
-
|
|
80
|
-
const handleRoll = () => {
|
|
81
|
-
if (props.roll) {
|
|
82
|
-
props.roll();
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
</script>
|
|
86
|
-
|
|
87
|
-
<template>
|
|
88
|
-
<div class="dice-container"
|
|
89
|
-
@mouseenter="isHovering = true"
|
|
90
|
-
@mouseleave="isHovering = false"
|
|
91
|
-
>
|
|
92
|
-
<div v-if="attributeStyle == 'plain'">
|
|
93
|
-
<v-number-input v-if="attributeStyle == 'plain' && editMode" :model-value="value" @update:model-value="(v) => { value = v; persistOnStep(systemPath, v); }" :name="systemPath" :min="props.min" :disabled="disabled" type="number" variant="outlined" density="compact" :hide-details="true" data-tooltip="Value">
|
|
94
|
-
<template #label>
|
|
95
|
-
<span v-html="getLabel" />
|
|
96
|
-
</template>
|
|
97
|
-
</v-number-input>
|
|
98
|
-
<v-number-input v-if="attributeStyle == 'plain' && !editMode" :model-value="mod" :name="systemPath" :disabled="true" type="number" controlVariant="hidden" variant="outlined" density="compact" :hide-details="true" data-tooltip="Mod">
|
|
99
|
-
<template #label>
|
|
100
|
-
<span v-html="getLabel" />
|
|
101
|
-
</template>
|
|
102
|
-
</v-number-input>
|
|
103
|
-
<!-- Overlay with dice icon - appears on hover when roll function is available -->
|
|
104
|
-
<div
|
|
105
|
-
v-if="hasRoll && isHovering && !editMode"
|
|
106
|
-
class="dice-overlay plain"
|
|
107
|
-
@click="handleRoll"
|
|
108
|
-
>
|
|
109
|
-
<v-btn
|
|
110
|
-
icon
|
|
111
|
-
size="x-small"
|
|
112
|
-
:color="primaryColor"
|
|
113
|
-
class="dice-roll-btn"
|
|
114
|
-
variant="elevated"
|
|
115
|
-
elevation="4"
|
|
116
|
-
>
|
|
117
|
-
<v-icon size="small" :icon="clickIcon"></v-icon>
|
|
118
|
-
</v-btn>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
<v-container
|
|
122
|
-
:class="['isdl-property', 'attributeExp', { 'no-mod': !hasMod }]"
|
|
123
|
-
v-if="attributeStyle == 'box'"
|
|
124
|
-
|
|
125
|
-
>
|
|
126
|
-
<v-label :style="getStyle"><v-icon v-if="icon" size="x-small" :icon="icon" style="padding-right: 0.5rem;"></v-icon>{{ game.i18n.localize(label) }}</v-label>
|
|
127
|
-
<div class="mod" v-if="hasMod">{{ mod }}</div>
|
|
128
|
-
<v-number-input :model-value="value" @update:model-value="(v) => { value = v; persistOnStep(systemPath, v); }" inset :min="props.min" :disabled="disabled" :name="systemPath" :controlVariant="disabled ? 'hidden' : 'split'" :step="1" type="number" variant="outlined" density="compact" :hide-details="true" :tile="true"></v-number-input>
|
|
129
|
-
<!-- Overlay with dice icon - appears on hover when roll function is available -->
|
|
130
|
-
<div
|
|
131
|
-
v-if="hasRoll && isHovering && !editMode"
|
|
132
|
-
class="dice-overlay"
|
|
133
|
-
@click="handleRoll"
|
|
134
|
-
>
|
|
135
|
-
<v-btn
|
|
136
|
-
icon
|
|
137
|
-
:color="primaryColor"
|
|
138
|
-
class="dice-roll-btn"
|
|
139
|
-
variant="elevated"
|
|
140
|
-
elevation="4"
|
|
141
|
-
>
|
|
142
|
-
<v-icon :icon="clickIcon"></v-icon>
|
|
143
|
-
</v-btn>
|
|
144
|
-
</div>
|
|
145
|
-
</v-container>
|
|
146
|
-
</div>
|
|
147
|
-
</template>
|
|
10
|
+
const fileNode = expandToNode `
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, computed, inject } from "vue";
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
label: String,
|
|
16
|
+
icon: String,
|
|
17
|
+
hasMod: Boolean,
|
|
18
|
+
mod: Number,
|
|
19
|
+
systemPath: String,
|
|
20
|
+
context: Object,
|
|
21
|
+
min: Number,
|
|
22
|
+
disabled: Boolean,
|
|
23
|
+
primaryColor: String,
|
|
24
|
+
secondaryColor: String,
|
|
25
|
+
editMode: Boolean,
|
|
26
|
+
attributeStyle: String, // plain or box
|
|
27
|
+
roll: Function,
|
|
28
|
+
hasRoll: Boolean,
|
|
29
|
+
// Icon shown in the click overlay. Defaults to a die for roll: handlers; function: handlers
|
|
30
|
+
// pass the attribute's own icon so the affordance matches what the click actually does.
|
|
31
|
+
clickIcon: { type: String, default: "fa-solid fa-dice" }
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const document = inject("rawDocument");
|
|
35
|
+
|
|
36
|
+
const value = computed({
|
|
37
|
+
get: () => foundry.utils.getProperty(props.context, props.systemPath),
|
|
38
|
+
set: (newValue) => foundry.utils.setProperty(props.context, props.systemPath, newValue)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Vuetify's up/down stepper buttons update the model without firing a
|
|
42
|
+
// native change event, so Foundry's submitOnChange form handler never
|
|
43
|
+
// persists them. When the value changes while focus is NOT on a text
|
|
44
|
+
// input (i.e. a stepper click, not typing), persist directly. Typing
|
|
45
|
+
// still persists via the input's native change on blur/enter.
|
|
46
|
+
// ('document' is the injected Foundry document; DOM access uses window.)
|
|
47
|
+
const persistOnStep = (path, newValue) => {
|
|
48
|
+
if (document && window.document.activeElement?.tagName !== 'INPUT') {
|
|
49
|
+
document.update({ [path]: newValue });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const getStyle = computed(() => {
|
|
54
|
+
const p = props.primaryColor || "#92aed9";
|
|
55
|
+
|
|
56
|
+
// Get either black or white text color based on the primary color brightness
|
|
57
|
+
const brightness = (parseInt(p.slice(1, 3), 16) * 299 + parseInt(p.slice(3, 5), 16) * 587 + parseInt(p.slice(5, 7), 16) * 114) / 1000;
|
|
58
|
+
let textColor = "#ffffff"; // Default to white text
|
|
59
|
+
if (brightness > 128) {
|
|
60
|
+
textColor = "#000000"; // Use black text for brighter colors
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
backgroundColor: p,
|
|
65
|
+
color: textColor,
|
|
66
|
+
borderColor: p
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const getLabel = computed(() => {
|
|
71
|
+
const localized = game.i18n.localize(props.label);
|
|
72
|
+
if (props.icon) {
|
|
73
|
+
return \`<i class="fa-solid \${props.icon}"></i> \${localized}\`;
|
|
74
|
+
}
|
|
75
|
+
return localized;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const isHovering = ref(false);
|
|
79
|
+
|
|
80
|
+
const handleRoll = () => {
|
|
81
|
+
if (props.roll) {
|
|
82
|
+
props.roll();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<template>
|
|
88
|
+
<div class="dice-container"
|
|
89
|
+
@mouseenter="isHovering = true"
|
|
90
|
+
@mouseleave="isHovering = false"
|
|
91
|
+
>
|
|
92
|
+
<div v-if="attributeStyle == 'plain'">
|
|
93
|
+
<v-number-input v-if="attributeStyle == 'plain' && editMode" :model-value="value" @update:model-value="(v) => { value = v; persistOnStep(systemPath, v); }" :name="systemPath" :min="props.min" :disabled="disabled" type="number" variant="outlined" density="compact" :hide-details="true" data-tooltip="Value">
|
|
94
|
+
<template #label>
|
|
95
|
+
<span v-html="getLabel" />
|
|
96
|
+
</template>
|
|
97
|
+
</v-number-input>
|
|
98
|
+
<v-number-input v-if="attributeStyle == 'plain' && !editMode" :model-value="mod" :name="systemPath" :disabled="true" type="number" controlVariant="hidden" variant="outlined" density="compact" :hide-details="true" data-tooltip="Mod">
|
|
99
|
+
<template #label>
|
|
100
|
+
<span v-html="getLabel" />
|
|
101
|
+
</template>
|
|
102
|
+
</v-number-input>
|
|
103
|
+
<!-- Overlay with dice icon - appears on hover when roll function is available -->
|
|
104
|
+
<div
|
|
105
|
+
v-if="hasRoll && isHovering && !editMode"
|
|
106
|
+
class="dice-overlay plain"
|
|
107
|
+
@click="handleRoll"
|
|
108
|
+
>
|
|
109
|
+
<v-btn
|
|
110
|
+
icon
|
|
111
|
+
size="x-small"
|
|
112
|
+
:color="primaryColor"
|
|
113
|
+
class="dice-roll-btn"
|
|
114
|
+
variant="elevated"
|
|
115
|
+
elevation="4"
|
|
116
|
+
>
|
|
117
|
+
<v-icon size="small" :icon="clickIcon"></v-icon>
|
|
118
|
+
</v-btn>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<v-container
|
|
122
|
+
:class="['isdl-property', 'attributeExp', { 'no-mod': !hasMod }]"
|
|
123
|
+
v-if="attributeStyle == 'box'"
|
|
124
|
+
|
|
125
|
+
>
|
|
126
|
+
<v-label :style="getStyle"><v-icon v-if="icon" size="x-small" :icon="icon" style="padding-right: 0.5rem;"></v-icon>{{ game.i18n.localize(label) }}</v-label>
|
|
127
|
+
<div class="mod" v-if="hasMod">{{ mod }}</div>
|
|
128
|
+
<v-number-input :model-value="value" @update:model-value="(v) => { value = v; persistOnStep(systemPath, v); }" inset :min="props.min" :disabled="disabled" :name="systemPath" :controlVariant="disabled ? 'hidden' : 'split'" :step="1" type="number" variant="outlined" density="compact" :hide-details="true" :tile="true"></v-number-input>
|
|
129
|
+
<!-- Overlay with dice icon - appears on hover when roll function is available -->
|
|
130
|
+
<div
|
|
131
|
+
v-if="hasRoll && isHovering && !editMode"
|
|
132
|
+
class="dice-overlay"
|
|
133
|
+
@click="handleRoll"
|
|
134
|
+
>
|
|
135
|
+
<v-btn
|
|
136
|
+
icon
|
|
137
|
+
:color="primaryColor"
|
|
138
|
+
class="dice-roll-btn"
|
|
139
|
+
variant="elevated"
|
|
140
|
+
elevation="4"
|
|
141
|
+
>
|
|
142
|
+
<v-icon :icon="clickIcon"></v-icon>
|
|
143
|
+
</v-btn>
|
|
144
|
+
</div>
|
|
145
|
+
</v-container>
|
|
146
|
+
</div>
|
|
147
|
+
</template>
|
|
148
148
|
`;
|
|
149
149
|
fs.writeFileSync(generatedFilePath, toString(fileNode));
|
|
150
150
|
}
|