From 3c2f1d0735b6e832dfd56880cfb4699fdc1bc47a Mon Sep 17 00:00:00 2001 From: Shark that walks like a man <106829671+stwlam@users.noreply.github.com> Date: Sun, 20 Oct 2024 05:59:55 -0500 Subject: [PATCH] Add an "ABC Picker" as an alternative to opening compendiums from PC sheets (#16896) --- eslint.config.js | 10 +- package-lock.json | 486 ++++++++++++++---- package.json | 21 +- .../character/apps/abc-picker/app.svelte | 172 +++++++ .../actor/character/apps/abc-picker/app.ts | 119 +++++ src/module/actor/character/document.ts | 2 +- src/module/actor/character/sheet.ts | 10 + src/module/system/svelte/mixin.svelte.ts | 62 +++ src/scripts/hooks/load.ts | 4 + src/styles/_application.scss | 39 ++ src/styles/main.scss | 2 + static/lang/en.json | 17 + .../actors/character/tabs/character.hbs | 2 +- tsconfig.json | 6 +- .../client-esm/applications/_types.d.ts | 2 +- types/foundry/client/apps/app.d.ts | 9 +- types/foundry/client/core/searchfilter.d.ts | 92 ++-- vite.config.ts | 9 +- 18 files changed, 899 insertions(+), 165 deletions(-) create mode 100644 src/module/actor/character/apps/abc-picker/app.svelte create mode 100644 src/module/actor/character/apps/abc-picker/app.ts create mode 100644 src/module/system/svelte/mixin.svelte.ts create mode 100644 src/styles/_application.scss diff --git a/eslint.config.js b/eslint.config.js index 7bea831494f..f2375c97273 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,13 +6,12 @@ import prettier from "eslint-plugin-prettier"; import globals from "globals"; import tseslint from "typescript-eslint"; -export default [ - { files: ["**/*.ts"] }, - { ignores: ["**/dist/"] }, +export default tseslint.config( + { ignores: ["dist/", "packs/", "static/"] }, + { plugins: { jest, prettier } }, eslint.configs.recommended, ...tseslint.configs.recommended, { - plugins: { jest, prettier }, languageOptions: { globals: { ...globals.browser, @@ -20,6 +19,7 @@ export default [ }, ecmaVersion: 2023, sourceType: "module", + parser: tseslint.parser, parserOptions: { project: "./tsconfig.json" }, }, rules: { @@ -55,4 +55,4 @@ export default [ files: ["tests/**/*"], rules: { "global-require": "off" }, }, -]; +); diff --git a/package-lock.json b/package-lock.json index f9a47f10597..a91e3ee5204 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,32 +16,34 @@ "luxon": "^3.5.0", "minisearch": "^7.1.0", "nouislider": "^15.8.1", - "remeda": "^2.15.0", + "remeda": "^2.15.2", "sortablejs": "^1.15.3", + "svelte": "^5.0.2", "uuid": "^10.0.0" }, "devDependencies": { - "@eslint/js": "^9.12.0", + "@eslint/js": "^9.13.0", "@pixi/graphics-smooth": "^1.1.0", "@pixi/particle-emitter": "5.0.8", + "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/eslint__js": "^8.42.3", "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.13", "@types/jquery": "^3.5.31", "@types/jsdom": "^21.1.7", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.6", + "@types/node": "^20.16.13", "@types/prompts": "^2.4.9", "@types/showdown": "^2.0.6", "@types/sortablejs": "^1.15.8", "@types/tooltipster": "^0.0.35", "@types/uuid": "^10.0.0", "@types/yaireo__tagify": "4.17.0", - "@typescript-eslint/eslint-plugin": "^8.8.1", - "@typescript-eslint/parser": "^8.8.1", + "@typescript-eslint/eslint-plugin": "^8.10.0", + "@typescript-eslint/parser": "^8.10.0", "classic-level": "^1.3.0", "es-jest": "^2.1.0", - "eslint": "^9.12.0", + "eslint": "^9.13.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.8.3", "eslint-plugin-json": "^4.0.1", @@ -59,15 +61,16 @@ "prompts": "^2.4.2", "prosemirror-commands": "^1.5.2", "prosemirror-view": "^1.33.6", - "sass": "^1.79.5", + "sass": "^1.80.3", "socket.io": "4.7.5", "socket.io-client": "4.7.5", + "svelte-preprocess": "^6.0.3", "tinymce": "6.8.4", "tsconfig-paths": "^4.2.0", "tsx": "^4.19.1", "typescript": "^5.3.3", - "typescript-eslint": "^8.8.1", - "vite": "^5.4.8", + "typescript-eslint": "^8.10.0", + "vite": "^5.4.9", "vite-plugin-checker": "^0.8.0", "vite-plugin-static-copy": "^2.0.0", "vite-tsconfig-paths": "^5.0.1", @@ -90,7 +93,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -1190,9 +1192,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", - "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1224,9 +1226,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", - "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", + "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", "dev": true, "license": "MIT", "engines": { @@ -1697,7 +1699,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1711,7 +1712,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1720,22 +1720,20 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2786,6 +2784,56 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0.tgz", + "integrity": "sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", + "debug": "^4.3.7", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.12", + "vitefu": "^1.0.3" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "3.0.0-next.3", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.0-next.3.tgz", + "integrity": "sha512-kuGJ2CZ5lAw3gKF8Kw0AfKtUJWbwdlDHY14K413B0MCyrzvQvsKTorwmwZcky0+QqY6RnVIZ/5FttB9bQmkLXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2879,7 +2927,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, "license": "MIT" }, "node_modules/@types/fs-extra": { @@ -2980,12 +3027,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "20.16.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz", + "integrity": "sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/prompts": { @@ -3086,17 +3134,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3120,16 +3168,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4" }, "engines": { @@ -3149,14 +3197,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", + "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3167,14 +3215,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", + "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/utils": "8.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -3192,9 +3240,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", + "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", "dev": true, "license": "MIT", "engines": { @@ -3206,14 +3254,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", + "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3261,16 +3309,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", + "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3284,13 +3332,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", + "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3344,7 +3392,6 @@ "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3363,6 +3410,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "license": "MIT", + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -3463,12 +3519,30 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4080,12 +4154,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -4375,18 +4450,18 @@ } }, "node_modules/eslint": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", - "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", + "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.6.0", + "@eslint/core": "^0.7.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.12.0", + "@eslint/js": "9.13.0", "@eslint/plugin-kit": "^0.2.0", "@humanfs/node": "^0.16.5", "@humanwhocodes/module-importer": "^1.0.1", @@ -4560,6 +4635,12 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esm-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", + "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", + "license": "MIT" + }, "node_modules/espree": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", @@ -4616,6 +4697,16 @@ "node": ">=0.10" } }, + "node_modules/esrap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.2.tgz", + "integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -5390,6 +5481,15 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -6254,12 +6354,30 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6318,6 +6436,15 @@ "node": ">=12" } }, + "node_modules/magic-string": { + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -6437,10 +6564,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.7", @@ -6912,6 +7040,38 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7135,9 +7295,9 @@ } }, "node_modules/remeda": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.15.0.tgz", - "integrity": "sha512-Q0Xdg6z3pDKMGVCAI9wGZ+Yz0y0HOzaxxY3wc9gdjJyxqH93LywDUJ4PJ/zhweicYgcB4HbbOliR8HsIdse6mA==", + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.15.2.tgz", + "integrity": "sha512-xcvVY/Xo5D5/s8SjLqEHcYOhmWtzmyyKW4BEaUZ6NVbIHsO03lyxnM5lfy1Mtytehkb9Zo+RJtIsRgkNOEDmEw==", "license": "MIT", "dependencies": { "type-fest": "^4.26.1" @@ -7301,9 +7461,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.79.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz", - "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==", + "version": "1.80.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.3.tgz", + "integrity": "sha512-ptDWyVmDMVielpz/oWy3YP3nfs7LpJTHIJZboMVs8GEC9eUmtZTZhMHlTW98wY4aEorDfjN38+Wr/XjskFWcfA==", "dev": true, "license": "MIT", "dependencies": { @@ -7676,6 +7836,86 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svelte": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.2.tgz", + "integrity": "sha512-TIqp5kjyTMa45L0McUvVfjuvlF/hyxVolyAc9APY3/FeF5aqYpt+Y1PckPQ7DlsDkthxNeq2+ystop8GlIV3kw==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "acorn-typescript": "^1.4.13", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "esm-env": "^1.0.0", + "esrap": "^1.2.2", + "is-reference": "^3.0.2", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-preprocess": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz", + "integrity": "sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3 || ^4.0.0", + "postcss": "^7 || ^8", + "postcss-load-config": ">=3", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": ">=0.55", + "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.100 || ^5.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -8364,15 +8604,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", - "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.10.0.tgz", + "integrity": "sha512-YIu230PeN7z9zpu/EtqCIuRVHPs4iSlqW6TEvjbyDAE3MZsSl2RXBo+5ag+lbABCG8sFM1WVKEXhlQ8Ml8A3Fw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.8.1", - "@typescript-eslint/parser": "8.8.1", - "@typescript-eslint/utils": "8.8.1" + "@typescript-eslint/eslint-plugin": "8.10.0", + "@typescript-eslint/parser": "8.10.0", + "@typescript-eslint/utils": "8.10.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8401,10 +8641,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" }, "node_modules/universalify": { "version": "2.0.1", @@ -8507,9 +8748,9 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "license": "MIT", "dependencies": { @@ -8681,6 +8922,25 @@ } } }, + "node_modules/vitefu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.3.tgz", + "integrity": "sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vscode-json-languageservice": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", @@ -8958,6 +9218,18 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -8996,6 +9268,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 565d56397f2..f96f02821e3 100644 --- a/package.json +++ b/package.json @@ -29,27 +29,28 @@ "node": ">=20.15.0" }, "devDependencies": { - "@eslint/js": "^9.12.0", + "@eslint/js": "^9.13.0", "@pixi/graphics-smooth": "^1.1.0", "@pixi/particle-emitter": "5.0.8", + "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/eslint__js": "^8.42.3", "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.13", "@types/jquery": "^3.5.31", "@types/jsdom": "^21.1.7", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.6", + "@types/node": "^20.16.13", "@types/prompts": "^2.4.9", "@types/showdown": "^2.0.6", "@types/sortablejs": "^1.15.8", "@types/tooltipster": "^0.0.35", "@types/uuid": "^10.0.0", "@types/yaireo__tagify": "4.17.0", - "@typescript-eslint/eslint-plugin": "^8.8.1", - "@typescript-eslint/parser": "^8.8.1", + "@typescript-eslint/eslint-plugin": "^8.10.0", + "@typescript-eslint/parser": "^8.10.0", "classic-level": "^1.3.0", "es-jest": "^2.1.0", - "eslint": "^9.12.0", + "eslint": "^9.13.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.8.3", "eslint-plugin-json": "^4.0.1", @@ -67,15 +68,16 @@ "prompts": "^2.4.2", "prosemirror-commands": "^1.5.2", "prosemirror-view": "^1.33.6", - "sass": "^1.79.5", + "sass": "^1.80.3", "socket.io": "4.7.5", "socket.io-client": "4.7.5", + "svelte-preprocess": "^6.0.3", "tinymce": "6.8.4", "tsconfig-paths": "^4.2.0", "tsx": "^4.19.1", "typescript": "^5.3.3", - "typescript-eslint": "^8.8.1", - "vite": "^5.4.8", + "typescript-eslint": "^8.10.0", + "vite": "^5.4.9", "vite-plugin-checker": "^0.8.0", "vite-plugin-static-copy": "^2.0.0", "vite-tsconfig-paths": "^5.0.1", @@ -89,8 +91,9 @@ "luxon": "^3.5.0", "minisearch": "^7.1.0", "nouislider": "^15.8.1", - "remeda": "^2.15.0", + "remeda": "^2.15.2", "sortablejs": "^1.15.3", + "svelte": "^5.0.2", "uuid": "^10.0.0" } } diff --git a/src/module/actor/character/apps/abc-picker/app.svelte b/src/module/actor/character/apps/abc-picker/app.svelte new file mode 100644 index 00000000000..6479cbba5e6 --- /dev/null +++ b/src/module/actor/character/apps/abc-picker/app.svelte @@ -0,0 +1,172 @@ + + + + + + + diff --git a/src/module/actor/character/apps/abc-picker/app.ts b/src/module/actor/character/apps/abc-picker/app.ts new file mode 100644 index 00000000000..c27308b2816 --- /dev/null +++ b/src/module/actor/character/apps/abc-picker/app.ts @@ -0,0 +1,119 @@ +import { CharacterPF2e } from "@actor"; +import type { ItemPF2e } from "@item"; +import type { ItemType } from "@item/base/data/index.ts"; +import { SvelteApplicationMixin, SvelteApplicationRenderContext } from "@system/svelte/mixin.svelte.ts"; +import { sluggify } from "@util"; +import { UUIDUtils } from "@util/uuid.ts"; +import * as R from "remeda"; +import type { ApplicationConfiguration } from "types/foundry/client-esm/applications/_types.d.ts"; +import type { ApplicationV2 } from "types/foundry/client-esm/applications/api/module.d.ts"; +import Root from "./app.svelte"; + +type AhBCDType = Extract; + +interface ABCPickerConfiguration extends ApplicationConfiguration { + actor: CharacterPF2e; + itemType: AhBCDType; +} + +interface ABCItemRef { + name: string; + img: ImageFilePath; + uuid: ItemUUID; + source: { + name: string; + /** Whether the source comes from an item's publication data or is simply the providing module */ + publication: boolean; + }; + hidden: boolean; +} + +interface ABCPickerContext extends SvelteApplicationRenderContext { + actor: CharacterPF2e; + foundryApp: ABCPicker; + state: { prompt: string; itemType: AhBCDType; items: ABCItemRef[] }; +} + +/** A `Compendium`-like application for presenting A(H)BCD options for a character */ +class ABCPicker extends SvelteApplicationMixin< + AbstractConstructorOf & { DEFAULT_OPTIONS: DeepPartial } +>(foundry.applications.api.ApplicationV2) { + static override DEFAULT_OPTIONS: DeepPartial = { + id: "{id}", + classes: ["abc-picker"], + position: { width: 350, height: 650 }, + window: { icon: "fa-solid fa-atlas", contentClasses: ["standard-form", "compact"] }, + }; + + declare options: ABCPickerConfiguration; + + protected root = Root; + + override get title(): string { + const type = game.i18n.localize(`TYPES.Item.${this.options.itemType}`); + return game.i18n.format("PF2E.Actor.Character.ABCPicker.Title", { type }); + } + + protected override _initializeApplicationOptions(options: Partial): ABCPickerConfiguration { + const initialized = super._initializeApplicationOptions(options) as ABCPickerConfiguration; + initialized.window.icon = `fa-solid ${CONFIG.Item.typeIcons[initialized.itemType]}`; + initialized.uniqueId = `abc-picker-${initialized.itemType}-${initialized.actor.uuid}`; + return initialized; + } + + /** Gather all items of the request type from the world and across all item compendiums. */ + async #gatherItems(): Promise { + const { actor, itemType } = this.options; + const worldItems = game.items.filter((i) => i.type === itemType && i.testUserPermission(game.user, "LIMITED")); + const packItems = await UUIDUtils.fromUUIDs( + game.packs + .filter((p) => p.documentName === "Item" && p.testUserPermission(game.user, "LIMITED")) + .flatMap((p) => p.index.filter((e) => e.type === itemType).map((e) => e.uuid as CompendiumItemUUID)), + ); + + const items = [...worldItems, ...packItems] + .filter((item): item is ItemPF2e => { + if (item.type !== itemType || item.parent) return false; + if (item.isOfType("heritage")) { + const ancestrySlug = actor.ancestry ? (actor.ancestry.slug ?? sluggify(actor.ancestry.name)) : null; + return item.system.ancestry?.slug === ancestrySlug || item.system.ancestry === null; + } + return true; + }) + .sort((a, b) => a.name.localeCompare(b.name)); + + /** Resolve a "source", preferring publication title if set and resorting to fallbacks. */ + const resolveSource = (item: ItemPF2e): { name: string; publication: boolean } => { + const publication = item.system.publication.title.trim(); + if (publication) return { name: publication, publication: true }; + if (item.uuid.startsWith("Item.")) return { name: game.world.title, publication: false }; + const compendiumPack = game.packs.get(item.pack ?? ""); + const module = game.modules.get(compendiumPack?.metadata.packageName ?? ""); + const name = module?.title ?? compendiumPack?.title ?? "???"; + return { name, publication: false }; + }; + + return items.map( + (i): ABCItemRef => ({ + ...R.pick(i, ["name", "img", "uuid"]), + source: resolveSource(i), + hidden: false, + }), + ); + } + + protected override async _prepareContext(): Promise { + const itemType = this.options.itemType; + return { + actor: this.options.actor, + foundryApp: this, + state: { + prompt: game.i18n.localize(`PF2E.Actor.Character.ABCPicker.Prompt.${itemType}`), + itemType, + items: await this.#gatherItems(), + }, + }; + } +} + +export { ABCPicker, type ABCPickerContext }; diff --git a/src/module/actor/character/document.ts b/src/module/actor/character/document.ts index 66ef7d4501c..5f29f548354 100644 --- a/src/module/actor/character/document.ts +++ b/src/module/actor/character/document.ts @@ -1156,7 +1156,7 @@ class CharacterPF2e extends CreatureSheetPF2e // MAIN TAB + handlers["open-abc-picker"] = async (_event, target) => { + const itemType = target.dataset.itemType; + if (!tupleHasValue(["ancestry", "heritage", "background", "class", "deity"], itemType)) { + throw ErrorPF2e("Unexpected item type"); + } + + new ABCPicker({ actor: this.actor, itemType }).render({ force: true }); + }; + handlers["edit-attribute-boosts"] = () => { const builder = Object.values(this.actor.apps).find((a) => a instanceof AttributeBuilder) ?? diff --git a/src/module/system/svelte/mixin.svelte.ts b/src/module/system/svelte/mixin.svelte.ts new file mode 100644 index 00000000000..2d03a829cb5 --- /dev/null +++ b/src/module/system/svelte/mixin.svelte.ts @@ -0,0 +1,62 @@ +import * as svelte from "svelte"; +import type { + ApplicationConfiguration, + ApplicationRenderContext, + ApplicationRenderOptions, +} from "types/foundry/client-esm/applications/_types.d.ts"; +import type ApplicationV2 from "types/foundry/client-esm/applications/api/application.d.ts"; + +interface SvelteApplicationRenderContext { + /** State data tracked by the root component: objects herein must be plain object. */ + state: object; + /** This application instance */ + foundryApp: SvelteApplication; +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +function SvelteApplicationMixin< + TBase extends AbstractConstructorOf & { DEFAULT_OPTIONS: DeepPartial }, +>(Base: TBase) { + abstract class SvelteApplication extends Base { + static override DEFAULT_OPTIONS: DeepPartial = { + classes: ["pf2e"], + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + protected abstract root: svelte.Component; + + /** State data tracked by the root component */ + #reactiveData: object = $state({}); + + /** The mounted root component, saved to be unmounted on application close */ + #mount: object = {}; + + protected override async _renderHTML( + context: SvelteApplicationRenderContext, + ): Promise { + return context; + } + + protected override _replaceHTML( + result: SvelteApplicationRenderContext, + content: HTMLElement, + options: ApplicationRenderOptions, + ): void { + if (options.isFirstRender) { + this.#mount = svelte.mount(this.root, { target: content, props: result }); + } + Object.assign(this.#reactiveData, result.state); + } + + protected override _onClose(options: ApplicationRenderOptions): void { + super._onClose(options); + svelte.unmount(this.#mount); + } + } + + return SvelteApplication; +} + +type SvelteApplication = InstanceType>; + +export { SvelteApplicationMixin, type SvelteApplicationRenderContext }; diff --git a/src/scripts/hooks/load.ts b/src/scripts/hooks/load.ts index 804ef54ba11..ca8acef682e 100644 --- a/src/scripts/hooks/load.ts +++ b/src/scripts/hooks/load.ts @@ -125,15 +125,19 @@ export const Load = { CONFIG.Item.typeIcons = { action: "fa-solid fa-person-running-fast", affliction: "fa-solid fa-biohazard", + ancestry: "fa-solid fa-person-fairy", armor: "fa-solid fa-shirt-long-sleeve", + background: "fa-solid fa-baby", backpack: "fa-solid fa-sack", book: "fa-solid fa-book", + class: "fa-solid fa-user-beard-bolt", condition: "fa-solid fa-face-zany", consumable: "fa-solid fa-flask-round-potion", deity: "fa-solid fa-hamsa", effect: "fa-solid fa-person-rays", equipment: "fa-solid fa-hat-cowboy", feat: "fa-solid fa-medal", + heritage: "fa-solid fa-wreath-laurel", shield: "fa-solid fa-shield-halved", spell: "fa-solid fa-sparkles", treasure: "fa-solid fa-gem", diff --git a/src/styles/_application.scss b/src/styles/_application.scss new file mode 100644 index 00000000000..0114d0e57fc --- /dev/null +++ b/src/styles/_application.scss @@ -0,0 +1,39 @@ +.application { + .window-content.compact { + padding: 0; + gap: var(--space-6); + + > * { + display: flex; + } + } + + button.flat { + align-items: unset; + background: unset; + border: none; + border-radius: unset; + color: unset; + display: unset; + font-family: unset; + font-size: unset; + justify-content: unset; + line-height: unset; + margin: unset; + padding: unset; + text-align: inherit; + width: unset; + + &:hover { + background: inherit; + box-shadow: none; + text-shadow: 0 0 8px var(--color-shadow-primary); + } + + &:focus { + box-shadow: none; + outline: none; + text-shadow: 0 0 8px var(--color-shadow-primary); + } + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss index ed2b1b3948c..f3fb06c9202 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -9,6 +9,8 @@ "globals", // Foundry System stylings "ui", + // General styling for system V2 Applications + "application", // Character Sheets "actor", // Compendium Browser diff --git a/static/lang/en.json b/static/lang/en.json index d2a7e7b3f01..b9e86bfd0ba 100644 --- a/static/lang/en.json +++ b/static/lang/en.json @@ -140,6 +140,14 @@ }, "Attacks": "Attacks", "Character": { + "ABCPicker": { + "SearchPlaceholder": "Search {items}", + "Title": "{type} Selection", + "Tooltip": { + "ConfirmSelection": "Confirm Selection", + "ViewSheet": "View Sheet" + } + }, "Attribute": { "Apex": "Apex Attribute", "Boosts": "Attribute Boosts", @@ -1758,6 +1766,7 @@ }, "Ancestry": { "DropFeatures": "Drop ancestry features here.", + "Plural": "Ancestries", "Vision": { "Label": "Vision", "Normal": "Normal Vision" @@ -1817,11 +1826,17 @@ }, "UnequippedHint": "This armor is bulkier due to not being equipped." }, + "Background": { + "Plural": "Backgrounds" + }, "CampaignFeature": { "CampaignLabel": "Campaign", "Plural": "Campaign Features" }, "CannotAddType": "{type} items cannot be added to this actor.", + "Class": { + "Plural": "Classes" + }, "Condition": { "Chat": { "Current": "Current Conditions" @@ -2228,6 +2243,7 @@ "Hint": "Almost all deities have a single favored weapon, but a few have both a favored weapon and unarmed attack.", "Label": "Favored Weapon(s)" }, + "Plural": "Deities", "Sanctification": { "Can": { "Holy": "can choose holy", @@ -2338,6 +2354,7 @@ "AncestryNotFound": "The referenced ancestry item was not found.", "DragAncestryHere": "Drag an ancestry item here to set this heritage's prerequisite ancestry.", "NoneVersatile": "None (Versatile)", + "Plural": "Heritages", "RemoveAncestry": "Remove prerequisite ancestry" }, "IconLabel": "Icon", diff --git a/static/templates/actors/character/tabs/character.hbs b/static/templates/actors/character/tabs/character.hbs index 94a16d4c23d..2f066f8120f 100644 --- a/static/templates/actors/character/tabs/character.hbs +++ b/static/templates/actors/character/tabs/character.hbs @@ -158,7 +158,7 @@ {{#if item}} {{else}} - + {{/if}} {{/if}} {{#if showEmblem}} diff --git a/tsconfig.json b/tsconfig.json index 637264e5121..b546108f641 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -75,9 +75,11 @@ ] }, "watchOptions": { + "fallbackPolling": "dynamicpriority", "excludeDirectories": [ - "packs/", - "static/packs/" + "dist", + "packs", + "static" ] } } diff --git a/types/foundry/client-esm/applications/_types.d.ts b/types/foundry/client-esm/applications/_types.d.ts index d66e7c51ad0..5c6c60af753 100644 --- a/types/foundry/client-esm/applications/_types.d.ts +++ b/types/foundry/client-esm/applications/_types.d.ts @@ -114,7 +114,7 @@ export interface ApplicationWindowRenderOptions { controls: boolean; } -type ApplicationRenderContext = Record; +type ApplicationRenderContext = object; export interface ApplicationClosingOptions { /** Whether to animate the close, or perform it instantaneously */ diff --git a/types/foundry/client/apps/app.d.ts b/types/foundry/client/apps/app.d.ts index 65e8b3de6d8..55fc7003856 100644 --- a/types/foundry/client/apps/app.d.ts +++ b/types/foundry/client/apps/app.d.ts @@ -348,13 +348,20 @@ declare global { /** The CSS selector used to target the content container for these tabs. */ contentSelector: string; /** A callback function which executes when the filter changes. */ - callback?: Function; + callback?: SearchFilterCallback; /** The initial value of the search query. */ initial?: string; /** The number of milliseconds to wait for text input before processing. */ delay?: number; } + type SearchFilterCallback = ( + event: KeyboardEvent, + query: string, + rgx?: RegExp, + content?: HTMLElement | null, + ) => void; + interface ApplicationHeaderButton { label: string; class: string; diff --git a/types/foundry/client/core/searchfilter.d.ts b/types/foundry/client/core/searchfilter.d.ts index fb6fee064b9..6bb04aecdd0 100644 --- a/types/foundry/client/core/searchfilter.d.ts +++ b/types/foundry/client/core/searchfilter.d.ts @@ -1,60 +1,78 @@ declare class SearchFilter { - /** - * The value of the current query string - */ + /** The allowed Filter Operators which can be used to define a search filter */ + static OPERATORS: { + EQUALS: "equals"; + CONTAINS: "contains"; + STARTS_WITH: "starts_with"; + ENDS_WITH: "ends_with"; + LESS_THAN: "lt"; + LESS_THAN_EQUAL: "lte"; + GREATER_THAN: "gt"; + GREATER_THAN_EQUAL: "gte"; + BETWEEN: "between"; + IS_EMPTY: "is_empty"; + }; + + constructor(config: SearchFilterConfiguration); + + /** The value of the current query string */ query: string; - /** - * A callback function to trigger when the tab is changed - */ - callback: () => void | null; + /** A callback function to trigger when the tab is changed */ + callback: SearchFilterCallback | null; - /** - * The CSS selector used to target the tab navigation element - */ + /** The regular expression corresponding to the query that should be matched against */ + rgx: RegExp; + + /** The CSS selector used to target the tab navigation element */ _inputSelector: string; - /** - * A reference to the HTML navigation element the tab controller is bound to - */ + /** A reference to the HTML navigation element the tab controller is bound to */ _input: HTMLElement | null; - /** - * The CSS selector used to target the tab content element - */ + /** The CSS selector used to target the tab content element */ _contentSelector: string; - /** - * A reference to the HTML container element of the tab content - */ + /** A reference to the HTML container element of the tab content */ _content: HTMLElement | null; + /** A debounced function which applies the search filtering */ + protected _filter: SearchFilterCallback | null; + /** - * A debounced function which applies the search filtering + * Test whether a given object matches a provided filter + * @param obj An object to test against + * @param filter The filter to test + * @returns Whether the object matches the filter */ - protected _filter: () => void; - - constructor({ - inputSelector, - contentSelector, - initial, - callback, - delay, - }?: { - inputSelector: string; - contentSelector: string; - initial: string; - callback: () => void; - delay: number; - }); + static evaluateFilter(obj: object, filter: FieldFilter): boolean; + /** Bind the SearchFilter controller to an HTML application */ bind(html: HTMLElement): void; + /** + * Perform a filtering of the content by invoking the callback function + * @param event The triggering keyboard event + * @param query The input search string + */ + filter(event: KeyboardEvent, query: string): void; + /** * Clean a query term to standardize it for matching. * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize - * @param query An input string which may contain leading/trailing spaces or diacritics - * @returns A cleaned string of ASCII characters for comparison + * @param query An input string which may contain leading/trailing spaces or diacritics + * @returns A cleaned string of ASCII characters for comparison */ static cleanQuery(query: string): string; } + +interface FieldFilter { + /** The dot-delimited path to the field being filtered */ + field: string; + /** The search operator, from CONST.OPERATORS */ + operator?: string; + /** Negate the filter, returning results which do NOT match the filter criteria */ + negate: boolean; + /** The value against which to test */ + value: unknown; +} diff --git a/vite.config.ts b/vite.config.ts index 5945dd7fbf7..4499bd26bf8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,5 @@ import type { ConditionSource } from "@item/base/data/index.ts"; +import { svelte as sveltePlugin } from "@sveltejs/vite-plugin-svelte"; import { execSync } from "child_process"; import esbuild from "esbuild"; import fs from "fs-extra"; @@ -46,7 +47,7 @@ const config = Vite.defineConfig(({ command, mode }): Vite.UserConfig => { 'AbstractDamageRoll.parser = { StartRules: ["Expression"], SyntaxError: peg$SyntaxError, parse: peg$parse };', ); - const plugins = [checker({ typescript: true }), tsconfigPaths()]; + const plugins = [checker({ typescript: true }), tsconfigPaths({ loose: true }), sveltePlugin()]; // Handle minification after build to allow for tree-shaking and whitespace minification // "Note the build.minify option does not minify whitespaces when using the 'es' format in lib mode, as it removes // pure annotations and breaks tree-shaking." @@ -88,7 +89,7 @@ const config = Vite.defineConfig(({ command, mode }): Vite.UserConfig => { }, }, }, - // Vite HMR is only preconfigured for css files: add handler for HBS templates + // Vite HMR is only preconfigured for css files: add handler for HBS templates and localization JSON { name: "hmr-handler", apply: "serve", @@ -131,7 +132,7 @@ const config = Vite.defineConfig(({ command, mode }): Vite.UserConfig => { fs.writeFileSync("./vendor.mjs", `/** ${message} */\n`); } - const reEscape = (s: string) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + const reEscape = (s: string) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); return { base: command === "build" ? "./" : "/systems/pf2e/", @@ -147,7 +148,7 @@ const config = Vite.defineConfig(({ command, mode }): Vite.UserConfig => { esbuild: { keepNames: true }, build: { outDir, - emptyOutDir: false, // fails if world is running due to compendium locks. We do it in "npm run clean" instead. + emptyOutDir: false, // Fails if world is running due to compendium locks: handled with `npm run clean` minify: false, sourcemap: buildMode === "development", lib: {