create-lt-adventure 0.0.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.
Files changed (125) hide show
  1. package/README.md +79 -0
  2. package/addons/github-workflow/addon.json +5 -0
  3. package/addons/github-workflow/main.yml +48 -0
  4. package/addons/github-workflow/setup.ts +112 -0
  5. package/dist/bin.d.ts +3 -0
  6. package/dist/bin.d.ts.map +1 -0
  7. package/dist/bin.js +283 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +3 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/options.d.ts +21 -0
  14. package/dist/options.d.ts.map +1 -0
  15. package/dist/options.js +66 -0
  16. package/dist/options.js.map +1 -0
  17. package/package.json +66 -0
  18. package/templates/vite/.env.example +2 -0
  19. package/templates/vite/README.md +0 -0
  20. package/templates/vite/assets/journals/Read-Aloud_Corner.webp +0 -0
  21. package/templates/vite/assets/journals/Sidebar_Frame_Brown.webp +0 -0
  22. package/templates/vite/assets/journals/Sidebar_Frame_Green.webp +0 -0
  23. package/templates/vite/assets/journals/borders/panel-border-000.webp +0 -0
  24. package/templates/vite/assets/journals/borders/panel-border-001.webp +0 -0
  25. package/templates/vite/assets/journals/borders/panel-border-002.webp +0 -0
  26. package/templates/vite/assets/journals/borders/panel-border-003.webp +0 -0
  27. package/templates/vite/assets/journals/borders/panel-border-004.webp +0 -0
  28. package/templates/vite/assets/journals/borders/panel-border-005.webp +0 -0
  29. package/templates/vite/assets/journals/borders/panel-border-006.webp +0 -0
  30. package/templates/vite/assets/journals/borders/panel-border-007.webp +0 -0
  31. package/templates/vite/assets/journals/borders/panel-border-008.webp +0 -0
  32. package/templates/vite/assets/journals/borders/panel-border-009.webp +0 -0
  33. package/templates/vite/assets/journals/borders/panel-border-010.webp +0 -0
  34. package/templates/vite/assets/journals/borders/panel-border-011.webp +0 -0
  35. package/templates/vite/assets/journals/borders/panel-border-012.webp +0 -0
  36. package/templates/vite/assets/journals/borders/panel-border-013.webp +0 -0
  37. package/templates/vite/assets/journals/borders/panel-border-014.webp +0 -0
  38. package/templates/vite/assets/journals/borders/panel-border-015.webp +0 -0
  39. package/templates/vite/assets/journals/borders/panel-border-016.webp +0 -0
  40. package/templates/vite/assets/journals/borders/panel-border-017.webp +0 -0
  41. package/templates/vite/assets/journals/borders/panel-border-018.webp +0 -0
  42. package/templates/vite/assets/journals/borders/panel-border-019.webp +0 -0
  43. package/templates/vite/assets/journals/borders/panel-border-020.webp +0 -0
  44. package/templates/vite/assets/journals/borders/panel-border-021.webp +0 -0
  45. package/templates/vite/assets/journals/borders/panel-border-022.webp +0 -0
  46. package/templates/vite/assets/journals/borders/panel-border-023.webp +0 -0
  47. package/templates/vite/assets/journals/borders/panel-border-024.webp +0 -0
  48. package/templates/vite/assets/journals/borders/panel-border-025.webp +0 -0
  49. package/templates/vite/assets/journals/borders/panel-border-026.webp +0 -0
  50. package/templates/vite/assets/journals/borders/panel-border-027.webp +0 -0
  51. package/templates/vite/assets/journals/borders/panel-border-028.webp +0 -0
  52. package/templates/vite/assets/journals/borders/panel-border-029.webp +0 -0
  53. package/templates/vite/assets/journals/borders/panel-border-030.webp +0 -0
  54. package/templates/vite/assets/journals/borders/panel-border-031.webp +0 -0
  55. package/templates/vite/assets/journals/fonts/Candara.ttf +0 -0
  56. package/templates/vite/assets/journals/fonts/CoinageCapsKrugerGray.ttf +0 -0
  57. package/templates/vite/assets/journals/fonts/GeizerModifiedO.otf +0 -0
  58. package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Bold.otf +0 -0
  59. package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_BoldItalic.otf +0 -0
  60. package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Italic.otf +0 -0
  61. package/templates/vite/assets/journals/fonts/Kings Caslon/Kings_Caslon_Regular.otf +0 -0
  62. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Bold.otf +0 -0
  63. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_BoldItalic.otf +0 -0
  64. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Light.otf +0 -0
  65. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_LightItalic.otf +0 -0
  66. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_Medium.otf +0 -0
  67. package/templates/vite/assets/journals/fonts/Modesto/ModestoText_MediumItalic.otf +0 -0
  68. package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Bold.otf +0 -0
  69. package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Light.otf +0 -0
  70. package/templates/vite/assets/journals/fonts/Modesto/Modesto_Condensed_Regular.otf +0 -0
  71. package/templates/vite/assets/journals/headers/bg-header-length-01.svg +61 -0
  72. package/templates/vite/assets/journals/headers/bg-header-length-02.svg +59 -0
  73. package/templates/vite/assets/journals/headers/bg-header-length-03.svg +66 -0
  74. package/templates/vite/assets/journals/headers/bg-header-length-04.svg +58 -0
  75. package/templates/vite/assets/journals/headers/bg-header-length-05.svg +75 -0
  76. package/templates/vite/assets/journals/headers/bg-header-length-06.svg +73 -0
  77. package/templates/vite/assets/journals/headers/bg-header-length-07.svg +69 -0
  78. package/templates/vite/assets/journals/masks/flipped/mask-brush-02.webp +0 -0
  79. package/templates/vite/assets/journals/masks/flipped/mask-brush-03.webp +0 -0
  80. package/templates/vite/assets/journals/masks/flipped/mask-brush-04.webp +0 -0
  81. package/templates/vite/assets/journals/masks/flipped/mask-brush-05.webp +0 -0
  82. package/templates/vite/assets/journals/masks/flipped/mask-brush-06.webp +0 -0
  83. package/templates/vite/assets/journals/masks/flipped/mask-hard-01.webp +0 -0
  84. package/templates/vite/assets/journals/masks/flipped/mask-hard-02.webp +0 -0
  85. package/templates/vite/assets/journals/masks/flipped/mask-hard-03.webp +0 -0
  86. package/templates/vite/assets/journals/masks/flipped/mask-hard-04.webp +0 -0
  87. package/templates/vite/assets/journals/masks/flipped/mask-hard-05.webp +0 -0
  88. package/templates/vite/assets/journals/masks/flipped/mask-hard-06.webp +0 -0
  89. package/templates/vite/assets/journals/masks/flipped/mask-soft-01.webp +0 -0
  90. package/templates/vite/assets/journals/masks/flipped/mask-soft-02.webp +0 -0
  91. package/templates/vite/assets/journals/masks/flipped/mask-soft-03.webp +0 -0
  92. package/templates/vite/assets/journals/masks/flipped/mask-soft-04.webp +0 -0
  93. package/templates/vite/assets/journals/masks/flipped/mask-soft-05.webp +0 -0
  94. package/templates/vite/assets/journals/masks/flipped/mask-soft-06.webp +0 -0
  95. package/templates/vite/assets/journals/masks/mask-brush-02.webp +0 -0
  96. package/templates/vite/assets/journals/masks/mask-brush-03.webp +0 -0
  97. package/templates/vite/assets/journals/masks/mask-brush-04.webp +0 -0
  98. package/templates/vite/assets/journals/masks/mask-brush-05.webp +0 -0
  99. package/templates/vite/assets/journals/masks/mask-brush-06.webp +0 -0
  100. package/templates/vite/assets/journals/masks/mask-hard-01.webp +0 -0
  101. package/templates/vite/assets/journals/masks/mask-hard-02.webp +0 -0
  102. package/templates/vite/assets/journals/masks/mask-hard-03.webp +0 -0
  103. package/templates/vite/assets/journals/masks/mask-hard-04.webp +0 -0
  104. package/templates/vite/assets/journals/masks/mask-hard-05.webp +0 -0
  105. package/templates/vite/assets/journals/masks/mask-hard-06.webp +0 -0
  106. package/templates/vite/assets/journals/masks/mask-soft-01.webp +0 -0
  107. package/templates/vite/assets/journals/masks/mask-soft-02.webp +0 -0
  108. package/templates/vite/assets/journals/masks/mask-soft-03.webp +0 -0
  109. package/templates/vite/assets/journals/masks/mask-soft-04.webp +0 -0
  110. package/templates/vite/assets/journals/masks/mask-soft-05.webp +0 -0
  111. package/templates/vite/assets/journals/masks/mask-soft-06.webp +0 -0
  112. package/templates/vite/assets/setup.webp +0 -0
  113. package/templates/vite/bun.lock +983 -0
  114. package/templates/vite/lang/en.json +1 -0
  115. package/templates/vite/module.json +36 -0
  116. package/templates/vite/package.json +29 -0
  117. package/templates/vite/scripts/extractPacks.mjs +50 -0
  118. package/templates/vite/scripts/onCreate.mjs +14 -0
  119. package/templates/vite/scripts/symlink.mjs +79 -0
  120. package/templates/vite/src/adventureSheet/index.js +498 -0
  121. package/templates/vite/src/index.js +5 -0
  122. package/templates/vite/src/misc/fonts.js +50 -0
  123. package/templates/vite/src/misc/prosemirror.js +47 -0
  124. package/templates/vite/src/module.css +284 -0
  125. package/templates/vite/vite.config.ts +110 -0
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "module-template",
3
+ "title": "Module Template",
4
+ "description": "",
5
+ "authors": [
6
+ {
7
+ "name": "Vauxs",
8
+ "discord": "vauxs",
9
+ "twitter": "@ThatVauxs",
10
+ "email": "mrvauxs@gmail.com",
11
+ "ko-fi": "MrVauxs",
12
+ "patreon": "MrVauxs"
13
+ }
14
+ ],
15
+ "version": "dev",
16
+ "compatibility": {
17
+ "minimum": "13",
18
+ "verified": "13"
19
+ },
20
+ "esmodules": [],
21
+ "styles": [],
22
+ "relationships": {},
23
+ "packs": [],
24
+ "packFolders": [],
25
+ "flags": {
26
+ "canUpload": true,
27
+ "hotReload": {
28
+ "extensions": ["css", "json"],
29
+ "paths": ["styles"]
30
+ }
31
+ },
32
+ "url": "",
33
+ "bugs": "",
34
+ "manifest": "",
35
+ "download": ""
36
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "my-new-module",
3
+ "type": "module",
4
+ "private": true,
5
+ "license": "MIT",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "vite",
9
+ "extract": "node scripts/extractPacks.mjs",
10
+ "symlink": "node scripts/symlink.mjs"
11
+ },
12
+ "devDependencies": {
13
+ "@clack/prompts": "^1.0.0-alpha.9",
14
+ "@foundryvtt/foundryvtt-cli": "^3.0.2",
15
+ "@types/jquery": "^3.5.33",
16
+ "dotenv": "^17.2.3",
17
+ "foundry-pf2e": "github:7H3LaughingMan/foundry-pf2e",
18
+ "foundryvtt-sync": "https://github.com/MrVauxs/FoundryVTT-Sync",
19
+ "kolorist": "^1.8.0",
20
+ "postcss-preset-env": "^10.6.0",
21
+ "postcss-replace": "^2.0.1",
22
+ "typescript": "^5.9.3",
23
+ "vite": "^7.3.0"
24
+ },
25
+ "browserslist": [
26
+ ">5%",
27
+ "not IE 11"
28
+ ]
29
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from "node:fs";
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import * as p from "@clack/prompts";
7
+ import { extractPack } from "@foundryvtt/foundryvtt-cli";
8
+ import { yellow } from "kolorist";
9
+ // import moduleJSON from "../module.json" with { type: "json" };
10
+
11
+ const foundryDataDir = "packs/";
12
+ const jsonDataDir = "data/";
13
+
14
+ p.intro(`Extracting ${foundryDataDir} into ${jsonDataDir}...`)
15
+
16
+ const outDir = path.resolve(process.cwd());
17
+ const packsCompiled = path.resolve(outDir, foundryDataDir);
18
+ if (!existsSync(packsCompiled)) {
19
+ p.log.warn("Packs directory does not exist in the build!")
20
+ }
21
+
22
+ const packFolders = await fs.readdir(packsCompiled);
23
+
24
+ if (packFolders.length === 0) p.log.info("No packs to extract!")
25
+
26
+ await p.tasks(
27
+ packFolders.map(pack => ({
28
+ title: `Extracting ${pack}...`,
29
+ task: async () => {
30
+ if (!existsSync(`${jsonDataDir}/${pack}`)) {
31
+ await fs.mkdir(`${jsonDataDir}/${pack}`);
32
+ }
33
+ await extractPack(
34
+ path.resolve(packsCompiled, pack),
35
+ `${jsonDataDir}/${pack}`,
36
+ {
37
+ expandAdventures: true,
38
+ omitVolatile: true,
39
+ folders: false,
40
+ clean: true,
41
+ log: false
42
+ },
43
+ );
44
+ return `Extracted ${yellow(pack)}!`
45
+ }
46
+ })),
47
+ {}
48
+ );
49
+
50
+ p.outro("Finished!");
@@ -0,0 +1,14 @@
1
+ const mod = (await Bun.file("../module.json").json());
2
+ const pack = (await Bun.file("../package.json").json());
3
+
4
+ // Module
5
+ mod.esmodules = [`dist/${mod.id}.js`];
6
+ mod.styles = [`dist/${mod.id}.css`];
7
+
8
+ // Package
9
+ pack.name = mod.id;
10
+
11
+ await Bun.write("../module.json", JSON.stringify(mod, null, "\t"));
12
+ await Bun.write("../package.json", JSON.stringify(pack, null, "\t"));
13
+
14
+ export { };
@@ -0,0 +1,79 @@
1
+ import { lstatSync, rmSync, symlinkSync, unlinkSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import process from "node:process";
5
+ import * as p from "@clack/prompts";
6
+ import moduleJSON from "../module.json" with { type: "json" };
7
+ import { yellow } from "kolorist";
8
+
9
+ p.intro(`${moduleJSON.id} symlink script`)
10
+
11
+ // Store config in user's home directory
12
+ const configPath = resolve(homedir(), ".foundry-symlink-config.json");
13
+
14
+ // Load last known path from config
15
+ let lastPath = null;
16
+ try {
17
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
18
+ lastPath = config.dataPath;
19
+ } catch {
20
+ // Config doesn't exist yet, that's fine
21
+ }
22
+
23
+ const windowsInstructions = process.platform === "win32" ? " Start with a drive letter (\"C:\\\")." : "";
24
+ const lastFolder = lastPath ? `(last: ${lastPath})` : "";
25
+ const promptPath = await p.text({
26
+ message: `Enter the full path to your Foundry data folder.${windowsInstructions}`,
27
+ placeholder: lastPath,
28
+ initialValue: lastPath,
29
+ validate(val) {
30
+ const value = val.replace(/\W*$/, "").trim();
31
+ if (!value || !/\bData$/.test(value)) {
32
+ return (`"${value}" does not contain ${yellow('/Data')}`);
33
+ }
34
+ }
35
+ });
36
+
37
+ let dataPath = promptPath.replace(/\W*$/, "").trim();
38
+
39
+ if (dataPath !== lastPath) {
40
+ // Ask if user wants to save the path
41
+ const shouldSave = await p.confirm({
42
+ initialValue: true,
43
+ message: `Save "${dataPath}" for future use?`,
44
+ });
45
+
46
+ if (shouldSave) {
47
+ writeFileSync(configPath, JSON.stringify({ dataPath }, null, 2));
48
+ }
49
+ }
50
+
51
+ const symlinkPath = resolve(dataPath, "modules", moduleJSON.id);
52
+ const symlinkStats = lstatSync(symlinkPath, { throwIfNoEntry: false });
53
+ if (symlinkStats) {
54
+ const atPath = symlinkStats.isDirectory() ? "folder" : symlinkStats.isSymbolicLink() ? "symlink" : "file";
55
+ const proceed = await p.confirm({
56
+ initialValue: false,
57
+ message: `A "${moduleJSON.id}" ${atPath} already exists in the "modules" subfolder. Replace with new symlink?`,
58
+ });
59
+ if (!proceed) {
60
+ p.cancel("Aborting.");
61
+ process.exit(0);
62
+ }
63
+ }
64
+
65
+ try {
66
+ if (symlinkStats?.isDirectory()) {
67
+ rmSync(symlinkPath, { recursive: true, force: true });
68
+ } else if (symlinkStats) {
69
+ unlinkSync(symlinkPath);
70
+ }
71
+ symlinkSync(resolve(process.cwd()), symlinkPath);
72
+ } catch (error) {
73
+ if (error instanceof Error) {
74
+ console.error(`An error was encountered trying to create a symlink: ${error.message}`);
75
+ process.exit(1);
76
+ }
77
+ }
78
+
79
+ p.outro(`Symlink successfully created at "${symlinkPath}"!`);
@@ -0,0 +1,498 @@
1
+ import moduleJSON from "moduleJSON";
2
+
3
+ const affectedDocuments = ["journal"];
4
+
5
+ const backgrounds = [
6
+ { maxWidth: 130, image: "bg-header-length-01.svg" },
7
+ { maxWidth: 150, image: "bg-header-length-02.svg" },
8
+ { maxWidth: 210, image: "bg-header-length-03.svg" },
9
+ { maxWidth: 250, image: "bg-header-length-04.svg" },
10
+ { maxWidth: 300, image: "bg-header-length-05.svg" },
11
+ { maxWidth: 400, image: "bg-header-length-06.svg" },
12
+ { maxWidth: Infinity, image: "bg-header-length-07.svg" },
13
+ ];
14
+
15
+ const DnDSheet = CONFIG.JournalEntry.sheetClasses.base['dnd5e.JournalEntrySheet5e'].cls;
16
+
17
+ export class LootTavernSheet extends DnDSheet {
18
+ static DEFAULT_OPTIONS = {
19
+ classes: [`${moduleJSON.id}-journal`],
20
+ actions: {
21
+ "openLootDevTools": this.openLootDevTools
22
+ },
23
+ window: {
24
+ controls: [
25
+ {
26
+ "icon": "fa-solid fa-wrench",
27
+ "label": "Loot Tavern Dev Tools",
28
+ "action": "openLootDevTools",
29
+ visible: () => moduleJSON.version === "dev",
30
+ }
31
+ ]
32
+ }
33
+ }
34
+
35
+ static openLootDevTools(event) {
36
+ event.stopPropagation(); // Don't trigger other events
37
+ if (event.detail > 1) return; // Ignore repeated clicks
38
+ const docSheetConfigWidth = foundry.applications.apps.DocumentSheetConfig.DEFAULT_OPTIONS.position.width;
39
+ const fields = foundry.applications.fields;
40
+
41
+ const placeholder = `modules/${moduleJSON.id}/assets/setup.webp`
42
+
43
+ const headersInput = fields.createCheckboxInput({ name: "helianaHeaders", value: this.document.getFlag(moduleJSON.id, "helianaHeaders"), placeholder })
44
+ const selectGroup = fields.createFormGroup({
45
+ input: headersInput,
46
+ label: "Heliana-styled Headers",
47
+ hint: undefined
48
+ })
49
+
50
+ const imageInput = fields.createTextInput({ name: "image", value: this.document.getFlag(moduleJSON.id, "image"), placeholder })
51
+ const imageGroup = fields.createFormGroup({
52
+ input: imageInput,
53
+ label: "Main Image",
54
+ hint: undefined
55
+ })
56
+
57
+ const sidebarImageInput = fields.createTextInput({ name: "sidebarImage", value: this.document.getFlag(moduleJSON.id, "sidebarImage"), placeholder })
58
+ const sidebarImageGroup = fields.createFormGroup({
59
+ input: sidebarImageInput,
60
+ label: "Sidebar Background Image",
61
+ hint: undefined
62
+ })
63
+
64
+ const borderNumberInput = fields.createSelectInput({
65
+ options: Array(31).fill().map((x, i) => ({ label: i, value: i })),
66
+ name: 'borderNumber',
67
+ value: this.document.getFlag(moduleJSON.id, "borderNumber") || 26,
68
+ })
69
+ const borderNumberGroup = fields.createFormGroup({
70
+ input: borderNumberInput,
71
+ label: "Border Select",
72
+ hint: "Pick a style of border you want."
73
+ })
74
+
75
+ const formContent = [
76
+ selectGroup,
77
+ imageGroup,
78
+ sidebarImageGroup,
79
+ borderNumberGroup
80
+ ]
81
+
82
+ const updateFlags = (flags) => this.document.update({ [`flags.${moduleJSON.id}`]: flags });
83
+
84
+ const dialog = new foundry.applications.api.DialogV2({
85
+ id: `${this.id}-dev-menu`,
86
+ classes: [],
87
+ window: {
88
+ title: `${this.document.name} Dev Config`,
89
+ resizable: true,
90
+ },
91
+ position: {
92
+ width: 550,
93
+ top: this.position.top + 40,
94
+ left: this.position.left + ((this.position.width - docSheetConfigWidth) / 2)
95
+ },
96
+ // ../../assets/Art/ReignOfIron-Landscape-Vertical.webp
97
+ content: formContent.map(x => x.outerHTML).join(""),
98
+ buttons: [
99
+ {
100
+ action: "submit",
101
+ label: "Submit",
102
+ default: true,
103
+ callback: async function (event, target) {
104
+ const flags = Object.fromEntries(Object.values(target.form.elements).map(x => [x.name, x.checked || x.value]).filter(x => x[0]))
105
+
106
+ await updateFlags(flags)
107
+ },
108
+ },
109
+ ],
110
+ });
111
+ dialog.render(true);
112
+ }
113
+
114
+ async render(force, options) {
115
+ const doc = this.document;
116
+ const modFlags = doc.flags?.[moduleJSON.id] ?? {};
117
+ await super.render(force, options);
118
+
119
+ const html = this.element;
120
+
121
+ // Sidebar Background
122
+ const sidebarImg = this.document.getFlag(moduleJSON.id, "sidebarImage");
123
+ if (sidebarImg) html.style.setProperty("--sidebarImage", `url("/${sidebarImg}")`);
124
+
125
+ const borderNumber = this.document.getFlag(moduleJSON.id, "borderNumber");
126
+ if (!isNaN(borderNumber)) html.style.setProperty("--urlBorderImage", `url("/modules/${moduleJSON.id}/assets/journals/borders/panel-border-${("00" + Number(borderNumber)).slice(-3)}.webp")`);
127
+
128
+ let borderRadius = "7px";
129
+ let borderSizeMod = "0px";
130
+ let borderCut = "32";
131
+ let borderOutset = "32";
132
+ switch (Number(borderNumber)) {
133
+ case 0: {
134
+ borderRadius = `7px`;
135
+ borderSizeMod = `25px`;
136
+ break;
137
+ }
138
+ case 1: {
139
+ borderRadius = `15px`;
140
+ borderSizeMod = `0px`;
141
+ break;
142
+ }
143
+ case 2: {
144
+ borderRadius = `15px`;
145
+ borderSizeMod = `0px`;
146
+ break;
147
+ }
148
+ case 3: {
149
+ borderRadius = `0px`;
150
+ borderSizeMod = `35px`;
151
+ break;
152
+ }
153
+ case 4: {
154
+ borderRadius = `6px`;
155
+ borderSizeMod = `20px`;
156
+ break;
157
+ }
158
+ case 5: {
159
+ borderRadius = `0px`;
160
+ borderSizeMod = `0px`;
161
+ break;
162
+ }
163
+ case 6: {
164
+ borderRadius = `10px`;
165
+ borderSizeMod = `0px`;
166
+ break;
167
+ }
168
+ case 7: {
169
+ borderRadius = `15px`;
170
+ borderSizeMod = `0px`;
171
+ break;
172
+ }
173
+ case 8:
174
+ case 9: {
175
+ borderRadius = `10px`;
176
+ borderSizeMod = `12px`;
177
+ break;
178
+ }
179
+ case 10: {
180
+ borderRadius = `16px`;
181
+ borderSizeMod = `5px`;
182
+ break;
183
+ }
184
+ case 11:
185
+ case 12: {
186
+ borderRadius = `16px`;
187
+ borderSizeMod = `5px`;
188
+ break;
189
+ }
190
+ case 13: {
191
+ borderRadius = `12px`;
192
+ borderSizeMod = `5px`;
193
+ break;
194
+ }
195
+ case 14: {
196
+ borderRadius = `10px`;
197
+ borderSizeMod = `20px`;
198
+ break;
199
+ }
200
+ case 15: {
201
+ borderRadius = `0px`;
202
+ borderSizeMod = `5px`;
203
+ break;
204
+ }
205
+ case 16: {
206
+ borderRadius = `8px`;
207
+ borderSizeMod = `10px`;
208
+ break;
209
+ }
210
+ case 17: {
211
+ borderRadius = `11px`;
212
+ borderSizeMod = `10px`;
213
+ break;
214
+ }
215
+ case 18:
216
+ case 19: {
217
+ borderRadius = `16px`;
218
+ borderSizeMod = `0px`;
219
+ break;
220
+ }
221
+ case 20: {
222
+ borderRadius = `0px`;
223
+ borderSizeMod = `10px`;
224
+ borderCut = `40`
225
+ borderOutset = `46`
226
+ break;
227
+ }
228
+ case 21: {
229
+ borderRadius = `25px`;
230
+ borderSizeMod = `0px`;
231
+ break;
232
+ }
233
+ case 22: {
234
+ borderRadius = `10px`;
235
+ borderSizeMod = `6px`;
236
+ break;
237
+ }
238
+ case 23: {
239
+ borderRadius = `24px`;
240
+ borderSizeMod = `0px`;
241
+ break;
242
+ }
243
+ case 24: {
244
+ borderRadius = `0px`;
245
+ borderSizeMod = `50px`;
246
+ break;
247
+ }
248
+ case 25: {
249
+ borderRadius = `27px`;
250
+ borderSizeMod = `0px`;
251
+ borderCut = `40`
252
+ borderOutset = `32`
253
+ break;
254
+ }
255
+ case 26: {
256
+ borderRadius = `30px`;
257
+ borderSizeMod = `0px`;
258
+ break;
259
+ }
260
+ case 27: {
261
+ borderRadius = `0px`;
262
+ borderSizeMod = `8px`;
263
+ break;
264
+ }
265
+ case 28: {
266
+ borderRadius = `15px`;
267
+ borderSizeMod = `8px`;
268
+ break;
269
+ }
270
+ case 29: {
271
+ borderRadius = `15px`;
272
+ borderSizeMod = `8px`;
273
+ break;
274
+ }
275
+ case 30: {
276
+ borderRadius = `15px`;
277
+ borderSizeMod = `0px`;
278
+ break;
279
+ }
280
+ default: {
281
+ console.log("wtf??", borderNumber)
282
+ }
283
+ }
284
+
285
+ html.style.setProperty("--ltBorderRadius", borderRadius);
286
+ html.style.setProperty("--ltBorderSizeMod", borderSizeMod);
287
+ html.style.setProperty("--ltBorderCut", borderCut);
288
+ html.style.setProperty("--ltBorderOutset", borderOutset);
289
+
290
+ if (this.mode === 2) {
291
+ const imgPath = this.document.flags[moduleJSON.id]?.image;
292
+ if (!imgPath) return;
293
+ const scrollable = html.querySelector(".journal-entry-content .scrollable");
294
+
295
+ // Create and insert scenery element
296
+ const sceneryDiv = document.createElement("div");
297
+ sceneryDiv.className = "scenery mask upside-down";
298
+ sceneryDiv.innerHTML = `<img src="/${imgPath}">`;
299
+ scrollable.prepend(sceneryDiv);
300
+
301
+ const scenery = scrollable.querySelector(".scenery");
302
+
303
+ scrollable.addEventListener("scroll", ({ target }) => {
304
+ const opacity = Math.max(10, 100 - target.scrollTop / 2);
305
+ scenery.style.opacity = `${opacity}%`;
306
+ scenery.style.pointerEvents = opacity < 50 ? "none" : "auto";
307
+ });
308
+
309
+
310
+ const header = html.querySelector("header.journal-header");
311
+ header.remove();
312
+
313
+ const title = document.createElement('header');
314
+ title.textContent = this.document.name;
315
+ scenery.appendChild(title);
316
+ }
317
+
318
+ // Level Select
319
+ const levelSelect = html.querySelector("#level-select");
320
+
321
+ if (levelSelect) {
322
+ // Add radio inputs to each level
323
+ const levelOptions = levelSelect.querySelectorAll("[data-level]");
324
+ levelOptions.forEach((option) => {
325
+ const input = document.createElement("input");
326
+ input.type = "radio";
327
+ input.name = "level-selector";
328
+ option.prepend(input);
329
+ });
330
+
331
+ // Handle radio button clicks
332
+ const radioInputs = levelSelect.querySelectorAll("input[type='radio']");
333
+ radioInputs.forEach((radio) => {
334
+ radio.addEventListener("click", function () {
335
+ const radioElement = this;
336
+
337
+ if (radioElement.checked) {
338
+ // Uncheck all radios in the group
339
+ const groupName = radioElement.name;
340
+ const allRadios = levelSelect.querySelectorAll(`input[type='radio'][name='${groupName}']`);
341
+ allRadios.forEach((r) => {
342
+ (r).checked = false;
343
+ });
344
+
345
+ // Check the clicked radio
346
+ radioElement.checked = true;
347
+
348
+ // Get level and update
349
+ const parent = radioElement.parentElement;
350
+ const level = parseInt(parent.dataset.level || "0", 10);
351
+
352
+ ui.notifications.info(`Setting the adventure level to ${level}!`);
353
+ doc.update({ flags: { [moduleJSON.id]: { level } } });
354
+
355
+ // Determine changes based on level
356
+ let changes = [11, 3, 3, "1d6"];
357
+ switch (level) {
358
+ case 1:
359
+ case 2:
360
+ changes = [11, 3, 3, "1d6"];
361
+ break;
362
+ case 3:
363
+ case 4:
364
+ changes = [12, 4, 3, "1d6"];
365
+ break;
366
+ case 5:
367
+ case 6:
368
+ changes = [13, 5, 5, "2d4"];
369
+ break;
370
+ case 7:
371
+ case 8:
372
+ changes = [14, 6, 7, "2d6"];
373
+ break;
374
+ case 9:
375
+ case 10:
376
+ case 11:
377
+ changes = [15, 7, 10, "3d6"];
378
+ break;
379
+ case 12:
380
+ case 13:
381
+ case 14:
382
+ changes = [16, 8, 14, "4d6"];
383
+ break;
384
+ case 15:
385
+ case 16:
386
+ case 17:
387
+ changes = [17, 9, 21, "6d6"];
388
+ break;
389
+ case 18:
390
+ case 19:
391
+ case 20:
392
+ changes = [18, 10, 28, "8d6"];
393
+ break;
394
+ }
395
+
396
+ // Update all flags
397
+ doc.update({
398
+ flags: {
399
+ [moduleJSON.id]: {
400
+ level,
401
+ dc: changes[0],
402
+ mod: changes[1],
403
+ damage: changes[2],
404
+ damageDice: changes[3],
405
+ },
406
+ },
407
+ });
408
+ } else {
409
+ (this).checked = false;
410
+ }
411
+ });
412
+ });
413
+
414
+ // Set checked state for previously chosen level
415
+ const chosenLevel = modFlags.level;
416
+ if (chosenLevel) {
417
+ const chosenOption = levelSelect.querySelector(`[data-level="${chosenLevel}"]`);
418
+ if (chosenOption) {
419
+ const chosenInput = chosenOption.querySelector("input");
420
+ chosenInput.checked = true;
421
+ }
422
+ }
423
+ }
424
+ }
425
+
426
+ async _renderPageViews(context, options) {
427
+ const rendered = await super._renderPageViews(context, options);
428
+ this.applyHelianaHeaders()
429
+ return rendered;
430
+ }
431
+
432
+ applyHelianaHeaders(target = this.element) {
433
+ const headers = target.querySelectorAll("h2");
434
+ headers.forEach((h2) => {
435
+ const parent = h2.parentElement;
436
+ if (!parent) return;
437
+ parent.classList.add("heliana-style-bg");
438
+
439
+ // Find appropriate background based on header width
440
+ const background = backgrounds.find((bg) => h2.offsetWidth <= bg.maxWidth);
441
+
442
+ if (background) {
443
+ parent.style.backgroundImage = `url("/modules/${moduleJSON.id}/assets/journals/headers/${background.image}")`;
444
+ }
445
+ });
446
+ }
447
+
448
+ async deleteSelf() {
449
+ await this.close({ force: true });
450
+ // @ts-expect-error Intentional for purposes of HMR
451
+ this.document._sheet = null;
452
+ }
453
+ }
454
+
455
+ function registerSheet(sheet) {
456
+ foundry.applications.apps.DocumentSheetConfig.registerSheet(JournalEntry, moduleJSON.id, sheet, {
457
+ label: `${moduleJSON.title} Sheet`,
458
+ canBeDefault: false,
459
+ });
460
+ }
461
+
462
+ function unregisterSheet(sheet) {
463
+ foundry.applications.apps.DocumentSheetConfig.unregisterSheet(JournalEntry, moduleJSON.id, sheet);
464
+ }
465
+
466
+ // Assuming we are inside a Hooks.on("ready")
467
+ registerSheet(LootTavernSheet)
468
+
469
+ if (import.meta.hot) {
470
+ import.meta.hot.accept(async (newModule) => {
471
+ if (!newModule) return;
472
+ let reopenedDocuments = [];
473
+
474
+ unregisterSheet(LootTavernSheet);
475
+
476
+ for (const type of affectedDocuments) {
477
+ for (const doc of game[type].contents) {
478
+ // @ts-expect-error Custom function for LootTavernSheets
479
+ if (doc.sheet.deleteSelf) {
480
+ // @ts-expect-error Custom function for LootTavernSheets
481
+ await doc.sheet.deleteSelf();
482
+ reopenedDocuments.push(doc.uuid);
483
+ }
484
+ }
485
+ };
486
+
487
+ registerSheet(newModule.LootTavernSheet);
488
+ console.log(`Registered new ${newModule.LootTavernSheet.name} sheet.`)
489
+
490
+ reopenedDocuments.forEach(async (uuid) => {
491
+ const doc = await fromUuid(uuid);
492
+ if (!doc) return;
493
+
494
+ doc?.sheet?.render(true);
495
+ });
496
+ reopenedDocuments = [];
497
+ });
498
+ }