Merge pull request 'Add tweak to set the default Hotbar page' (#34) from feature/default-hotbar-page into main

Reviewed-on: #34
This commit is contained in:
Oliver 2026-01-17 22:45:06 +00:00
commit 52cf835a52
14 changed files with 93 additions and 2091 deletions

View file

@ -1,2 +1,5 @@
# The absolute path to the Foundry installation to create symlinks to # The absolute path to the Foundry installation to create symlinks to
FOUNDRY_ROOT="" FOUNDRY_ROOT=""
# The manifest file for the module/system
MANIFEST="./module.json"

View file

@ -1,89 +1,46 @@
on: [ workflow_dispatch ] on: [ workflow_dispatch ]
env:
MANIFEST: "module.json"
jobs: jobs:
create-artifacts: create-draft-release:
name: "Create artifacts" name: "Create Draft Release"
runs-on: act runs-on: act
outputs:
version: ${{steps.version.outputs.version}}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies - name: Install dependencies
run: npm clean-install run: npm clean-install
- id: version - id: version
run: cat module.json | echo version=`jq -r ".version"` >> "$FORGEJO_OUTPUT" run: cat system.json | echo version=`jq -r ".version"` >> "$FORGEJO_OUTPUT"
- name: Assert that the tag doesn't exist - name: Assert that the tag doesn't exist
run: node scripts/tagExists.mjs run: node scripts/src/tagExists.mjs
env: env:
TAG_NAME: "v${{steps.version.outputs.version}}" TAG_NAME: "v${{steps.version.outputs.version}}"
- name: Compress files # Compendia steps
run: zip -r release.zip langs module styles templates README.md assets - name: Build compendia
run: "npm run data:build"
- name: Upload artifacts - name: Remove compendia source
uses: https://data.forgejo.org/forgejo/upload-artifact@v4 run: "rm -rf packs/**/_source"
with:
path: |
module.json
release.zip
scripts/*.mjs
package-lock.json
package.json
retention-days: 7
if-no-files-found: error
forgejo-release:
name: "Create Forgejo release"
runs-on: act
needs:
- create-artifacts
steps:
- name: Download artifacts
uses: https://data.forgejo.org/forgejo/download-artifact@v4
with:
merge-multiple: true
- name: Install dependencies
run: npm i
- id: version
run: cat module.json | echo version=`jq -r ".version"` >> "$FORGEJO_OUTPUT"
- name: Update manifest - name: Update manifest
run: node scripts/prepareManifest.mjs run: node scripts/src/prepareManifest.mjs
env: env:
DOWNLOAD_URL: "${{forgejo.server_url}}/${{forgejo.repository}}/releases/download/v${{steps.version.outputs.version}}/release.zip" DOWNLOAD_URL: "${{forgejo.server_url}}/${{forgejo.repository}}/releases/download/v${{steps.version.outputs.version}}/release.zip"
LATEST_URL: "${{forgejo.server_url}}/${{forgejo.repository}}/releases/download/latest/module.json" LATEST_URL: "${{forgejo.server_url}}/${{forgejo.repository}}/releases/download/latest/system.json"
- name: Add manifest into release archive - name: Compress files
run: zip release.zip --update module.json run: zip -r release.zip langs module styles templates README.md assets LICENSE
- name: Upload archive to s3 - name: Create forgejo release
run: node scripts/uploadToS3.mjs run: node scripts/src/createForgejoRelease.mjs
env:
TAG: "v${{steps.version.outputs.version}}"
FILE: "release.zip"
S3_BUCKET: "${{vars.S3_BUCKET}}"
S3_REGION: "${{vars.S3_REGION}}"
S3_KEY: "${{secrets.S3_KEY}}"
S3_SECRET: "${{secrets.S3_SECRET}}"
S3_ENDPOINT: "${{vars.S3_ENDPOINT}}"
- name: Upload manifest to s3
run: node scripts/uploadToS3.mjs
env:
TAG: "v${{steps.version.outputs.version}}"
FILE: "module.json"
S3_BUCKET: "${{vars.S3_BUCKET}}"
S3_REGION: "${{vars.S3_REGION}}"
S3_KEY: "${{secrets.S3_KEY}}"
S3_SECRET: "${{secrets.S3_SECRET}}"
S3_ENDPOINT: "${{vars.S3_ENDPOINT}}"
- name: Create draft release
run: node scripts/createForgejoRelease.mjs
env: env:
TAG: "v${{steps.version.outputs.version}}" TAG: "v${{steps.version.outputs.version}}"
CDN_URL: "${{vars.CDN_URL}}" CDN_URL: "${{vars.CDN_URL}}"

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "scripts"]
path = scripts
url = https://git.varify.ca/Foundry/scripts.git

View file

@ -17,6 +17,10 @@
"name": "Chat Background", "name": "Chat Background",
"hint": "(v13+) Adds a background to the chat tab of the right-hand sidebar." "hint": "(v13+) Adds a background to the chat tab of the right-hand sidebar."
}, },
"defaultHotbarPage": {
"name": "Default Hotbar Page",
"hint": "What page the hotbar will default to when loading into Foundry."
},
"hotbarButtonGap": { "hotbarButtonGap": {
"name": "Hotbar Button Gap", "name": "Hotbar Button Gap",
"hint": "(v13+) Allows changing the amount of space between the hotbar buttons." "hint": "(v13+) Allows changing the amount of space between the hotbar buttons."

View file

@ -1,8 +1,9 @@
// Settings // Tweaks
import { addGlobalDocReferrer } from "../tweaks/addGlobalDocReferrer.mjs"; import { addGlobalDocReferrer } from "../tweaks/addGlobalDocReferrer.mjs";
import { autoUnpauseOnLoad } from "../tweaks/autoUnpauseOnLoad.mjs"; import { autoUnpauseOnLoad } from "../tweaks/autoUnpauseOnLoad.mjs";
import { chatImageLinks } from "../tweaks/chatImageLinks.mjs"; import { chatImageLinks } from "../tweaks/chatImageLinks.mjs";
import { chatSidebarBackground } from "../tweaks/chatSidebarBackground.mjs"; import { chatSidebarBackground } from "../tweaks/chatSidebarBackground.mjs";
import { defaultHotbarPage } from "../tweaks/defaultHotbarPage.mjs";
import { hotbarButtonGap } from "../tweaks/hotbarButtonGap.mjs"; import { hotbarButtonGap } from "../tweaks/hotbarButtonGap.mjs";
import { hotbarButtonSize } from "../tweaks/hotbarButtonSize.mjs"; import { hotbarButtonSize } from "../tweaks/hotbarButtonSize.mjs";
import { preventTokenRotation } from "../tweaks/preventTokenRotation.mjs"; import { preventTokenRotation } from "../tweaks/preventTokenRotation.mjs";
@ -40,6 +41,7 @@ Hooks.on(`setup`, () => {
restricted: false, restricted: false,
type: HotbarSettingsMenu, type: HotbarSettingsMenu,
}); });
defaultHotbarPage();
hotbarButtonSize(); hotbarButtonSize();
hotbarButtonGap(); hotbarButtonGap();
repositionHotbar(); repositionHotbar();

View file

@ -0,0 +1,37 @@
import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs";
import { __ID__ } from "../consts.mjs";
import { Logger } from "../utils/Logger.mjs";
import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs";
import { registerCategorySetting } from "../utils/SubMenuSettings.mjs";
export const key = `defaultHotbarPage`;
export function defaultHotbarPage() {
status[key] = SettingStatusEnum.Unknown;
if (preventTweakRegistration(key)) { return };
// #region Registration
Logger.log(`Registering setting: ${key}`);
registerCategorySetting(`hotbar`, __ID__, key, {
name: `OFT.setting.${key}.name`,
hint: `OFT.setting.${key}.hint`,
scope: `user`,
type: new foundry.data.fields.NumberField({
min: 1,
max: 5,
step: 1,
}),
default: 1,
config: true,
requiresReload: false,
});
// #endregion Registration
// #region Implementation
Hooks.once(`ready`, () => {
ui.hotbar.changePage(game.settings.get(__ID__, key));
});
// #endregion Implementation
status[key] = SettingStatusEnum.Registered;
};

1788
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,13 @@
{ {
"devDependencies": { "devDependencies": {
"@aws-sdk/client-s3": "^3.946.0",
"@eslint/js": "^9.8.0", "@eslint/js": "^9.8.0",
"@foundryvtt/foundryvtt-cli": "^1.0.3",
"@stylistic/eslint-plugin": "^2.6.1", "@stylistic/eslint-plugin": "^2.6.1",
"axios": "^1.13.2",
"dotenv": "^17.2.2",
"eslint": "^9.8.0", "eslint": "^9.8.0",
"globals": "^15.9.0" "globals": "^15.9.0",
"scripts": "file:./scripts"
}, },
"scripts": { "scripts": {
"link": "node scripts/linkFoundry.mjs", "link": "node scripts/src/linkFoundry.mjs",
"lint": "eslint --fix", "lint": "eslint --fix",
"lint:nofix": "eslint" "lint:nofix": "eslint"
} }

1
scripts Submodule

@ -0,0 +1 @@
Subproject commit edfb087341d74e5fb1398d3833e3d4c8261d215b

View file

@ -1,54 +0,0 @@
import axios from "axios";
const {
TAG,
FORGEJO_SERVER_URL: WEB_URL,
FORGEJO_API_URL: API,
FORGEJO_REPOSITORY: REPO,
FORGEJO_TOKEN: TOKEN,
CDN_URL,
} = process.env;
async function addReleaseAsset(releaseID, name) {
return axios.post(
`${API}/repos/${REPO}/releases/${releaseID}/assets`,
{ external_url: `${CDN_URL}/${REPO}/${TAG}/${name}`, },
{
headers: {
Authorization: `token ${TOKEN}`,
"Content-Type": `multipart/form-data`,
},
params: { name },
}
);
};
async function main() {
// Initial Release Data
const release = await axios.post(
`${API}/repos/${REPO}/releases`,
{
name: TAG,
tag_name: TAG,
draft: true,
hide_archive_links: true,
body: `<!-- Manifest URL: ${WEB_URL}/${REPO}/releases/download/${TAG}/module.json -->`,
},
{
headers: { Authorization: `token ${TOKEN}` },
}
);
try {
await addReleaseAsset(release.data.id, `release.zip`);
await addReleaseAsset(release.data.id, `module.json`);
} catch (e) {
console.error(`Failed to add assets to the release`);
process.exit(1);
};
console.log(`Release created`);
};
main();

View file

@ -1,47 +0,0 @@
import { existsSync } from "fs";
import { symlink, unlink } from "fs/promises";
import { join } from "path";
import { config } from "dotenv";
config({ quiet: true });
const root = process.env.FOUNDRY_ROOT;
// Early exit
if (!root) {
console.error(`Must provide a FOUNDRY_ROOT environment variable`);
process.exit(1);
};
// Assert Foundry exists
if (!existsSync(root)) {
console.error(`Foundry root not found.`);
process.exit(1);
};
// Removing existing symlink
if (existsSync(`foundry`)) {
console.log(`Attempting to unlink foundry instance`);
try {
await unlink(`foundry`);
} catch {
console.error(`Failed to unlink foundry folder.`);
process.exit(1);
};
};
// Account for if the root is pointing at an Electron install
let targetRoot = root;
if (existsSync(join(root, `resources`, `app`))) {
console.log(`Switching to use the "${root}/resources/app" directory`);
targetRoot = join(root, `resources`, `app`);
};
// Create symlink
console.log(`Linking foundry source into folder`);
try {
await symlink(targetRoot, `foundry`);
} catch (e) {
console.error(e);
process.exit(1);
};

View file

@ -1,46 +0,0 @@
/*
The intent of this script is to do all of the modifications of the
manifest file that we need to do in order to release the system.
This can include removing dev-only fields/attributes that end
users will never, and should never, care about nor need.
*/
import { readFile, writeFile } from "fs/promises";
const MANIFEST_PATH = `module.json`;
const {
DOWNLOAD_URL,
LATEST_URL,
} = process.env;
let manifest;
try {
manifest = JSON.parse(await readFile(MANIFEST_PATH, `utf-8`));
console.log(`Manifest loaded from disk`);
} catch {
console.error(`Failed to parse manifest file.`);
process.exit(1);
};
console.log(`Updating download/manifest URLs`);
manifest.download = DOWNLOAD_URL;
manifest.manifest = LATEST_URL;
// Filter out dev-only resources
if (manifest.esmodules) {
console.log(`Removing dev-only esmodules`);
manifest.esmodules = manifest.esmodules.filter(
filepath => !filepath.startsWith(`dev/`)
);
};
// Remove dev flags
console.log(`Cleaning up flags`);
delete manifest.flags?.hotReload;
delete manifest.flags?.inDev;
if (Object.keys(manifest.flags).length === 0) {
delete manifest.flags;
};
await writeFile(MANIFEST_PATH, JSON.stringify(manifest, undefined, `\t`));
console.log(`Manifest written back to disk`);

View file

@ -1,38 +0,0 @@
import axios from "axios";
const {
TAG_NAME,
FORGEJO_API_URL: API_URL,
FORGEJO_REPOSITORY: REPO,
FORGEJO_TOKEN: TOKEN,
} = process.env;
async function main() {
if (!TAG_NAME) {
console.log(`Tag name must not be blank`);
process.exit(1);
};
const requestURL = `${API_URL}/repos/${REPO}/tags/${TAG_NAME}`;
const response = await axios.get(
requestURL,
{
headers: { Authorization: `token ${TOKEN}` },
validateStatus: () => true,
},
);
// We actually *want* an error when the tag exists, instead of when
// it doesn't
if (response.status === 200) {
console.log(`Tag with name "${TAG_NAME}" already exists`);
process.exit(1);
};
console.log(`Tag with name "${TAG_NAME}" not found, proceeding`);
};
main();

View file

@ -1,65 +0,0 @@
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { createReadStream } from "fs";
const requiredEnvVariables = [
`TAG`, `FILE`,
`FORGEJO_REPOSITORY`,
`S3_BUCKET`, `S3_REGION`, `S3_KEY`, `S3_SECRET`, `S3_ENDPOINT`,
];
async function main() {
// Assert all of the required env variables are present
const missing = [];
for (const envVar of requiredEnvVariables) {
if (!(envVar in process.env)) {
missing.push(envVar);
};
};
if (missing.length > 0) {
console.error(`Missing the following required environment variables: ${missing.join(`, `)}`);
process.exit(1);
};
const {
TAG,
S3_ENDPOINT,
S3_REGION,
S3_KEY,
S3_SECRET,
S3_BUCKET,
FILE,
FORGEJO_REPOSITORY: REPO,
} = process.env;
const s3Client = new S3Client({
endpoint: S3_ENDPOINT,
forcePathStyle: false,
region: S3_REGION,
credentials: {
accessKeyId: S3_KEY,
secretAccessKey: S3_SECRET
},
});
const name = FILE.split(`/`).at(-1);
const params = {
Bucket: S3_BUCKET,
Key: `${REPO}/${TAG}/${name}`,
Body: createReadStream(FILE),
ACL: "public-read",
METADATA: {
"x-repo-version": TAG,
},
};
try {
const response = await s3Client.send(new PutObjectCommand(params));
console.log("Upload successful");
} catch (err) {
console.error("Upload to s3 failed");
};
};
main();