@vttforge/cli 0.0.0 → 0.5.0

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 (67) hide show
  1. package/CHANGELOG.md +350 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -5
  4. package/dist/bin.d.mts +1 -0
  5. package/dist/bin.mjs +201 -0
  6. package/dist/bin.mjs.map +1 -0
  7. package/dist/index.d.mts +496 -0
  8. package/dist/index.mjs +2 -0
  9. package/dist/src-CWhUNM__.mjs +2443 -0
  10. package/dist/src-CWhUNM__.mjs.map +1 -0
  11. package/package.json +62 -9
  12. package/templates/module-js/.github/workflows/release.yml +92 -0
  13. package/templates/module-js/README.md +57 -0
  14. package/templates/module-js/_gitignore +12 -0
  15. package/templates/module-js/lang/en.json +11 -0
  16. package/templates/module-js/module.json +20 -0
  17. package/templates/module-js/package.json +24 -0
  18. package/templates/module-js/scripts/main.mjs +65 -0
  19. package/templates/module-js/styles/main.css +16 -0
  20. package/templates/module-js/vite.config.mjs +13 -0
  21. package/templates/module-ts/.github/workflows/release.yml +92 -0
  22. package/templates/module-ts/README.md +57 -0
  23. package/templates/module-ts/_gitignore +12 -0
  24. package/templates/module-ts/lang/en.json +11 -0
  25. package/templates/module-ts/module.json +20 -0
  26. package/templates/module-ts/package.json +26 -0
  27. package/templates/module-ts/scripts/foundry-globals.ts +23 -0
  28. package/templates/module-ts/scripts/main.ts +71 -0
  29. package/templates/module-ts/styles/main.css +16 -0
  30. package/templates/module-ts/tsconfig.json +18 -0
  31. package/templates/module-ts/vite.config.mjs +13 -0
  32. package/templates/system-js/.github/workflows/release.yml +92 -0
  33. package/templates/system-js/README.md +57 -0
  34. package/templates/system-js/_gitignore +12 -0
  35. package/templates/system-js/lang/en.json +55 -0
  36. package/templates/system-js/package.json +25 -0
  37. package/templates/system-js/scripts/data/character-data.mjs +79 -0
  38. package/templates/system-js/scripts/data/gear-data.mjs +37 -0
  39. package/templates/system-js/scripts/main.mjs +79 -0
  40. package/templates/system-js/scripts/migrations.mjs +43 -0
  41. package/templates/system-js/scripts/sheets/character-sheet.mjs +164 -0
  42. package/templates/system-js/scripts/sheets/gear-sheet.mjs +65 -0
  43. package/templates/system-js/styles/main.css +495 -0
  44. package/templates/system-js/system.json +44 -0
  45. package/templates/system-js/template.json +8 -0
  46. package/templates/system-js/templates/actor/character-sheet.hbs +168 -0
  47. package/templates/system-js/templates/item/gear-sheet.hbs +54 -0
  48. package/templates/system-js/vite.config.mjs +16 -0
  49. package/templates/system-ts/.github/workflows/release.yml +92 -0
  50. package/templates/system-ts/README.md +57 -0
  51. package/templates/system-ts/_gitignore +12 -0
  52. package/templates/system-ts/lang/en.json +55 -0
  53. package/templates/system-ts/package.json +27 -0
  54. package/templates/system-ts/scripts/data/character-data.ts +81 -0
  55. package/templates/system-ts/scripts/data/gear-data.ts +37 -0
  56. package/templates/system-ts/scripts/foundry-globals.ts +33 -0
  57. package/templates/system-ts/scripts/main.ts +82 -0
  58. package/templates/system-ts/scripts/migrations.ts +43 -0
  59. package/templates/system-ts/scripts/sheets/character-sheet.ts +202 -0
  60. package/templates/system-ts/scripts/sheets/gear-sheet.ts +68 -0
  61. package/templates/system-ts/styles/main.css +495 -0
  62. package/templates/system-ts/system.json +44 -0
  63. package/templates/system-ts/template.json +8 -0
  64. package/templates/system-ts/templates/actor/character-sheet.hbs +168 -0
  65. package/templates/system-ts/templates/item/gear-sheet.hbs +54 -0
  66. package/templates/system-ts/tsconfig.json +18 -0
  67. package/templates/system-ts/vite.config.mjs +16 -0
