@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.
- package/CHANGELOG.md +350 -0
- package/LICENSE +21 -0
- package/README.md +33 -5
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +201 -0
- package/dist/bin.mjs.map +1 -0
- package/dist/index.d.mts +496 -0
- package/dist/index.mjs +2 -0
- package/dist/src-CWhUNM__.mjs +2443 -0
- package/dist/src-CWhUNM__.mjs.map +1 -0
- package/package.json +62 -9
- package/templates/module-js/.github/workflows/release.yml +92 -0
- package/templates/module-js/README.md +57 -0
- package/templates/module-js/_gitignore +12 -0
- package/templates/module-js/lang/en.json +11 -0
- package/templates/module-js/module.json +20 -0
- package/templates/module-js/package.json +24 -0
- package/templates/module-js/scripts/main.mjs +65 -0
- package/templates/module-js/styles/main.css +16 -0
- package/templates/module-js/vite.config.mjs +13 -0
- package/templates/module-ts/.github/workflows/release.yml +92 -0
- package/templates/module-ts/README.md +57 -0
- package/templates/module-ts/_gitignore +12 -0
- package/templates/module-ts/lang/en.json +11 -0
- package/templates/module-ts/module.json +20 -0
- package/templates/module-ts/package.json +26 -0
- package/templates/module-ts/scripts/foundry-globals.ts +23 -0
- package/templates/module-ts/scripts/main.ts +71 -0
- package/templates/module-ts/styles/main.css +16 -0
- package/templates/module-ts/tsconfig.json +18 -0
- package/templates/module-ts/vite.config.mjs +13 -0
- package/templates/system-js/.github/workflows/release.yml +92 -0
- package/templates/system-js/README.md +57 -0
- package/templates/system-js/_gitignore +12 -0
- package/templates/system-js/lang/en.json +55 -0
- package/templates/system-js/package.json +25 -0
- package/templates/system-js/scripts/data/character-data.mjs +79 -0
- package/templates/system-js/scripts/data/gear-data.mjs +37 -0
- package/templates/system-js/scripts/main.mjs +79 -0
- package/templates/system-js/scripts/migrations.mjs +43 -0
- package/templates/system-js/scripts/sheets/character-sheet.mjs +164 -0
- package/templates/system-js/scripts/sheets/gear-sheet.mjs +65 -0
- package/templates/system-js/styles/main.css +495 -0
- package/templates/system-js/system.json +44 -0
- package/templates/system-js/template.json +8 -0
- package/templates/system-js/templates/actor/character-sheet.hbs +168 -0
- package/templates/system-js/templates/item/gear-sheet.hbs +54 -0
- package/templates/system-js/vite.config.mjs +16 -0
- package/templates/system-ts/.github/workflows/release.yml +92 -0
- package/templates/system-ts/README.md +57 -0
- package/templates/system-ts/_gitignore +12 -0
- package/templates/system-ts/lang/en.json +55 -0
- package/templates/system-ts/package.json +27 -0
- package/templates/system-ts/scripts/data/character-data.ts +81 -0
- package/templates/system-ts/scripts/data/gear-data.ts +37 -0
- package/templates/system-ts/scripts/foundry-globals.ts +33 -0
- package/templates/system-ts/scripts/main.ts +82 -0
- package/templates/system-ts/scripts/migrations.ts +43 -0
- package/templates/system-ts/scripts/sheets/character-sheet.ts +202 -0
- package/templates/system-ts/scripts/sheets/gear-sheet.ts +68 -0
- package/templates/system-ts/styles/main.css +495 -0
- package/templates/system-ts/system.json +44 -0
- package/templates/system-ts/template.json +8 -0
- package/templates/system-ts/templates/actor/character-sheet.hbs +168 -0
- package/templates/system-ts/templates/item/gear-sheet.hbs +54 -0
- package/templates/system-ts/tsconfig.json +18 -0
- package/templates/system-ts/vite.config.mjs +16 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# {{TITLE}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
A Foundry VTT module built on [VTTForge](https://github.com/vttforge/vttforge) — uses `SystemConfig` from `@vttforge/core` for typed settings, ships an example hook listener, and exposes a small public API on `game.modules.get("{{ID}}").api`.
|
|
6
|
+
|
|
7
|
+
> **Note** — VTTForge is currently in pre-release and not yet published to npm. Dependencies in `package.json` will resolve once VTTForge ships its first public release.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm install
|
|
13
|
+
pnpm dev # vite build --watch + auto-symlinks dist/ into Foundry's Data
|
|
14
|
+
pnpm build # one-shot build + emits {{ID}}-<version>.zip for foundryvtt.com
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`pnpm dev` (`vttforge dev` under the hood) auto-detects your Foundry user-data
|
|
18
|
+
directory on the first run, prompts you to confirm it, and saves the choice to
|
|
19
|
+
`.vttforge/config.json` for next time. Override with `--data-dir <path>` or set
|
|
20
|
+
`FOUNDRY_DATA_DIR` in your environment.
|
|
21
|
+
|
|
22
|
+
Run Foundry with `--hotReload` to pick up changes without refreshing the page.
|
|
23
|
+
Then enable **{{TITLE}}** in any world.
|
|
24
|
+
|
|
25
|
+
## What's inside
|
|
26
|
+
|
|
27
|
+
| Path | Purpose |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `module.json` | Manifest |
|
|
30
|
+
| `scripts/main.mjs` | Entry point — settings registration, hook listeners, public API |
|
|
31
|
+
| `styles/main.css` | Stylesheet, scoped under `.{{ID}}` |
|
|
32
|
+
| `lang/en.json` | Localization strings |
|
|
33
|
+
|
|
34
|
+
## Public API
|
|
35
|
+
|
|
36
|
+
The module exposes its API on `game.modules.get("{{ID}}").api`:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const api = game.modules.get("{{ID}}").api;
|
|
40
|
+
api.greet("world"); // returns "Hello, world!"
|
|
41
|
+
api.getSetting("showWelcome"); // returns the current value
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Extend `scripts/main.mjs` to add real methods — the example exists to show the registration pattern.
|
|
45
|
+
|
|
46
|
+
## Releasing to Foundry
|
|
47
|
+
|
|
48
|
+
Tag-push triggers `.github/workflows/release.yml` to build, zip, and attach `{{ID}}-<version>.zip` + `dist/module.json` to a GitHub Release. Point Foundry at the `latest/download/module.json` URL.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git tag v0.1.0
|
|
52
|
+
git push --tags
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
{{LICENSE}} © {{YEAR}} {{AUTHOR}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"{{LOCALE_PREFIX}}": {
|
|
3
|
+
"Settings": {
|
|
4
|
+
"showWelcome": {
|
|
5
|
+
"name": "Show welcome notification",
|
|
6
|
+
"hint": "Display a one-shot toast when {{TITLE}} loads in a world."
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"Welcome": "{{TITLE}} loaded — try `game.modules.get(\"{{ID}}\").api.greet(\"GM\")` in the console."
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
"flags": {
|
|
15
|
+
"hotReload": {
|
|
16
|
+
"extensions": ["css", "json"],
|
|
17
|
+
"paths": ["styles", "lang"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{ID}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{{DESCRIPTION}}",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "{{LICENSE}}",
|
|
8
|
+
"author": "{{AUTHOR}}",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "vttforge build",
|
|
11
|
+
"dev": "vttforge dev"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@vttforge/core": "^0.6.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@vttforge/cli": "^0.5.0",
|
|
18
|
+
"@vttforge/vite-plugin": "^0.3.0",
|
|
19
|
+
"vite": "^8.2.2"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=26"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{TITLE}} — entry point.
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates the typical Foundry module lifecycle:
|
|
5
|
+
*
|
|
6
|
+
* - `Hooks.once("init")` — register settings (here via `SystemConfig` from
|
|
7
|
+
* `@vttforge/core` for typed access) and expose the public API on
|
|
8
|
+
* `game.modules.get(MODULE_ID).api`.
|
|
9
|
+
* - `Hooks.once("ready")` — surface a one-shot notification if the user
|
|
10
|
+
* opted in via settings.
|
|
11
|
+
* - `Hooks.on("renderActorSheetV2", ...)` — example hook listener that
|
|
12
|
+
* tags any actor sheet with a discreet `.{{ID}}-badge` so you can verify
|
|
13
|
+
* the module is actually attached without hunting through devtools.
|
|
14
|
+
*/
|
|
15
|
+
import { SystemConfig } from '@vttforge/core';
|
|
16
|
+
|
|
17
|
+
const MODULE_ID = '{{ID}}';
|
|
18
|
+
const settings = new SystemConfig(MODULE_ID);
|
|
19
|
+
|
|
20
|
+
Hooks.once('init', () => {
|
|
21
|
+
settings.register('showWelcome', {
|
|
22
|
+
name: '{{LOCALE_PREFIX}}.Settings.showWelcome.name',
|
|
23
|
+
hint: '{{LOCALE_PREFIX}}.Settings.showWelcome.hint',
|
|
24
|
+
scope: 'client',
|
|
25
|
+
config: true,
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const api = {
|
|
31
|
+
greet(name) {
|
|
32
|
+
return `Hello, ${name}!`;
|
|
33
|
+
},
|
|
34
|
+
getSetting(key) {
|
|
35
|
+
return settings.get(key);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const moduleHandle = game.modules.get(MODULE_ID);
|
|
40
|
+
if (moduleHandle) {
|
|
41
|
+
moduleHandle.api = api;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
Hooks.once('ready', () => {
|
|
46
|
+
const shouldWelcome = settings.get('showWelcome');
|
|
47
|
+
if (shouldWelcome) {
|
|
48
|
+
ui.notifications?.info(game.i18n.localize('{{LOCALE_PREFIX}}.Welcome'));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
Hooks.on('renderActorSheetV2', (_app, element) => {
|
|
53
|
+
// Decorative: tag the rendered sheet so it's obvious the module is
|
|
54
|
+
// active. Remove or replace with real behaviour once you start shipping
|
|
55
|
+
// module features.
|
|
56
|
+
const header = element.querySelector('.window-header .window-title');
|
|
57
|
+
if (header && !header.querySelector(`.{{ID}}-badge`)) {
|
|
58
|
+
const badge = document.createElement('span');
|
|
59
|
+
badge.className = '{{ID}}-badge';
|
|
60
|
+
// Double-quoted because `vttforge init` rejects `"` and `\` in the
|
|
61
|
+
// title — apostrophes in titles like "Daisy's Module" stay intact.
|
|
62
|
+
badge.textContent = "{{TITLE}}";
|
|
63
|
+
header.appendChild(badge);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{TITLE}} stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Scope all rules under the `.{{ID}}` namespace so they don't leak into
|
|
5
|
+
* other modules or the host system's sheets. Foundry's CSS cascade is
|
|
6
|
+
* already layered, but namespacing makes intent explicit.
|
|
7
|
+
*/
|
|
8
|
+
.{{ID}}-badge {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 0.4em;
|
|
12
|
+
padding: 0.15em 0.5em;
|
|
13
|
+
border-radius: 4px;
|
|
14
|
+
background: rgba(0, 0, 0, 0.08);
|
|
15
|
+
font-size: 0.8em;
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import vttforge from '@vttforge/vite-plugin';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vite config for {{TITLE}}.
|
|
6
|
+
*
|
|
7
|
+
* `@vttforge/vite-plugin` owns the build contract for both systems and
|
|
8
|
+
* modules — `kind: 'module'` tells the plugin to write `dist/module.json`
|
|
9
|
+
* (instead of `system.json`) and to base chunk URLs at `/modules/{{ID}}/`.
|
|
10
|
+
*/
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [vttforge({ id: '{{ID}}', kind: 'module' })],
|
|
13
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Resolve version from tag
|
|
18
|
+
id: version
|
|
19
|
+
env:
|
|
20
|
+
REF: ${{ github.ref_name }}
|
|
21
|
+
run: |
|
|
22
|
+
version="${REF#v}"
|
|
23
|
+
# Git allows surprising characters in tag names. Reject anything
|
|
24
|
+
# that isn't a strict semver-ish identifier so downstream node -e
|
|
25
|
+
# / shell interpolations can't be turned into command injection
|
|
26
|
+
# by a malicious tag push.
|
|
27
|
+
if ! [[ "$version" =~ ^[0-9A-Za-z._+-]+$ ]]; then
|
|
28
|
+
echo "Refusing tag with unsafe characters: $version" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
32
|
+
|
|
33
|
+
- uses: pnpm/action-setup@v4
|
|
34
|
+
with:
|
|
35
|
+
version: 10
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-node@v4
|
|
38
|
+
with:
|
|
39
|
+
node-version: 22
|
|
40
|
+
|
|
41
|
+
- run: pnpm install
|
|
42
|
+
|
|
43
|
+
- name: Sync manifest version
|
|
44
|
+
env:
|
|
45
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
46
|
+
run: |
|
|
47
|
+
node -e "const fs=require('node:fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version=process.env.VERSION;fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');"
|
|
48
|
+
|
|
49
|
+
# We invoke vite directly (not `pnpm build` → `vttforge build`) because
|
|
50
|
+
# the release workflow needs to:
|
|
51
|
+
# 1. Run vite build → emit dist/module.json
|
|
52
|
+
# 2. Patch manifest/download URLs into dist/module.json
|
|
53
|
+
# 3. Zip dist/ → release artifact (so the zipped manifest carries URLs)
|
|
54
|
+
# `vttforge build` zips at step 1, which would freeze the un-patched
|
|
55
|
+
# manifest. Eventually the vite-plugin will support env-driven URL
|
|
56
|
+
# injection and this dance disappears.
|
|
57
|
+
- run: pnpm exec vite build
|
|
58
|
+
|
|
59
|
+
- name: Inject release URLs into manifest
|
|
60
|
+
env:
|
|
61
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
62
|
+
REPO: ${{ github.repository }}
|
|
63
|
+
PKG_ID: '{{ID}}'
|
|
64
|
+
run: |
|
|
65
|
+
node -e "const fs=require('node:fs');const path='dist/module.json';const m=JSON.parse(fs.readFileSync(path,'utf8'));const r=process.env.REPO;const v=process.env.VERSION;const id=process.env.PKG_ID;m.manifest=\`https://github.com/\${r}/releases/latest/download/module.json\`;m.download=\`https://github.com/\${r}/releases/download/v\${v}/\${id}-\${v}.zip\`;fs.writeFileSync(path,JSON.stringify(m,null,2)+'\n');"
|
|
66
|
+
|
|
67
|
+
- name: Build release zip
|
|
68
|
+
env:
|
|
69
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
70
|
+
PKG_ID: '{{ID}}'
|
|
71
|
+
run: |
|
|
72
|
+
cd dist
|
|
73
|
+
zip -r "../${PKG_ID}-${VERSION}.zip" .
|
|
74
|
+
|
|
75
|
+
- name: Create GitHub Release
|
|
76
|
+
env:
|
|
77
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
78
|
+
PKG_ID: '{{ID}}'
|
|
79
|
+
uses: softprops/action-gh-release@v2
|
|
80
|
+
with:
|
|
81
|
+
files: |
|
|
82
|
+
${{ env.PKG_ID }}-${{ env.VERSION }}.zip
|
|
83
|
+
dist/module.json
|
|
84
|
+
generate_release_notes: true
|
|
85
|
+
body: |
|
|
86
|
+
## Foundry installation
|
|
87
|
+
|
|
88
|
+
Install via Foundry's setup wizard with this manifest URL:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
https://github.com/${{ github.repository }}/releases/latest/download/module.json
|
|
92
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# {{TITLE}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
A Foundry VTT module built on [VTTForge](https://github.com/vttforge/vttforge) — uses `SystemConfig` from `@vttforge/core` for typed settings, ships an example hook listener, and exposes a small public API on `game.modules.get("{{ID}}").api`.
|
|
6
|
+
|
|
7
|
+
> **Note** — VTTForge is currently in pre-release and not yet published to npm. Dependencies in `package.json` will resolve once VTTForge ships its first public release.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm install
|
|
13
|
+
pnpm dev # vite build --watch + auto-symlinks dist/ into Foundry's Data
|
|
14
|
+
pnpm build # one-shot build + emits {{ID}}-<version>.zip for foundryvtt.com
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`pnpm dev` (`vttforge dev` under the hood) auto-detects your Foundry user-data
|
|
18
|
+
directory on the first run, prompts you to confirm it, and saves the choice to
|
|
19
|
+
`.vttforge/config.json` for next time. Override with `--data-dir <path>` or set
|
|
20
|
+
`FOUNDRY_DATA_DIR` in your environment.
|
|
21
|
+
|
|
22
|
+
Run Foundry with `--hotReload` to pick up changes without refreshing the page.
|
|
23
|
+
Then enable **{{TITLE}}** in any world.
|
|
24
|
+
|
|
25
|
+
## What's inside
|
|
26
|
+
|
|
27
|
+
| Path | Purpose |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `module.json` | Manifest |
|
|
30
|
+
| `scripts/main.ts` | Entry point — settings registration, hook listeners, public API |
|
|
31
|
+
| `styles/main.css` | Stylesheet, scoped under `.{{ID}}` |
|
|
32
|
+
| `lang/en.json` | Localization strings |
|
|
33
|
+
|
|
34
|
+
## Public API
|
|
35
|
+
|
|
36
|
+
The module exposes its API on `game.modules.get("{{ID}}").api`:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const api = game.modules.get("{{ID}}").api;
|
|
40
|
+
api.greet("world"); // returns "Hello, world!"
|
|
41
|
+
api.getSetting("showWelcome"); // returns the current value
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Extend `scripts/main.ts` to add real methods — the example exists to show the registration pattern.
|
|
45
|
+
|
|
46
|
+
## Releasing to Foundry
|
|
47
|
+
|
|
48
|
+
Tag-push triggers `.github/workflows/release.yml` to build, zip, and attach `{{ID}}-<version>.zip` + `dist/module.json` to a GitHub Release. Point Foundry at the `latest/download/module.json` URL.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git tag v0.1.0
|
|
52
|
+
git push --tags
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
{{LICENSE}} © {{YEAR}} {{AUTHOR}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"{{LOCALE_PREFIX}}": {
|
|
3
|
+
"Settings": {
|
|
4
|
+
"showWelcome": {
|
|
5
|
+
"name": "Show welcome notification",
|
|
6
|
+
"hint": "Display a one-shot toast when {{TITLE}} loads in a world."
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"Welcome": "{{TITLE}} loaded — try `game.modules.get(\"{{ID}}\").api.greet(\"GM\")` in the console."
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
"flags": {
|
|
15
|
+
"hotReload": {
|
|
16
|
+
"extensions": ["css", "json"],
|
|
17
|
+
"paths": ["styles", "lang"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{ID}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "{{DESCRIPTION}}",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "{{LICENSE}}",
|
|
8
|
+
"author": "{{AUTHOR}}",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "vttforge build",
|
|
11
|
+
"dev": "vttforge dev",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@vttforge/core": "^0.6.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@vttforge/cli": "^0.5.0",
|
|
19
|
+
"@vttforge/vite-plugin": "^0.3.0",
|
|
20
|
+
"typescript": "^5.4.0",
|
|
21
|
+
"vite": "^8.2.2"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=26"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Foundry runtime globals.
|
|
3
|
+
*
|
|
4
|
+
* These declarations let the scaffold compile cleanly without an external
|
|
5
|
+
* type package. Replace with `fvtt-types` (community-maintained, pinned to
|
|
6
|
+
* a known-working commit SHA) or `@vttforge/types` (coming in a later
|
|
7
|
+
* VTTForge release) for full type coverage.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
// biome-ignore lint/suspicious/noExplicitAny: stub declarations only
|
|
12
|
+
const game: any;
|
|
13
|
+
// biome-ignore lint/suspicious/noExplicitAny: stub declarations only
|
|
14
|
+
const CONFIG: any;
|
|
15
|
+
// biome-ignore lint/suspicious/noExplicitAny: stub declarations only
|
|
16
|
+
const Hooks: any;
|
|
17
|
+
// biome-ignore lint/suspicious/noExplicitAny: stub declarations only
|
|
18
|
+
const ui: any;
|
|
19
|
+
// biome-ignore lint/suspicious/noExplicitAny: stub declarations only
|
|
20
|
+
const foundry: any;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{TITLE}} — entry point.
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates the typical Foundry module lifecycle:
|
|
5
|
+
*
|
|
6
|
+
* - `Hooks.once("init")` — register settings (here via `SystemConfig` from
|
|
7
|
+
* `@vttforge/core` for typed access) and expose the public API on
|
|
8
|
+
* `game.modules.get(MODULE_ID).api`.
|
|
9
|
+
* - `Hooks.once("ready")` — surface a one-shot notification if the user
|
|
10
|
+
* opted in via settings.
|
|
11
|
+
* - `Hooks.on("renderActorSheetV2", ...)` — example hook listener that
|
|
12
|
+
* tags any actor sheet with a discreet `.{{ID}}-badge` so you can verify
|
|
13
|
+
* the module is actually attached without hunting through devtools.
|
|
14
|
+
*/
|
|
15
|
+
import './foundry-globals.js';
|
|
16
|
+
import { SystemConfig } from '@vttforge/core';
|
|
17
|
+
|
|
18
|
+
const MODULE_ID = '{{ID}}';
|
|
19
|
+
const settings = new SystemConfig(MODULE_ID);
|
|
20
|
+
|
|
21
|
+
interface ModuleApi {
|
|
22
|
+
greet(name: string): string;
|
|
23
|
+
getSetting<T>(key: string): T;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Hooks.once('init', () => {
|
|
27
|
+
settings.register('showWelcome', {
|
|
28
|
+
name: '{{LOCALE_PREFIX}}.Settings.showWelcome.name',
|
|
29
|
+
hint: '{{LOCALE_PREFIX}}.Settings.showWelcome.hint',
|
|
30
|
+
scope: 'client',
|
|
31
|
+
config: true,
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const api: ModuleApi = {
|
|
37
|
+
greet(name: string): string {
|
|
38
|
+
return `Hello, ${name}!`;
|
|
39
|
+
},
|
|
40
|
+
getSetting<T>(key: string): T {
|
|
41
|
+
return settings.get(key) as T;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const moduleHandle = game.modules.get(MODULE_ID);
|
|
46
|
+
if (moduleHandle) {
|
|
47
|
+
moduleHandle.api = api;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
Hooks.once('ready', () => {
|
|
52
|
+
const shouldWelcome = settings.get<boolean>('showWelcome');
|
|
53
|
+
if (shouldWelcome) {
|
|
54
|
+
ui.notifications?.info(game.i18n.localize('{{LOCALE_PREFIX}}.Welcome'));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
Hooks.on('renderActorSheetV2', (_app: unknown, element: HTMLElement) => {
|
|
59
|
+
// Decorative: tag the rendered sheet so it's obvious the module is
|
|
60
|
+
// active. Remove or replace with real behaviour once you start shipping
|
|
61
|
+
// module features.
|
|
62
|
+
const header = element.querySelector('.window-header .window-title');
|
|
63
|
+
if (header && !header.querySelector(`.{{ID}}-badge`)) {
|
|
64
|
+
const badge = document.createElement('span');
|
|
65
|
+
badge.className = '{{ID}}-badge';
|
|
66
|
+
// Double-quoted because `vttforge init` rejects `"` and `\` in the
|
|
67
|
+
// title — apostrophes in titles like "Daisy's Module" stay intact.
|
|
68
|
+
badge.textContent = "{{TITLE}}";
|
|
69
|
+
header.appendChild(badge);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{TITLE}} stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Scope all rules under the `.{{ID}}` namespace so they don't leak into
|
|
5
|
+
* other modules or the host system's sheets. Foundry's CSS cascade is
|
|
6
|
+
* already layered, but namespacing makes intent explicit.
|
|
7
|
+
*/
|
|
8
|
+
.{{ID}}-badge {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 0.4em;
|
|
12
|
+
padding: 0.15em 0.5em;
|
|
13
|
+
border-radius: 4px;
|
|
14
|
+
background: rgba(0, 0, 0, 0.08);
|
|
15
|
+
font-size: 0.8em;
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"isolatedModules": true,
|
|
10
|
+
"verbatimModuleSyntax": true,
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"allowImportingTsExtensions": false
|
|
15
|
+
},
|
|
16
|
+
"include": ["scripts/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import vttforge from '@vttforge/vite-plugin';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vite config for {{TITLE}}.
|
|
6
|
+
*
|
|
7
|
+
* `@vttforge/vite-plugin` owns the build contract for both systems and
|
|
8
|
+
* modules — `kind: 'module'` tells the plugin to write `dist/module.json`
|
|
9
|
+
* (instead of `system.json`) and to base chunk URLs at `/modules/{{ID}}/`.
|
|
10
|
+
*/
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [vttforge({ id: '{{ID}}', kind: 'module', entry: 'scripts/main.ts' })],
|
|
13
|
+
});
|