intelligent-system-design-language 0.3.34 → 0.4.1
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/feedback-sweep.json +2 -2
- package/out/cli/cli-util.js +9 -3
- package/out/cli/cli-util.js.map +1 -1
- package/out/cli/components/derived-data-generator.js +45 -7
- package/out/cli/components/derived-data-generator.js.map +1 -1
- package/out/cli/components/dev-module-generator.js +399 -0
- package/out/cli/components/dev-module-generator.js.map +1 -0
- package/out/cli/components/method-generator.js +68 -7
- package/out/cli/components/method-generator.js.map +1 -1
- package/out/cli/components/utils.js +1 -1
- package/out/cli/components/utils.js.map +1 -1
- package/out/cli/components/vue/base-components/vue-string-choices.js +11 -4
- package/out/cli/components/vue/base-components/vue-string-choices.js.map +1 -1
- package/out/cli/generator.js +46 -42
- package/out/cli/generator.js.map +1 -1
- package/out/extension/main.cjs +713 -560
- package/out/extension/main.cjs.map +4 -4
- package/out/extension/main.js +2 -2
- package/out/extension/main.js.map +1 -1
- package/out/extension/package.json +1 -1
- package/out/language/generated/ast.js +13 -4
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.js +106 -32
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/intelligent-system-design-language-module.js +3 -1
- package/out/language/intelligent-system-design-language-module.js.map +1 -1
- package/out/language/intelligent-system-design-language-validator.js +20 -7
- package/out/language/intelligent-system-design-language-validator.js.map +1 -1
- package/out/language/isdl-completion-provider.js +32 -0
- package/out/language/isdl-completion-provider.js.map +1 -0
- package/out/language/isdl-docs.js +106 -0
- package/out/language/isdl-docs.js.map +1 -0
- package/out/language/isdl-hover-provider.js +4 -54
- package/out/language/isdl-hover-provider.js.map +1 -1
- package/out/language/isdl-parser-error-message-provider.js +6 -1
- package/out/language/isdl-parser-error-message-provider.js.map +1 -1
- package/out/language/main.cjs +636 -483
- package/out/language/main.cjs.map +4 -4
- package/out/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { expandToNode, toString } from 'langium/generate';
|
|
4
|
+
import { isConfigExpression, isActor, isItem } from '../../language/generated/ast.js';
|
|
5
|
+
export function generateDevModule(entry, id, devDest) {
|
|
6
|
+
var _a, _b, _c, _d;
|
|
7
|
+
const scriptsDir = path.join(devDest, 'scripts');
|
|
8
|
+
if (!fs.existsSync(scriptsDir)) {
|
|
9
|
+
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
const title = (_b = (_a = entry.config.body.find(x => isConfigExpression(x) && x.type === 'label')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : id;
|
|
12
|
+
const author = (_d = (_c = entry.config.body.find(x => isConfigExpression(x) && x.type === 'author')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '';
|
|
13
|
+
// Foundry fires `render${ClassName}` — for our generated sheets that's `render<Doc>VueSheet`
|
|
14
|
+
// (e.g. renderHeroVueSheet), NOT the generic renderActorSheet. Hook the real class names so
|
|
15
|
+
// the dev overlays actually attach.
|
|
16
|
+
const sheetHooks = entry.documents
|
|
17
|
+
.filter(d => isActor(d) || isItem(d))
|
|
18
|
+
.map(d => `render${d.name}VueSheet`);
|
|
19
|
+
generateModuleJson(id, title, author, devDest);
|
|
20
|
+
generateDevTools(id, title, devDest, sheetHooks);
|
|
21
|
+
}
|
|
22
|
+
function generateModuleJson(id, title, author, devDest) {
|
|
23
|
+
const filePath = path.join(devDest, 'module.json');
|
|
24
|
+
const fileNode = expandToNode `
|
|
25
|
+
{
|
|
26
|
+
"id": "${id}-dev",
|
|
27
|
+
"title": "${title} — Dev Tools",
|
|
28
|
+
"description": "Developer companion module for ${title}. Install locally; never distribute to players.",
|
|
29
|
+
"version": "dev",
|
|
30
|
+
"compatibility": {
|
|
31
|
+
"minimum": 12,
|
|
32
|
+
"verified": 14
|
|
33
|
+
},
|
|
34
|
+
"authors": [
|
|
35
|
+
{ "name": "${author}" }
|
|
36
|
+
],
|
|
37
|
+
"esmodules": [
|
|
38
|
+
"scripts/dev-tools.mjs"
|
|
39
|
+
],
|
|
40
|
+
"relationships": {
|
|
41
|
+
"systems": [
|
|
42
|
+
{
|
|
43
|
+
"id": "${id}",
|
|
44
|
+
"type": "system",
|
|
45
|
+
"reason": "Dev tools require the ${title} system to be active"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`.appendNewLineIfNotEmpty();
|
|
51
|
+
fs.writeFileSync(filePath, toString(fileNode));
|
|
52
|
+
}
|
|
53
|
+
function generateDevTools(id, title, devDest, sheetHooks) {
|
|
54
|
+
const filePath = path.join(devDest, 'scripts', 'dev-tools.mjs');
|
|
55
|
+
const allRenderHooks = ['renderActorSheet', 'renderActorSheetV2', 'renderItemSheet', 'renderItemSheetV2', ...sheetHooks];
|
|
56
|
+
const fileNode = expandToNode `
|
|
57
|
+
// ${id}-dev — developer companion for ${title}
|
|
58
|
+
// Regenerated on each build. Customise ${id}-custom.mjs instead.
|
|
59
|
+
|
|
60
|
+
const MODULE_ID = "${id}-dev";
|
|
61
|
+
|
|
62
|
+
// ─── Vue DevTools ───────────────────────────────────────────────────
|
|
63
|
+
// Only enable if the Vue DevTools extension is already installed —
|
|
64
|
+
// creating a stub hook causes Vue to call emit() on a no-op object and break.
|
|
65
|
+
if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
|
|
66
|
+
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Tooltip styles ─────────────────────────────────────────────────
|
|
70
|
+
{
|
|
71
|
+
const style = document.createElement("style");
|
|
72
|
+
style.textContent = \`
|
|
73
|
+
.isdl-dev-tooltip {
|
|
74
|
+
position: fixed;
|
|
75
|
+
background: #1a1a2e;
|
|
76
|
+
border: 1px solid #4a9eff;
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
padding: 6px 8px;
|
|
79
|
+
font-family: monospace;
|
|
80
|
+
font-size: 11px;
|
|
81
|
+
color: #e0e0e0;
|
|
82
|
+
box-shadow: 0 2px 8px rgba(0,0,0,.6);
|
|
83
|
+
pointer-events: none;
|
|
84
|
+
min-width: 220px;
|
|
85
|
+
z-index: 10000;
|
|
86
|
+
}
|
|
87
|
+
.isdl-dev-section-header {
|
|
88
|
+
font-size: 10px;
|
|
89
|
+
font-weight: bold;
|
|
90
|
+
color: #c0d8f0;
|
|
91
|
+
border-bottom: 1px solid #2a3a5a;
|
|
92
|
+
padding-bottom: 3px;
|
|
93
|
+
margin-bottom: 4px;
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
gap: 5px;
|
|
97
|
+
}
|
|
98
|
+
.isdl-dev-section-hint {
|
|
99
|
+
font-weight: normal;
|
|
100
|
+
font-size: 9px;
|
|
101
|
+
color: #6a8aaa;
|
|
102
|
+
margin-left: auto;
|
|
103
|
+
}
|
|
104
|
+
.isdl-dev-label {
|
|
105
|
+
font-size: 9px;
|
|
106
|
+
text-transform: uppercase;
|
|
107
|
+
letter-spacing: 0.05em;
|
|
108
|
+
color: #6a8aaa;
|
|
109
|
+
margin-top: 5px;
|
|
110
|
+
margin-bottom: 1px;
|
|
111
|
+
}
|
|
112
|
+
.isdl-dev-label:first-child { margin-top: 0; }
|
|
113
|
+
.isdl-dev-row {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 6px;
|
|
117
|
+
padding: 2px 0;
|
|
118
|
+
}
|
|
119
|
+
.isdl-dev-row i { color: #4a9eff; width: 14px; text-align: center; }
|
|
120
|
+
.isdl-dev-row code { flex: 1; font-size: 10px; color: #a0cfff; word-break: break-all; }
|
|
121
|
+
.isdl-dev-copy {
|
|
122
|
+
background: none;
|
|
123
|
+
border: 1px solid #4a9eff;
|
|
124
|
+
border-radius: 2px;
|
|
125
|
+
color: #4a9eff;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
padding: 1px 5px;
|
|
128
|
+
font-size: 10px;
|
|
129
|
+
line-height: 1.4;
|
|
130
|
+
}
|
|
131
|
+
.isdl-dev-copy:hover { background: #4a9eff; color: #1a1a2e; }
|
|
132
|
+
.isdl-pick-mode-btn { opacity: 0.4; transition: opacity 0.15s, color 0.15s; }
|
|
133
|
+
.isdl-pick-mode-btn.isdl-pick-active { opacity: 1; color: #f6a623 !important; }
|
|
134
|
+
.isdl-dev-tooltip.isdl-dev-locked { border-color: #f6a623; box-shadow: 0 4px 16px rgba(0,0,0,.8); pointer-events: auto; }
|
|
135
|
+
.isdl-dev-tip-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; padding-bottom: 4px; border-bottom: 1px solid #2a3a5a; }
|
|
136
|
+
.isdl-dev-tip-title { font-size: 10px; font-weight: bold; color: #f6a623; letter-spacing: 0.05em; }
|
|
137
|
+
.isdl-dev-dismiss { background: none; border: 1px solid #f6a623; border-radius: 2px; color: #f6a623; cursor: pointer; padding: 0 5px; font-size: 11px; line-height: 1.6; }
|
|
138
|
+
.isdl-dev-dismiss:hover { background: #f6a623; color: #1a1a2e; }
|
|
139
|
+
.isdl-inspector { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
|
|
140
|
+
.isdl-inspector-hint { font-size: 12px; color: #888; margin: 0; }
|
|
141
|
+
.isdl-inspector-hint code { font-size: 11px; background: #eee; padding: 1px 3px; border-radius: 2px; }
|
|
142
|
+
.isdl-inspector-copy-all { align-self: flex-end; cursor: pointer; }
|
|
143
|
+
.isdl-inspector-pre {
|
|
144
|
+
max-height: 420px;
|
|
145
|
+
overflow: auto;
|
|
146
|
+
font-size: 11px;
|
|
147
|
+
font-family: monospace;
|
|
148
|
+
background: #1a1a2e;
|
|
149
|
+
color: #a0cfff;
|
|
150
|
+
padding: 10px;
|
|
151
|
+
border-radius: 4px;
|
|
152
|
+
margin: 0;
|
|
153
|
+
white-space: pre;
|
|
154
|
+
line-height: 1.5;
|
|
155
|
+
}
|
|
156
|
+
\`;
|
|
157
|
+
document.head.appendChild(style);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Pick-mode field inspector ──────────────────────────────────────
|
|
161
|
+
let _pickMode = false;
|
|
162
|
+
let _hoverTip = null;
|
|
163
|
+
let _lockedTip = null;
|
|
164
|
+
|
|
165
|
+
function _setPickMode(enabled) {
|
|
166
|
+
_pickMode = enabled;
|
|
167
|
+
if (!enabled) { _clearLockedTip(); _clearHoverTip(); }
|
|
168
|
+
document.querySelectorAll(".isdl-pick-mode-btn")
|
|
169
|
+
.forEach(b => b.classList.toggle("isdl-pick-active", enabled));
|
|
170
|
+
console.log(\`[\${MODULE_ID}] pick mode \${enabled ? "ON — hover a field, click to lock" : "off"}\`);
|
|
171
|
+
}
|
|
172
|
+
function _clearHoverTip() { _hoverTip?.remove(); _hoverTip = null; }
|
|
173
|
+
function _clearLockedTip() { _lockedTip?.remove(); _lockedTip = null; }
|
|
174
|
+
|
|
175
|
+
function _makeRow(iconClass, label, value) {
|
|
176
|
+
return \`
|
|
177
|
+
<div class="isdl-dev-label">\${label}</div>
|
|
178
|
+
<div class="isdl-dev-row">
|
|
179
|
+
<i class="\${iconClass}"></i>
|
|
180
|
+
<code>\${value}</code>
|
|
181
|
+
<button type="button" class="isdl-dev-copy" data-copy="\${value}" title="Copy">
|
|
182
|
+
<i class="fa-solid fa-copy"></i>
|
|
183
|
+
</button>
|
|
184
|
+
</div>
|
|
185
|
+
\`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function _buildTipBody(systemPath, typeSelector, nameSelector) {
|
|
189
|
+
return \`<div class="isdl-dev-section-header"><i class="fa-solid fa-database"></i> Data Path<span class="isdl-dev-section-hint">for macros & scripts</span></div>\`
|
|
190
|
+
+ _makeRow("fa-solid fa-folder-open", "system path", systemPath)
|
|
191
|
+
+ \`<div class="isdl-dev-section-header" style="margin-top:8px"><i class="fa-solid fa-paintbrush"></i> CSS Selectors<span class="isdl-dev-section-hint">for custom.css</span></div>\`
|
|
192
|
+
+ (typeSelector ? _makeRow("fa-solid fa-layer-group", "all fields of this type", typeSelector) : "")
|
|
193
|
+
+ (nameSelector ? _makeRow("fa-solid fa-crosshairs", "just this field", nameSelector) : "");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _positionTip(tip, el) {
|
|
197
|
+
const rect = el.getBoundingClientRect();
|
|
198
|
+
tip.style.top = (rect.bottom + 4) + "px";
|
|
199
|
+
tip.style.left = rect.left + "px";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function _showHoverTip(el, systemPath, typeSelector, nameSelector) {
|
|
203
|
+
_clearHoverTip();
|
|
204
|
+
if (!_pickMode || _lockedTip) return;
|
|
205
|
+
const tip = document.createElement("div");
|
|
206
|
+
tip.className = "isdl-dev-tooltip";
|
|
207
|
+
tip.innerHTML = _buildTipBody(systemPath, typeSelector, nameSelector);
|
|
208
|
+
_positionTip(tip, el);
|
|
209
|
+
document.body.appendChild(tip);
|
|
210
|
+
_hoverTip = tip;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function _lockTip(el, systemPath, typeSelector, nameSelector) {
|
|
214
|
+
_clearHoverTip();
|
|
215
|
+
_clearLockedTip();
|
|
216
|
+
const tip = document.createElement("div");
|
|
217
|
+
tip.className = "isdl-dev-tooltip isdl-dev-locked";
|
|
218
|
+
tip.innerHTML =
|
|
219
|
+
\`<div class="isdl-dev-tip-header"><span class="isdl-dev-tip-title"><i class="fa-solid fa-crosshairs"></i> FIELD INFO</span><button type="button" class="isdl-dev-dismiss" title="Dismiss">×</button></div>\`
|
|
220
|
+
+ _buildTipBody(systemPath, typeSelector, nameSelector);
|
|
221
|
+
tip.querySelector(".isdl-dev-dismiss").addEventListener("click", e => { e.stopPropagation(); _clearLockedTip(); });
|
|
222
|
+
tip.querySelectorAll(".isdl-dev-copy").forEach(btn => {
|
|
223
|
+
btn.addEventListener("click", e => {
|
|
224
|
+
e.stopPropagation();
|
|
225
|
+
navigator.clipboard.writeText(btn.dataset.copy).then(() => {
|
|
226
|
+
const icon = btn.querySelector("i");
|
|
227
|
+
icon.className = "fa-solid fa-check";
|
|
228
|
+
setTimeout(() => icon.className = "fa-solid fa-copy", 1500);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
_positionTip(tip, el);
|
|
233
|
+
document.body.appendChild(tip);
|
|
234
|
+
_lockedTip = tip;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Delegated field listeners — attached once on document, so they work for Vue-mounted
|
|
238
|
+
// fields regardless of render timing and survive sheet re-renders.
|
|
239
|
+
let _pickWired = false;
|
|
240
|
+
function _wirePickDelegation() {
|
|
241
|
+
if (_pickWired) return;
|
|
242
|
+
_pickWired = true;
|
|
243
|
+
let _curField = null;
|
|
244
|
+
const _fieldAt = t => {
|
|
245
|
+
const el = t?.closest?.("[class*='isdl-']");
|
|
246
|
+
if (!el) return null;
|
|
247
|
+
const input = el.querySelector("input[name^='system.'], select[name^='system.'], textarea[name^='system.']");
|
|
248
|
+
if (!input) return null;
|
|
249
|
+
const { typeSelector, nameSelector } = _buildCssSelectors(el);
|
|
250
|
+
return { el, systemPath: input.getAttribute("name"), typeSelector, nameSelector };
|
|
251
|
+
};
|
|
252
|
+
document.addEventListener("mouseover", e => {
|
|
253
|
+
if (!_pickMode) return;
|
|
254
|
+
const f = _fieldAt(e.target);
|
|
255
|
+
if (f && f.el !== _curField) { _curField = f.el; _showHoverTip(f.el, f.systemPath, f.typeSelector, f.nameSelector); }
|
|
256
|
+
else if (!f && _curField) { _curField = null; _clearHoverTip(); }
|
|
257
|
+
});
|
|
258
|
+
document.addEventListener("click", e => {
|
|
259
|
+
if (!_pickMode) return;
|
|
260
|
+
const f = _fieldAt(e.target);
|
|
261
|
+
if (f) { e.stopPropagation(); _lockTip(f.el, f.systemPath, f.typeSelector, f.nameSelector); }
|
|
262
|
+
}, true);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function _buildCssSelectors(el) {
|
|
266
|
+
const classes = [...el.classList];
|
|
267
|
+
// e.g. isdl-field-brawn → "Just this field"
|
|
268
|
+
const nameClass = classes.find(c => /^isdl-field-[a-z]/.test(c));
|
|
269
|
+
// e.g. isdl-number, isdl-attribute — the field type, skip field/visibility/name classes
|
|
270
|
+
const typeClass = classes.find(c =>
|
|
271
|
+
c.startsWith("isdl-") &&
|
|
272
|
+
c !== "isdl-field" &&
|
|
273
|
+
!c.startsWith("isdl-visibility-") &&
|
|
274
|
+
!c.startsWith("isdl-field-")
|
|
275
|
+
);
|
|
276
|
+
return {
|
|
277
|
+
typeSelector: typeClass ? \`.\${typeClass}\` : null,
|
|
278
|
+
nameSelector: nameClass ? \`.\${nameClass}\` : null
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ─── System data inspector ──────────────────────────────────────────
|
|
283
|
+
function _openInspector(doc) {
|
|
284
|
+
const data = doc.system?.toObject?.() ?? doc.system ?? {};
|
|
285
|
+
const json = JSON.stringify(data, null, 2);
|
|
286
|
+
new Dialog({
|
|
287
|
+
title: \`System Data — \${doc.name}\`,
|
|
288
|
+
content: \`
|
|
289
|
+
<div class="isdl-inspector">
|
|
290
|
+
<p class="isdl-inspector-hint">
|
|
291
|
+
<i class="fa-solid fa-circle-info"></i>
|
|
292
|
+
Everything stored in <code>actor.system</code> for this document.
|
|
293
|
+
Use these paths in macros, roll formulas, and scripts.
|
|
294
|
+
</p>
|
|
295
|
+
<button type="button" class="isdl-inspector-copy-all">
|
|
296
|
+
<i class="fa-solid fa-copy"></i> Copy all
|
|
297
|
+
</button>
|
|
298
|
+
<pre class="isdl-inspector-pre">\${json}</pre>
|
|
299
|
+
</div>
|
|
300
|
+
\`,
|
|
301
|
+
buttons: { close: { label: "Close" } },
|
|
302
|
+
render: html => {
|
|
303
|
+
const root = html instanceof HTMLElement ? html : html?.[0];
|
|
304
|
+
root?.querySelector(".isdl-inspector-copy-all")?.addEventListener("click", () => {
|
|
305
|
+
navigator.clipboard.writeText(json).then(() => {
|
|
306
|
+
const btn = root.querySelector(".isdl-inspector-copy-all");
|
|
307
|
+
if (btn) {
|
|
308
|
+
btn.innerHTML = \`<i class="fa-solid fa-check"></i> Copied!\`;
|
|
309
|
+
setTimeout(() => { btn.innerHTML = \`<i class="fa-solid fa-copy"></i> Copy all\`; }, 1500);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}).render(true);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ─── Sheet overlay ──────────────────────────────────────────────────
|
|
318
|
+
function _addHeaderControl(header, btn) {
|
|
319
|
+
const closeBtn = header.querySelector(".close, [data-action='close'], [aria-label='Close']");
|
|
320
|
+
if (closeBtn) closeBtn.before(btn); else header.append(btn);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function _activateOverlays(doc, html) {
|
|
324
|
+
const root = html instanceof HTMLElement ? html : html?.[0];
|
|
325
|
+
if (!root) return;
|
|
326
|
+
|
|
327
|
+
// v14: header is a direct child of the form (root IS the sheet element)
|
|
328
|
+
// v12/v13: header is a sibling of root inside .window-app
|
|
329
|
+
const header = root.querySelector(".window-header, .window-controls")
|
|
330
|
+
?? root.closest(".window-app, .application")?.querySelector(".window-header, .window-controls");
|
|
331
|
+
|
|
332
|
+
// System Data Viewer button — icon as an <i> child (not a class on the button),
|
|
333
|
+
// inserted before the close control so it sits on the right.
|
|
334
|
+
if (header && !header.querySelector(".isdl-inspector-btn")) {
|
|
335
|
+
const btn = document.createElement("button");
|
|
336
|
+
btn.type = "button";
|
|
337
|
+
btn.className = "isdl-inspector-btn header-control";
|
|
338
|
+
btn.innerHTML = '<i class="fa-solid fa-magnifying-glass"></i>';
|
|
339
|
+
btn.setAttribute("aria-label", "Inspect System Data");
|
|
340
|
+
btn.setAttribute("data-tooltip", "Inspect System Data");
|
|
341
|
+
btn.addEventListener("click", e => { e.preventDefault(); _openInspector(doc); });
|
|
342
|
+
_addHeaderControl(header, btn);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Pick Mode button — toggles the field path/CSS picker: hover a field to preview
|
|
346
|
+
// its data path + CSS selectors, click to lock the tooltip.
|
|
347
|
+
if (header && !header.querySelector(".isdl-pick-mode-btn")) {
|
|
348
|
+
const btn = document.createElement("button");
|
|
349
|
+
btn.type = "button";
|
|
350
|
+
btn.className = "isdl-pick-mode-btn header-control";
|
|
351
|
+
btn.innerHTML = '<i class="fa-solid fa-crosshairs"></i>';
|
|
352
|
+
btn.setAttribute("aria-label", "Pick Mode");
|
|
353
|
+
btn.setAttribute("data-tooltip", "Pick Mode — hover a field to preview its data path & CSS, click to lock the tooltip");
|
|
354
|
+
btn.addEventListener("click", e => { e.preventDefault(); _setPickMode(!_pickMode); });
|
|
355
|
+
_addHeaderControl(header, btn);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Field hover/click is handled by a single delegated listener (wired once below),
|
|
359
|
+
// so it works for Vue-mounted fields regardless of render timing.
|
|
360
|
+
_wirePickDelegation();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ─── Verbose logging ────────────────────────────────────────────────
|
|
364
|
+
Hooks.once("init", () => {
|
|
365
|
+
console.group(\`[\${MODULE_ID}] init\`);
|
|
366
|
+
console.log("system:", game.system);
|
|
367
|
+
console.groupEnd();
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
Hooks.once("ready", () => {
|
|
371
|
+
console.group(\`[\${MODULE_ID}] ready\`);
|
|
372
|
+
console.log("actors:", game.actors?.size ?? 0);
|
|
373
|
+
console.log("items:", game.items?.size ?? 0);
|
|
374
|
+
console.groupEnd();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// ApplicationV2 (Foundry v13+) fires renderActorSheetV2 / renderItemSheetV2
|
|
378
|
+
// ApplicationV1 fires the legacy renderActorSheet / renderItemSheet
|
|
379
|
+
// Foundry fires render<ClassName>; our sheets are <Doc>VueSheet. The generic
|
|
380
|
+
// ActorSheet/ItemSheet names are kept as a fallback for other Foundry versions.
|
|
381
|
+
for (const hook of ${JSON.stringify(allRenderHooks)}) {
|
|
382
|
+
Hooks.on(hook, (app, html) => {
|
|
383
|
+
const doc = app.actor ?? app.item ?? app.document;
|
|
384
|
+
_activateOverlays(doc, html instanceof HTMLElement ? html : html?.[0] ?? app.element);
|
|
385
|
+
console.log(\`[\${MODULE_ID}] \${hook}\`, doc?.name);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
Hooks.on("preUpdateActor", (actor, changes) => {
|
|
390
|
+
console.log(\`[\${MODULE_ID}] preUpdateActor\`, actor.name, changes);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
Hooks.on("preUpdateItem", (item, changes) => {
|
|
394
|
+
console.log(\`[\${MODULE_ID}] preUpdateItem\`, item.name, changes);
|
|
395
|
+
});
|
|
396
|
+
`.appendNewLineIfNotEmpty();
|
|
397
|
+
fs.writeFileSync(filePath, toString(fileNode));
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=dev-module-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-module-generator.js","sourceRoot":"","sources":["../../../src/cli/components/dev-module-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -66,6 +66,10 @@ export function translateDiceParts(expression, noLabels = false) {
|
|
|
66
66
|
`;
|
|
67
67
|
}
|
|
68
68
|
if (isParentAccess(expression)) {
|
|
69
|
+
if (expression.nameAccess) {
|
|
70
|
+
const suffix = noLabels ? "" : "[Name]";
|
|
71
|
+
return expandToNode `"@name${suffix}"`;
|
|
72
|
+
}
|
|
69
73
|
let path = (_j = (_h = (_g = expression.property) === null || _g === void 0 ? void 0 : _g.ref) === null || _h === void 0 ? void 0 : _h.name) !== null && _j !== void 0 ? _j : "";
|
|
70
74
|
let label = humanize((_m = (_l = (_k = expression.property) === null || _k === void 0 ? void 0 : _k.ref) === null || _l === void 0 ? void 0 : _l.name) !== null && _m !== void 0 ? _m : "");
|
|
71
75
|
const document = getParentDocument(expression);
|
|
@@ -92,6 +96,10 @@ export function translateDiceParts(expression, noLabels = false) {
|
|
|
92
96
|
`;
|
|
93
97
|
}
|
|
94
98
|
if (isTargetAccess(expression)) {
|
|
99
|
+
if (expression.nameAccess) {
|
|
100
|
+
const suffix = noLabels ? "" : "[Name]";
|
|
101
|
+
return expandToNode `"@name${suffix}"`;
|
|
102
|
+
}
|
|
95
103
|
let path = (_w = (_v = (_u = expression.property) === null || _u === void 0 ? void 0 : _u.ref) === null || _v === void 0 ? void 0 : _v.name) !== null && _w !== void 0 ? _w : "";
|
|
96
104
|
let label = humanize((_z = (_y = (_x = expression.property) === null || _x === void 0 ? void 0 : _x.ref) === null || _y === void 0 ? void 0 : _y.name) !== null && _z !== void 0 ? _z : "");
|
|
97
105
|
const document = getTargetDocument(expression);
|
|
@@ -222,9 +230,13 @@ function translateDisplayFormula(expression) {
|
|
|
222
230
|
return label.trim();
|
|
223
231
|
}
|
|
224
232
|
if (isParentAccess(expression)) {
|
|
233
|
+
if (expression.nameAccess)
|
|
234
|
+
return "Name";
|
|
225
235
|
return humanize((_m = (_l = (_k = expression.property) === null || _k === void 0 ? void 0 : _k.ref) === null || _l === void 0 ? void 0 : _l.name) !== null && _m !== void 0 ? _m : "");
|
|
226
236
|
}
|
|
227
237
|
if (isTargetAccess(expression)) {
|
|
238
|
+
if (expression.nameAccess)
|
|
239
|
+
return "Name";
|
|
228
240
|
return humanize((_q = (_p = (_o = expression.property) === null || _o === void 0 ? void 0 : _o.ref) === null || _p === void 0 ? void 0 : _p.name) !== null && _q !== void 0 ? _q : "");
|
|
229
241
|
}
|
|
230
242
|
if (isFleetingAccess(expression)) {
|
|
@@ -236,6 +248,11 @@ export function translateDiceData(expression, entry, id, preDerived, generatingP
|
|
|
236
248
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61;
|
|
237
249
|
console.log("Translating Dice Data: ", expression.$type);
|
|
238
250
|
if (isParentAccess(expression)) {
|
|
251
|
+
if (expression.nameAccess) {
|
|
252
|
+
return expandToNode `
|
|
253
|
+
"name": context.object.parent?.name
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
239
256
|
let path = "context.object.parent.system";
|
|
240
257
|
let label = (_c = (_b = (_a = expression.property) === null || _a === void 0 ? void 0 : _a.ref) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : "";
|
|
241
258
|
console.log("Parent Access:", (_e = (_d = expression.property) === null || _d === void 0 ? void 0 : _d.ref) === null || _e === void 0 ? void 0 : _e.name);
|
|
@@ -257,6 +274,11 @@ export function translateDiceData(expression, entry, id, preDerived, generatingP
|
|
|
257
274
|
`;
|
|
258
275
|
}
|
|
259
276
|
if (isTargetAccess(expression)) {
|
|
277
|
+
if (expression.nameAccess) {
|
|
278
|
+
return expandToNode `
|
|
279
|
+
"name": context.target?.name
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
260
282
|
let path = "context.target.system";
|
|
261
283
|
let label = (_q = (_p = (_o = expression.property) === null || _o === void 0 ? void 0 : _o.ref) === null || _p === void 0 ? void 0 : _p.name) !== null && _q !== void 0 ? _q : "";
|
|
262
284
|
console.log("Target Access:", (_s = (_r = expression.property) === null || _r === void 0 ? void 0 : _r.ref) === null || _s === void 0 ? void 0 : _s.name);
|
|
@@ -823,11 +845,30 @@ export function translateExpression(entry, id, expression, preDerived = false, g
|
|
|
823
845
|
throw new Error("Reference expression has no reference");
|
|
824
846
|
}
|
|
825
847
|
console.log("Translating Reference Expression: " + accessPath);
|
|
826
|
-
//
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
848
|
+
// If this reference resolves to an `each` loop variable, the loop iterates over a
|
|
849
|
+
// collection of Foundry Documents (embedded items). Custom fields on a Document live
|
|
850
|
+
// under `.system` (e.g. `sk.Expertise` -> `sk.system.expertise`), so member access on
|
|
851
|
+
// the loop variable needs the `.system` hop. Built-in Document props like `.name` are
|
|
852
|
+
// handled separately below and must NOT get `.system`.
|
|
853
|
+
//
|
|
854
|
+
// Only the loop VARIABLE itself gets this treatment. Function/method parameters and
|
|
855
|
+
// other locals that merely appear inside the loop body (e.g. `SkillName`, `Expt`) are
|
|
856
|
+
// plain JS values and must be referenced by their bare name -- appending `.system` to a
|
|
857
|
+
// string param produced `SkillName.system` (undefined at runtime). See bug #123.
|
|
858
|
+
//
|
|
859
|
+
// Walk every enclosing `each` (not just the innermost) so a reference to an outer loop
|
|
860
|
+
// variable from within a nested loop still resolves correctly.
|
|
861
|
+
const refTarget0 = expression.val.ref;
|
|
862
|
+
if (isParameter(refTarget0)) {
|
|
863
|
+
let eachAncestor = AstUtils.getContainerOfType(expression.$container, isEach);
|
|
864
|
+
while (eachAncestor != undefined) {
|
|
865
|
+
if (eachAncestor.var === refTarget0) {
|
|
866
|
+
if (isAccess(eachAncestor.collection) || isParentAccess(eachAncestor.collection) || isTargetAccess(eachAncestor.collection)) {
|
|
867
|
+
accessPath = `${accessPath}.system`;
|
|
868
|
+
}
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
eachAncestor = AstUtils.getContainerOfType(eachAncestor.$container, isEach);
|
|
831
872
|
}
|
|
832
873
|
}
|
|
833
874
|
const refTargetIsRoll = isVariableExpression(expression.val.ref) && isRoll(expression.val.ref.value);
|
|
@@ -1173,6 +1214,11 @@ export function translateExpression(entry, id, expression, preDerived = false, g
|
|
|
1173
1214
|
}
|
|
1174
1215
|
if (isParentAccess(expression)) {
|
|
1175
1216
|
let path = "context.object.parent";
|
|
1217
|
+
if (expression.nameAccess) {
|
|
1218
|
+
return expandToNode `
|
|
1219
|
+
${path}?.name
|
|
1220
|
+
`;
|
|
1221
|
+
}
|
|
1176
1222
|
let systemPath = getSystemPath((_a = expression.property) === null || _a === void 0 ? void 0 : _a.ref, expression.subProperties, expression, true);
|
|
1177
1223
|
path = `${path}?.${systemPath}`;
|
|
1178
1224
|
console.log("Translating Parent Access Expression: ", path);
|
|
@@ -1182,6 +1228,11 @@ export function translateExpression(entry, id, expression, preDerived = false, g
|
|
|
1182
1228
|
}
|
|
1183
1229
|
if (isTargetAccess(expression)) {
|
|
1184
1230
|
let path = "context.target";
|
|
1231
|
+
if (expression.nameAccess) {
|
|
1232
|
+
return expandToNode `
|
|
1233
|
+
${path}?.name
|
|
1234
|
+
`;
|
|
1235
|
+
}
|
|
1185
1236
|
let systemPath = getSystemPath((_b = expression.property) === null || _b === void 0 ? void 0 : _b.ref, expression.subProperties, undefined, true);
|
|
1186
1237
|
path = `${path}?.${systemPath}`;
|
|
1187
1238
|
console.log("Translating Target Access Expression: ", path);
|
|
@@ -1353,15 +1404,25 @@ export function translateExpression(entry, id, expression, preDerived = false, g
|
|
|
1353
1404
|
`;
|
|
1354
1405
|
}
|
|
1355
1406
|
if (isParentAccess(expression)) {
|
|
1356
|
-
let systemPath = getSystemPath((_3 = expression.property) === null || _3 === void 0 ? void 0 : _3.ref, expression.subProperties, undefined, true);
|
|
1357
1407
|
const wide = expression.type == "wide" ? true : false;
|
|
1408
|
+
if (expression.nameAccess) {
|
|
1409
|
+
return expandToNode `
|
|
1410
|
+
{ isRoll: false, label: "Name", value: context.object.parent?.name, wide: ${wide}, hasValue: context.object.parent?.name != "" },
|
|
1411
|
+
`;
|
|
1412
|
+
}
|
|
1413
|
+
let systemPath = getSystemPath((_3 = expression.property) === null || _3 === void 0 ? void 0 : _3.ref, expression.subProperties, undefined, true);
|
|
1358
1414
|
return expandToNode `
|
|
1359
1415
|
{ isRoll: false, label: "${humanize((_6 = (_5 = (_4 = expression.property) === null || _4 === void 0 ? void 0 : _4.ref) === null || _5 === void 0 ? void 0 : _5.name) !== null && _6 !== void 0 ? _6 : "")}", value: context.object.parent?.${systemPath}, wide: ${wide}, hasValue: context.object.parent?.${systemPath} != "" },
|
|
1360
1416
|
`;
|
|
1361
1417
|
}
|
|
1362
1418
|
if (isTargetAccess(expression)) {
|
|
1363
|
-
let systemPath = getSystemPath((_7 = expression.property) === null || _7 === void 0 ? void 0 : _7.ref, expression.subProperties, undefined, true);
|
|
1364
1419
|
const wide = expression.type == "wide" ? true : false;
|
|
1420
|
+
if (expression.nameAccess) {
|
|
1421
|
+
return expandToNode `
|
|
1422
|
+
{ isRoll: false, label: "Name", value: context.target?.name, wide: ${wide}, hasValue: context.target?.name != "" },
|
|
1423
|
+
`;
|
|
1424
|
+
}
|
|
1425
|
+
let systemPath = getSystemPath((_7 = expression.property) === null || _7 === void 0 ? void 0 : _7.ref, expression.subProperties, undefined, true);
|
|
1365
1426
|
return expandToNode `
|
|
1366
1427
|
{ isRoll: false, label: "${humanize((_10 = (_9 = (_8 = expression.property) === null || _8 === void 0 ? void 0 : _8.ref) === null || _9 === void 0 ? void 0 : _9.name) !== null && _10 !== void 0 ? _10 : "")}", value: context.target?.${systemPath}, wide: ${wide}, hasValue: context.target?.${systemPath} != "" },
|
|
1367
1428
|
`;
|