@@ -0,0 +1,495 @@
1
+ @import "@vttforge/styles";
2
+
3
+ /*
4
+ * {{ID}} styles — implements the .sh-* class system from the
5
+ * canonical Figma reference (character-sheet.jsx + Theme Explorations.html)
6
+ * on top of @vttforge/styles. Tokens come from the Forge theme; no other
7
+ * theme is hand-rolled here (consumers swap themes by overriding --vttf-*).
8
+ *
9
+ * Component CSS stays UNLAYERED so it wins over Foundry's
10
+ * @layer applications without specificity wars. Scoped under .{{ID}} so
11
+ * nothing leaks into core Foundry UI.
12
+ */
13
+
14
+ /* Opaque sheet bg — Foundry's ApplicationV2 default is transparent. */
15
+ .{{ID}}.sheet,
16
+ .{{ID}} .window-content {
17
+ background: var(--vttf-bg);
18
+ opacity: 1;
19
+ }
20
+
21
+ .{{ID}}.sheet {
22
+ color: var(--vttf-text);
23
+ font-family: var(--vttf-font-body, system-ui, sans-serif);
24
+ }
25
+
26
+ .{{ID}}.sheet form {
27
+ display: flex;
28
+ flex-direction: column;
29
+ min-height: 0;
30
+ height: 100%;
31
+ }
32
+
33
+ /* ════════ HEAD (.sh-head) ════════ */
34
+ .{{ID}} .sh-head {
35
+ display: grid;
36
+ grid-template-columns: 88px 1fr;
37
+ gap: var(--vttf-space-4);
38
+ padding: var(--vttf-space-4);
39
+ background: var(--vttf-surface);
40
+ border-bottom: 1px solid var(--vttf-border);
41
+ }
42
+
43
+ .{{ID}} .sh-portrait {
44
+ width: 88px;
45
+ height: 110px;
46
+ border-radius: var(--vttf-radius-md);
47
+ background: var(--vttf-surface-2);
48
+ object-fit: cover;
49
+ cursor: pointer;
50
+ }
51
+
52
+ .{{ID}} .sh-id {
53
+ display: flex;
54
+ flex-direction: column;
55
+ justify-content: space-between;
56
+ min-width: 0;
57
+ }
58
+
59
+ .{{ID}} .sh-id-top {
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 4px;
63
+ }
64
+
65
+ .{{ID}} .sh-name {
66
+ font-family: var(--vttf-font-display, var(--vttf-font-body));
67
+ font-size: 28px;
68
+ font-weight: 700;
69
+ letter-spacing: -0.02em;
70
+ color: var(--vttf-text);
71
+ background: transparent;
72
+ border: none;
73
+ border-bottom: 1px solid transparent;
74
+ padding: 0;
75
+ }
76
+
77
+ .{{ID}} .sh-name:focus {
78
+ border-bottom-color: var(--vttf-border-strong);
79
+ outline: none;
80
+ }
81
+
82
+ .{{ID}} .sh-sub {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ gap: var(--vttf-space-2);
86
+ font-family: var(--vttf-font-mono, monospace);
87
+ font-size: 11px;
88
+ letter-spacing: 0.12em;
89
+ text-transform: uppercase;
90
+ color: var(--vttf-text-muted);
91
+ }
92
+
93
+ .{{ID}} .sh-sub input {
94
+ width: 3.5rem;
95
+ text-align: center;
96
+ background: transparent;
97
+ border: 1px solid var(--vttf-border);
98
+ border-radius: var(--vttf-radius-sm);
99
+ color: var(--vttf-text);
100
+ font: inherit;
101
+ padding: 2px 6px;
102
+ }
103
+
104
+ .{{ID}} .sh-quick {
105
+ display: grid;
106
+ grid-template-columns: repeat(4, 1fr);
107
+ gap: var(--vttf-space-3);
108
+ }
109
+
110
+ .{{ID}} .sh-quick .q {
111
+ display: flex;
112
+ flex-direction: column;
113
+ align-items: center;
114
+ }
115
+
116
+ .{{ID}} .sh-quick .qv {
117
+ display: inline-flex;
118
+ align-items: baseline;
119
+ gap: 4px;
120
+ font-family: var(--vttf-font-display, var(--vttf-font-body));
121
+ font-size: 24px;
122
+ font-weight: 700;
123
+ line-height: 1.1;
124
+ color: var(--vttf-ember-glow);
125
+ }
126
+
127
+ .{{ID}} .sh-quick .qv input {
128
+ width: 2.5rem;
129
+ font: inherit;
130
+ color: inherit;
131
+ text-align: center;
132
+ background: transparent;
133
+ border: none;
134
+ padding: 0;
135
+ }
136
+
137
+ .{{ID}} .sh-quick .qv-sep {
138
+ font-size: 14px;
139
+ color: var(--vttf-text-faint);
140
+ }
141
+
142
+ .{{ID}} .sh-quick .qv-max {
143
+ font-size: 14px;
144
+ color: var(--vttf-text-muted);
145
+ }
146
+
147
+ .{{ID}} .sh-quick .qk {
148
+ font-family: var(--vttf-font-mono, monospace);
149
+ font-size: 10px;
150
+ letter-spacing: 0.12em;
151
+ text-transform: uppercase;
152
+ color: var(--vttf-text-faint);
153
+ }
154
+
155
+ /* ════════ TABS (.sh-tabs) ════════ */
156
+ .{{ID}} .sh-tabs {
157
+ display: flex;
158
+ gap: 0;
159
+ padding: 0 var(--vttf-space-4);
160
+ background: var(--vttf-bg-elevated);
161
+ border-bottom: 1px solid var(--vttf-border);
162
+ }
163
+
164
+ .{{ID}} .sh-tab {
165
+ display: inline-flex;
166
+ align-items: center;
167
+ gap: 6px;
168
+ padding: 12px 14px;
169
+ cursor: pointer;
170
+ color: var(--vttf-text-muted);
171
+ background: transparent;
172
+ border: none;
173
+ border-bottom: 2px solid transparent;
174
+ margin-bottom: -1px;
175
+ font-family: var(--vttf-font-body);
176
+ font-size: 13px;
177
+ font-weight: 600;
178
+ text-decoration: none;
179
+ }
180
+
181
+ .{{ID}} .sh-tab:hover {
182
+ color: var(--vttf-text);
183
+ }
184
+
185
+ .{{ID}} .sh-tab.on,
186
+ .{{ID}} .sh-tab.active {
187
+ color: var(--vttf-text);
188
+ border-bottom-color: var(--vttf-ember);
189
+ }
190
+
191
+ /* ════════ BODY (.sh-body) ════════ */
192
+ .{{ID}} .sh-body {
193
+ flex: 1;
194
+ overflow: auto;
195
+ padding: var(--vttf-space-4);
196
+ display: flex;
197
+ flex-direction: column;
198
+ gap: var(--vttf-space-4);
199
+ min-height: 320px;
200
+ }
201
+
202
+ .{{ID}} .sh-body .tab {
203
+ display: none;
204
+ flex-direction: column;
205
+ gap: var(--vttf-space-3);
206
+ }
207
+
208
+ .{{ID}} .sh-body .tab.active {
209
+ display: flex;
210
+ }
211
+
212
+ .{{ID}} .sh-section-title {
213
+ display: inline-flex;
214
+ align-items: center;
215
+ gap: var(--vttf-space-3);
216
+ font-family: var(--vttf-font-mono, monospace);
217
+ font-size: 10px;
218
+ letter-spacing: 0.14em;
219
+ text-transform: uppercase;
220
+ color: var(--vttf-text-faint);
221
+ font-weight: 600;
222
+ margin: 0;
223
+ }
224
+
225
+ .{{ID}} .sh-section-title::after {
226
+ content: "";
227
+ flex: 1;
228
+ height: 1px;
229
+ background: var(--vttf-border);
230
+ }
231
+
232
+ .{{ID}} .sh-section-head {
233
+ display: flex;
234
+ justify-content: space-between;
235
+ align-items: center;
236
+ gap: var(--vttf-space-3);
237
+ }
238
+
239
+ .{{ID}} .sh-action {
240
+ display: inline-flex;
241
+ align-items: center;
242
+ gap: 6px;
243
+ padding: 5px 10px;
244
+ border-radius: var(--vttf-radius-sm);
245
+ background: var(--vttf-surface);
246
+ border: 1px solid var(--vttf-border-strong);
247
+ color: var(--vttf-text);
248
+ font-family: inherit;
249
+ font-size: 12px;
250
+ font-weight: 600;
251
+ cursor: pointer;
252
+ }
253
+
254
+ .{{ID}} .sh-action:hover {
255
+ background: var(--vttf-surface-2);
256
+ }
257
+
258
+ .{{ID}} .sh-action i {
259
+ color: var(--vttf-ember);
260
+ }
261
+
262
+ /* ─── Abilities ─── */
263
+ .{{ID}} .sh-abilities {
264
+ display: grid;
265
+ grid-template-columns: repeat(6, 1fr);
266
+ gap: var(--vttf-space-2);
267
+ }
268
+
269
+ .{{ID}} .sh-abil {
270
+ display: flex;
271
+ flex-direction: column;
272
+ align-items: center;
273
+ gap: 4px;
274
+ padding: 12px 8px;
275
+ background: var(--vttf-surface);
276
+ border: 1px solid var(--vttf-border);
277
+ border-radius: var(--vttf-radius-md);
278
+ }
279
+
280
+ .{{ID}} .sh-abil .lbl {
281
+ font-family: var(--vttf-font-mono, monospace);
282
+ font-size: 10px;
283
+ letter-spacing: 0.12em;
284
+ text-transform: uppercase;
285
+ color: var(--vttf-text-faint);
286
+ font-weight: 600;
287
+ }
288
+
289
+ .{{ID}} .sh-abil .val {
290
+ width: 100%;
291
+ text-align: center;
292
+ background: transparent;
293
+ border: none;
294
+ color: var(--vttf-text);
295
+ font-family: var(--vttf-font-display, var(--vttf-font-body));
296
+ font-size: 28px;
297
+ font-weight: 700;
298
+ line-height: 1;
299
+ padding: 0;
300
+ }
301
+
302
+ .{{ID}} .sh-abil .val:focus {
303
+ outline: 1px solid var(--vttf-ember);
304
+ border-radius: var(--vttf-radius-sm);
305
+ }
306
+
307
+ .{{ID}} .sh-abil .mod {
308
+ background: transparent;
309
+ border: none;
310
+ color: var(--vttf-ember-glow);
311
+ font-family: var(--vttf-font-body);
312
+ font-size: 12px;
313
+ font-weight: 600;
314
+ cursor: pointer;
315
+ padding: 2px 8px;
316
+ border-radius: var(--vttf-radius-full);
317
+ }
318
+
319
+ .{{ID}} .sh-abil .mod:hover {
320
+ background: color-mix(in oklch, var(--vttf-ember) 12%, transparent);
321
+ }
322
+
323
+ /* ─── Items ─── */
324
+ .{{ID}} .sh-items {
325
+ display: flex;
326
+ flex-direction: column;
327
+ gap: 6px;
328
+ }
329
+
330
+ .{{ID}} .sh-item {
331
+ display: grid;
332
+ grid-template-columns: 32px 1fr auto auto auto;
333
+ gap: var(--vttf-space-3);
334
+ align-items: center;
335
+ padding: 8px 10px;
336
+ background: var(--vttf-surface);
337
+ border: 1px solid var(--vttf-border);
338
+ border-radius: var(--vttf-radius-sm);
339
+ cursor: grab;
340
+ }
341
+
342
+ .{{ID}} .sh-item:active {
343
+ cursor: grabbing;
344
+ }
345
+
346
+ .{{ID}} .sh-item-ico {
347
+ display: flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ width: 32px;
351
+ height: 32px;
352
+ background: var(--vttf-surface-2);
353
+ border: 1px solid var(--vttf-border);
354
+ border-radius: var(--vttf-radius-sm);
355
+ color: var(--vttf-text-muted);
356
+ font-size: 16px;
357
+ overflow: hidden;
358
+ }
359
+
360
+ .{{ID}} .sh-item-ico img {
361
+ width: 100%;
362
+ height: 100%;
363
+ object-fit: cover;
364
+ }
365
+
366
+ .{{ID}} .sh-item-name {
367
+ font-size: 14px;
368
+ font-weight: 600;
369
+ color: var(--vttf-text);
370
+ }
371
+
372
+ .{{ID}} .sh-item-meta {
373
+ font-family: var(--vttf-font-mono, monospace);
374
+ font-size: 11px;
375
+ color: var(--vttf-text-faint);
376
+ }
377
+
378
+ .{{ID}} .sh-tag {
379
+ display: inline-flex;
380
+ align-items: center;
381
+ padding: 3px 8px;
382
+ font-family: var(--vttf-font-mono, monospace);
383
+ font-size: 10px;
384
+ letter-spacing: 0.08em;
385
+ text-transform: uppercase;
386
+ font-weight: 600;
387
+ border-radius: var(--vttf-radius-full);
388
+ background: color-mix(in oklch, var(--vttf-ember) 10%, transparent);
389
+ color: var(--vttf-ember);
390
+ border: 1px solid color-mix(in oklch, var(--vttf-ember) 40%, transparent);
391
+ }
392
+
393
+ .{{ID}} .sh-tag.muted {
394
+ background: var(--vttf-bg-sunken);
395
+ color: var(--vttf-text-muted);
396
+ border-color: var(--vttf-border);
397
+ }
398
+
399
+ .{{ID}} .sh-qty {
400
+ font-family: var(--vttf-font-mono, monospace);
401
+ font-size: 12px;
402
+ color: var(--vttf-text-muted);
403
+ }
404
+
405
+ .{{ID}} .sh-item-delete {
406
+ background: transparent;
407
+ border: none;
408
+ color: var(--vttf-text-faint);
409
+ cursor: pointer;
410
+ padding: 4px 6px;
411
+ }
412
+
413
+ .{{ID}} .sh-item-delete:hover {
414
+ color: var(--vttf-rose);
415
+ }
416
+
417
+ .{{ID}} .sh-items-empty {
418
+ text-align: center;
419
+ padding: var(--vttf-space-4);
420
+ color: var(--vttf-text-faint);
421
+ font-style: italic;
422
+ }
423
+
424
+ /* ─── Drop affordance ─── */
425
+ .{{ID}} .sh-drop {
426
+ display: flex;
427
+ flex-direction: column;
428
+ align-items: center;
429
+ gap: 4px;
430
+ padding: 20px;
431
+ border: 1.5px dashed var(--vttf-border-strong);
432
+ border-radius: var(--vttf-radius-md);
433
+ background: color-mix(in oklch, var(--vttf-ember) 6%, transparent);
434
+ text-align: center;
435
+ }
436
+
437
+ .{{ID}} .sh-drop .lbl {
438
+ font-size: 13px;
439
+ font-weight: 600;
440
+ color: var(--vttf-text);
441
+ }
442
+
443
+ .{{ID}} .sh-drop .sub {
444
+ font-family: var(--vttf-font-mono, monospace);
445
+ font-size: 11px;
446
+ color: var(--vttf-text-faint);
447
+ }
448
+
449
+ /* ─── Empty placeholder (Spells tab) ─── */
450
+ .{{ID}} .sh-empty {
451
+ padding: var(--vttf-space-6);
452
+ text-align: center;
453
+ color: var(--vttf-text-faint);
454
+ font-style: italic;
455
+ }
456
+
457
+ /* ─── Biography (readonly fallback) ─── */
458
+ .{{ID}} .sh-biography-readonly {
459
+ padding: var(--vttf-space-4);
460
+ background: var(--vttf-bg-sunken);
461
+ border: 1px solid var(--vttf-border);
462
+ border-radius: var(--vttf-radius-md);
463
+ color: var(--vttf-text-muted);
464
+ line-height: 1.6;
465
+ }
466
+
467
+ /* ════════ FOOT (.sh-foot) ════════ */
468
+ .{{ID}} .sh-foot {
469
+ display: flex;
470
+ justify-content: space-between;
471
+ align-items: center;
472
+ gap: var(--vttf-space-4);
473
+ padding: 12px var(--vttf-space-4);
474
+ background: var(--vttf-surface);
475
+ border-top: 1px solid var(--vttf-border);
476
+ font-family: var(--vttf-font-mono, monospace);
477
+ font-size: 10px;
478
+ letter-spacing: 0.14em;
479
+ text-transform: uppercase;
480
+ color: var(--vttf-text-faint);
481
+ }
482
+
483
+ .{{ID}} .sh-foot .nm {
484
+ color: var(--vttf-ember);
485
+ font-weight: 600;
486
+ }
487
+
488
+ /* ════════ Gear sheet (unchanged structurally) ════════ */
489
+ .{{ID}}.item .field {
490
+ display: grid;
491
+ grid-template-columns: 8rem 1fr;
492
+ gap: var(--vttf-space-3);
493
+ align-items: center;
494
+ padding: 0.35rem 0;
495
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "id": "{{ID}}",
3
+ "title": "{{TITLE}}",
4
+ "description": "{{DESCRIPTION}}",
5
+ "version": "0.1.0",
6
+ "compatibility": {
7
+ "minimum": "{{FOUNDRY_MIN_VERSION}}",
8
+ "verified": "{{FOUNDRY_VERIFIED_VERSION}}"
9
+ },
10
+ "authors": [{ "name": "{{AUTHOR}}" }],
11
+ "esmodules": ["main.mjs"],
12
+ "styles": [{ "src": "styles/main.css" }],
13
+ "languages": [{ "lang": "en", "name": "English", "path": "lang/en.json" }],
14
+ "documentTypes": {
15
+ "Actor": {
16
+ "character": {
17
+ "htmlFields": ["biography"]
18
+ }
19
+ },
20
+ "Item": {
21
+ "gear": {
22
+ "htmlFields": ["description"]
23
+ }
24
+ }
25
+ },
26
+ "grid": {
27
+ "type": 1,
28
+ "distance": 5,
29
+ "units": "ft",
30
+ "diagonals": 0
31
+ },
32
+ "primaryTokenAttribute": "health",
33
+ "secondaryTokenAttribute": "power",
34
+ "flags": {
35
+ "hotReload": {
36
+ "extensions": ["css", "hbs", "json"],
37
+ "paths": ["styles", "templates", "lang"]
38
+ },
39
+ "{{ID}}": {
40
+ "needsMigrationVersion": "0.1.0",
41
+ "compatibleMigrationVersion": "0.0.0"
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "Actor": {
3
+ "types": ["character"]
4
+ },
5
+ "Item": {
6
+ "types": ["gear"]
7
+ }
8
+ }
@@ -0,0 +1,168 @@
1
+ {{!--
2
+ Character sheet — markup mirrors the canonical Figma reference
3
+ (character-sheet.jsx + Theme Explorations .sh-* classes).
4
+
5
+ Tabs nav uses `data-tab`/`data-group` + `data-action="vttforgeTab"` so
6
+ BaseActorSheet's default tab action routes clicks. `context.tabs.primary`
7
+ is auto-populated by BaseActorSheet's _prepareContext.
8
+
9
+ `<img data-edit="img">` is handled by DocumentSheetV2's built-in editImage
10
+ action. `<prose-mirror>` is the v13 rich text element for biography.
11
+ --}}
12
+ <form autocomplete="off">
13
+
14
+ {{!-- ════════ HEAD (.sh-head) ════════ --}}
15
+ <div class="sh-head">
16
+ <img class="sh-portrait" src="{{actor.img}}" alt="{{actor.name}}" data-edit="img" />
17
+ <div class="sh-id">
18
+ <div class="sh-id-top">
19
+ <input class="sh-name" type="text" name="name" value="{{actor.name}}"
20
+ placeholder="{{localize '{{LOCALE_PREFIX}}.Sheet.NamePlaceholder'}}" />
21
+ <div class="sh-sub">
22
+ <span>{{localize "{{LOCALE_PREFIX}}.Sheet.Level"}}</span>
23
+ <input type="number" name="system.level" value="{{system.level}}" min="1" max="20" />
24
+ </div>
25
+ </div>
26
+ <div class="sh-quick">
27
+ <div class="q">
28
+ <div class="qv">
29
+ <input type="number" name="system.health.value" value="{{system.health.value}}" min="0" />
30
+ <span class="qv-sep">/</span>
31
+ <span class="qv-max">{{system.health.max}}</span>
32
+ </div>
33
+ <div class="qk">{{localize "{{LOCALE_PREFIX}}.Sheet.Quick.HP"}}</div>
34
+ </div>
35
+ <div class="q">
36
+ <div class="qv">{{system.armorClass}}</div>
37
+ <div class="qk">{{localize "{{LOCALE_PREFIX}}.Sheet.Quick.AC"}}</div>
38
+ </div>
39
+ <div class="q">
40
+ <div class="qv">
41
+ <input type="number" name="system.speed" value="{{system.speed}}" min="0" />
42
+ </div>
43
+ <div class="qk">{{localize "{{LOCALE_PREFIX}}.Sheet.Quick.SPD"}}</div>
44
+ </div>
45
+ <div class="q">
46
+ <div class="qv">{{numberFormat system.initiative sign=true}}</div>
47
+ <div class="qk">{{localize "{{LOCALE_PREFIX}}.Sheet.Quick.INIT"}}</div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ {{!-- ════════ TABS (.sh-tabs) ════════ --}}
54
+ <nav class="sh-tabs" data-group="primary">
55
+ {{#each tabs as |tab|}}
56
+ <button type="button" class="sh-tab {{#if tab.active}}on{{/if}}"
57
+ data-tab="{{tab.id}}"
58
+ data-group="primary"
59
+ data-action="vttforgeTab"
60
+ title="{{localize tab.label}}">
61
+ {{#if tab.icon}}<i class="{{tab.icon}}"></i>{{/if}}
62
+ <span>{{localize tab.label}}</span>
63
+ </button>
64
+ {{/each}}
65
+ </nav>
66
+
67
+ {{!-- ════════ BODY (.sh-body) ════════ --}}
68
+ <section class="sh-body">
69
+
70
+ {{!-- ABILITIES --}}
71
+ <section class="tab abilities {{tabs.abilities.cssClass}}"
72
+ data-tab="abilities" data-group="primary">
73
+ <h3 class="sh-section-title">{{localize "{{LOCALE_PREFIX}}.Sheet.AbilityScores"}}</h3>
74
+ <div class="sh-abilities">
75
+ {{#each abilities as |ability|}}
76
+ <div class="sh-abil">
77
+ <div class="lbl" title="{{ability.label}}">{{ability.key}}</div>
78
+ <input class="val" type="number"
79
+ name="system.abilities.{{ability.key}}"
80
+ value="{{ability.value}}" min="1" max="30" />
81
+ <button type="button" class="mod sh-abil-roll"
82
+ data-action="rollAbility" data-ability="{{ability.key}}"
83
+ title="{{localize '{{LOCALE_PREFIX}}.Sheet.Roll.label'}}">
84
+ {{numberFormat ability.mod sign=true}}
85
+ </button>
86
+ </div>
87
+ {{/each}}
88
+ </div>
89
+ </section>
90
+
91
+ {{!-- INVENTORY --}}
92
+ <section class="tab inventory {{tabs.inventory.cssClass}}"
93
+ data-tab="inventory" data-group="primary">
94
+ <header class="sh-section-head">
95
+ <h3 class="sh-section-title">{{localize "{{LOCALE_PREFIX}}.Sheet.Inventory"}}</h3>
96
+ <button type="button" class="sh-action" data-action="createGear"
97
+ title="{{localize '{{LOCALE_PREFIX}}.Sheet.NewGear'}}">
98
+ <i class="fa-solid fa-plus"></i>
99
+ <span>{{localize "{{LOCALE_PREFIX}}.Sheet.NewGear"}}</span>
100
+ </button>
101
+ </header>
102
+ <div class="sh-items">
103
+ {{#each gear as |item|}}
104
+ <div class="sh-item" data-item-id="{{item.id}}" draggable="true">
105
+ <div class="sh-item-ico">
106
+ {{#if item.img}}
107
+ <img src="{{item.img}}" alt="{{item.name}}" />
108
+ {{else}}
109
+ <i class="fa-solid fa-box"></i>
110
+ {{/if}}
111
+ </div>
112
+ <div>
113
+ <div class="sh-item-name">{{item.name}}</div>
114
+ <div class="sh-item-meta">{{numberFormat item.weight}} lb</div>
115
+ </div>
116
+ <span class="sh-tag {{#if (eq item.kind "stowed")}}muted{{/if}}">
117
+ {{localize (concat "{{LOCALE_PREFIX}}.Gear.Kind." item.kind)}}
118
+ </span>
119
+ <span class="sh-qty">×{{item.quantity}}</span>
120
+ <button type="button" class="sh-item-delete" data-action="deleteItem"
121
+ title="{{localize '{{LOCALE_PREFIX}}.Sheet.Delete'}}">
122
+ <i class="fa-solid fa-trash"></i>
123
+ </button>
124
+ </div>
125
+ {{else}}
126
+ <div class="sh-items-empty">{{localize "{{LOCALE_PREFIX}}.Sheet.NoGear"}}</div>
127
+ {{/each}}
128
+ </div>
129
+
130
+ <div class="sh-drop">
131
+ <div class="lbl">{{localize "{{LOCALE_PREFIX}}.Sheet.Drop.label"}}</div>
132
+ <div class="sub">onDropItem(item, event)</div>
133
+ </div>
134
+ </section>
135
+
136
+ {{!-- SPELLS (placeholder) --}}
137
+ <section class="tab spells {{tabs.spells.cssClass}}"
138
+ data-tab="spells" data-group="primary">
139
+ <h3 class="sh-section-title">{{localize "{{LOCALE_PREFIX}}.Sheet.Spells"}}</h3>
140
+ <div class="sh-empty">{{localize "{{LOCALE_PREFIX}}.Sheet.SpellsEmpty"}}</div>
141
+ </section>
142
+
143
+ {{!-- BIOGRAPHY --}}
144
+ <section class="tab biography {{tabs.biography.cssClass}}"
145
+ data-tab="biography" data-group="primary">
146
+ <h3 class="sh-section-title">{{localize "{{LOCALE_PREFIX}}.Sheet.Biography"}}</h3>
147
+ {{#if isEditable}}
148
+ <prose-mirror name="system.biography"
149
+ button="true"
150
+ editable="{{isEditable}}"
151
+ toggled="false"
152
+ value="{{system.biography}}">
153
+ {{{enrichedBiography}}}
154
+ </prose-mirror>
155
+ {{else}}
156
+ <div class="sh-biography-readonly">{{{enrichedBiography}}}</div>
157
+ {{/if}}
158
+ </section>
159
+
160
+ </section>
161
+
162
+ {{!-- ════════ FOOT (.sh-foot) ════════ --}}
163
+ <footer class="sh-foot">
164
+ <span class="nm">VTTFORGE · FORGE</span>
165
+ <span>@vttforge/styles · v0.2</span>
166
+ </footer>
167
+
168
+ </form>