jaml-ui 0.26.6 → 0.28.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.
- package/README.md +11 -6
- package/dist/assets/{searchPoolWorker-CejAnH4a.js → searchPoolWorker-BtlROziN.js} +9 -4
- package/dist/assets/searchPoolWorker-BtlROziN.js.map +1 -0
- package/dist/chunks/Layer-BBPJFHfs.js.map +1 -1
- package/dist/chunks/assets-RWUiFSTc.js.map +1 -1
- package/dist/chunks/motelyItemDecoder-BvFrQpaI.js +2061 -0
- package/dist/chunks/motelyItemDecoder-BvFrQpaI.js.map +1 -0
- package/dist/chunks/{searchPoolWorker-CTtPOuxF.js → searchPoolWorker-D1xR43D2.js} +2 -2
- package/dist/chunks/searchPoolWorker-D1xR43D2.js.map +1 -0
- package/dist/chunks/spriteMapper-CFjN0_TV.js.map +1 -1
- package/dist/chunks/tokens-B65Fzble.js.map +1 -1
- package/dist/chunks/ui-BEtq_JNg.js +3229 -0
- package/dist/chunks/ui-BEtq_JNg.js.map +1 -0
- package/dist/components/JamlAestheticSelector.d.ts +2 -3
- package/dist/components/JamlIdeToolbar.d.ts +3 -1
- package/dist/components/JamlSeedInput.d.ts +14 -4
- package/dist/components/JamlSeedSpinner.d.ts +7 -0
- package/dist/components/Jamlyzer.d.ts +5 -1
- package/dist/components/RunConfigModal.d.ts +1 -1
- package/dist/components/jamlSeedUtils.d.ts +1 -0
- package/dist/config.d.ts +5 -4
- package/dist/core.js.map +1 -1
- package/dist/decode/motelyItemFormats.d.ts +3124 -0
- package/dist/hooks/useIntersectionObserver.d.ts +1 -1
- package/dist/hooks/useMotelyRuntime.d.ts +1 -1
- package/dist/hooks/useSearch.d.ts +3 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3298 -3268
- package/dist/index.js.map +1 -1
- package/dist/lib/hooks/useDragScroll.d.ts +1 -1
- package/dist/motely.d.ts +2 -2
- package/dist/motely.js +23 -22
- package/dist/motely.js.map +1 -1
- package/dist/r3f.js.map +1 -1
- package/dist/ui/JimboPanelSpinner.d.ts +15 -0
- package/dist/ui/footer.d.ts +7 -1
- package/dist/ui/hooks.d.ts +2 -2
- package/dist/ui/jimbo.css +1 -1
- package/dist/ui/jimboText.d.ts +4 -6
- package/dist/ui/panel.d.ts +1 -2
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +3 -3
- package/jaml.schema.json +1219 -1165
- package/package.json +20 -22
- package/DESIGN.md +0 -236
- package/dist/assets/searchPoolWorker-CejAnH4a.js.map +0 -1
- package/dist/chunks/motelyItemDecoder-Bg12hhB2.js +0 -248
- package/dist/chunks/motelyItemDecoder-Bg12hhB2.js.map +0 -1
- package/dist/chunks/searchPoolWorker-CTtPOuxF.js.map +0 -1
- package/dist/chunks/ui-ByciFBYB.js +0 -1387
- package/dist/chunks/ui-ByciFBYB.js.map +0 -1
- package/dist/motelyBoot.d.ts +0 -15
- package/dist/ui/ide/DeckSprite.d.ts +0 -1
package/dist/motely.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { default as bootsharp, Motely } from 'motely-wasm';
|
|
2
2
|
export { decodeMotelyItem, decodeMotelyItemToJamlCard, motelyItemTypeName, motelyItemCategory, motelyItemDisplayName, motelyItemRenderCategory, motelyItemEditionName, motelyItemSealName, motelyItemEnhancementName, motelyStandardcardRankName, motelyStandardcardSuitName, decodeMotelyItemName, resolveMotelyItemType, warmMotelyItemCache, motelyItemCacheSize, type DecodedMotelyItem, type MotelyItemInput, type MotelyJamlCard, type MotelyRenderableCategory, type MotelyRuntimeItem, } from './decode/motelyItemDecoder.js';
|
|
3
3
|
export { motelyItemToSprite, getMotelySpriteByName, type MotelySpriteData, } from './decode/motelySprite.js';
|
|
4
4
|
export { motelyBossDisplayName, motelyBossDisplayNameFromKey, motelyBoosterPackDisplayName, motelyBoosterPackDisplayNameFromKey, motelyItemDisplayNameFromKey, motelyItemDisplayNameFromValue, motelyTagDisplayName, motelyTagDisplayNameFromKey, motelyVoucherDisplayName, motelyVoucherDisplayNameFromKey, } from './motelyDisplay.js';
|
|
5
5
|
export { useJamlLibrary, type JamlLibraryStatus, type UseJamlLibraryState, } from './hooks/useJamlLibrary.js';
|
|
6
|
-
export { useMotelyRuntime, useMotelyRuntimeOwner, type UseMotelyRuntimeState, } from './hooks/useMotelyRuntime.js';
|
|
6
|
+
export { useMotelyRuntime, useMotelyRuntimeOwner, type UseMotelyRuntimeState, type MotelyRuntimeStatus, } from './hooks/useMotelyRuntime.js';
|
package/dist/motely.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { n as e } from "./chunks/assets-RWUiFSTc.js";
|
|
3
3
|
import { d as t, h as n, r, t as i } from "./chunks/spriteMapper-CFjN0_TV.js";
|
|
4
|
-
import {
|
|
4
|
+
import { _ as a, a as o, c as s, d as c, f as l, g as u, h as d, i as f, l as p, m, n as h, o as g, p as _, r as v, s as y, t as b, u as x, v as S, y as C } from "./chunks/motelyItemDecoder-BvFrQpaI.js";
|
|
5
|
+
import w, { Motely as T, Motely as E } from "motely-wasm";
|
|
5
6
|
//#region src/decode/motelySprite.ts
|
|
6
|
-
function
|
|
7
|
-
let o =
|
|
7
|
+
function D(a) {
|
|
8
|
+
let o = b(a);
|
|
8
9
|
if (!o) return null;
|
|
9
10
|
if (o.category === "playing" && o.rank && o.suit) {
|
|
10
11
|
let r = t[o.rank], i = n[o.suit];
|
|
@@ -31,7 +32,7 @@ function A(a) {
|
|
|
31
32
|
category: o.category
|
|
32
33
|
} : null;
|
|
33
34
|
}
|
|
34
|
-
function
|
|
35
|
+
function O(a, o = "unknown") {
|
|
35
36
|
if (o === "playing") {
|
|
36
37
|
let r = /^(.*?)\s+of\s+(.*?)$/i.exec(a);
|
|
37
38
|
if (r) {
|
|
@@ -62,42 +63,42 @@ function j(a, o = "unknown") {
|
|
|
62
63
|
}
|
|
63
64
|
//#endregion
|
|
64
65
|
//#region src/motelyDisplay.ts
|
|
65
|
-
var
|
|
66
|
-
function
|
|
66
|
+
var k = E;
|
|
67
|
+
function A(e, t) {
|
|
67
68
|
let n = e[String(t)];
|
|
68
69
|
return typeof n == "string" && n.length > 0 ? n : null;
|
|
69
70
|
}
|
|
70
|
-
function
|
|
71
|
-
return
|
|
71
|
+
function j(e) {
|
|
72
|
+
return A(k.MotelyBossBlind, e & 255) ?? `boss#${e}`;
|
|
72
73
|
}
|
|
73
|
-
function
|
|
74
|
+
function M(e) {
|
|
74
75
|
return e;
|
|
75
76
|
}
|
|
76
|
-
function
|
|
77
|
-
return
|
|
77
|
+
function N(e) {
|
|
78
|
+
return A(k.MotelyVoucher, e) ?? `voucher#${e}`;
|
|
78
79
|
}
|
|
79
|
-
function
|
|
80
|
+
function P(e) {
|
|
80
81
|
return e;
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
return
|
|
83
|
+
function F(e) {
|
|
84
|
+
return A(k.MotelyTag, e) ?? `tag#${e}`;
|
|
84
85
|
}
|
|
85
|
-
function
|
|
86
|
+
function I(e) {
|
|
86
87
|
return e;
|
|
87
88
|
}
|
|
88
|
-
function
|
|
89
|
-
return
|
|
89
|
+
function L(e) {
|
|
90
|
+
return A(k.MotelyBoosterPack, e) ?? `pack#${e}`;
|
|
90
91
|
}
|
|
91
|
-
function
|
|
92
|
+
function R(e) {
|
|
92
93
|
return `${e} Pack`;
|
|
93
94
|
}
|
|
94
|
-
function
|
|
95
|
+
function z(e) {
|
|
95
96
|
return e;
|
|
96
97
|
}
|
|
97
|
-
function
|
|
98
|
-
return
|
|
98
|
+
function B(e) {
|
|
99
|
+
return u[e & 65535]?.displayName ?? `item#${e}`;
|
|
99
100
|
}
|
|
100
101
|
//#endregion
|
|
101
|
-
export {
|
|
102
|
+
export { T as Motely, w as bootsharp, b as decodeMotelyItem, h as decodeMotelyItemName, v as decodeMotelyItemToJamlCard, O as getMotelySpriteByName, L as motelyBoosterPackDisplayName, R as motelyBoosterPackDisplayNameFromKey, j as motelyBossDisplayName, M as motelyBossDisplayNameFromKey, f as motelyItemCacheSize, o as motelyItemCategory, g as motelyItemDisplayName, z as motelyItemDisplayNameFromKey, B as motelyItemDisplayNameFromValue, y as motelyItemEditionName, s as motelyItemEnhancementName, p as motelyItemRenderCategory, x as motelyItemSealName, D as motelyItemToSprite, c as motelyItemTypeName, l as motelyStandardcardRankName, _ as motelyStandardcardSuitName, F as motelyTagDisplayName, I as motelyTagDisplayNameFromKey, N as motelyVoucherDisplayName, P as motelyVoucherDisplayNameFromKey, m as resolveMotelyItemType, C as useJamlLibrary, a as useMotelyRuntime, S as useMotelyRuntimeOwner, d as warmMotelyItemCache };
|
|
102
103
|
|
|
103
104
|
//# sourceMappingURL=motely.js.map
|
package/dist/motely.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motely.js","names":[],"sources":["../src/decode/motelySprite.ts","../src/motelyDisplay.ts"],"sourcesContent":["import { decodeMotelyItem, type MotelyRenderableCategory } from \"./motelyItemDecoder.js\";\r\nimport { getSpriteData, SHEET_META } from \"../sprites/spriteMapper.js\";\r\nimport { RANK_MAP, SUIT_MAP } from \"../sprites/spriteData.js\";\r\nimport { resolveJamlAssetUrl } from \"../assets.js\";\r\n\r\nexport interface MotelySpriteData {\r\n atlasPath: string;\r\n gridCol: number;\r\n gridRow: number;\r\n gridCols: number;\r\n gridRows: number;\r\n displayName: string;\r\n category: MotelyRenderableCategory;\r\n}\r\n\r\n/**\r\n * Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),\r\n * resolves it to a sprite atlas path and grid coordinates for rendering.\r\n */\r\nexport function motelyItemToSprite(rawValue: number): MotelySpriteData | null {\r\n const decoded = decodeMotelyItem(rawValue);\r\n if (!decoded) return null;\r\n\r\n if (decoded.category === \"playing\" && decoded.rank && decoded.suit) {\r\n const col = RANK_MAP[decoded.rank];\r\n const row = SUIT_MAP[decoded.suit];\r\n if (col !== undefined && row !== undefined) {\r\n return {\r\n atlasPath: resolveJamlAssetUrl('deck'),\r\n gridCol: col,\r\n gridRow: row,\r\n gridCols: 13,\r\n gridRows: 4,\r\n displayName: decoded.displayName,\r\n category: \"playing\"\r\n };\r\n }\r\n }\r\n\r\n const sprite = getSpriteData(decoded.displayName);\r\n if (!sprite) return null;\r\n\r\n const meta = SHEET_META[sprite.type];\r\n if (!meta) return null;\r\n\r\n return {\r\n atlasPath: resolveJamlAssetUrl(meta.assetKey),\r\n gridCol: sprite.pos.x,\r\n gridRow: sprite.pos.y,\r\n gridCols: meta.cols,\r\n gridRows: meta.rows,\r\n displayName: decoded.displayName,\r\n category: decoded.category\r\n };\r\n}\r\n\r\n/**\r\n * Resolves a sprite by name and category without needing a Motely integer.\r\n */\r\nexport function getMotelySpriteByName(name: string, category: MotelyRenderableCategory = \"unknown\"): MotelySpriteData | null {\r\n if (category === \"playing\") {\r\n // Attempt to parse \"Rank of Suit\"\r\n const match = /^(.*?)\\s+of\\s+(.*?)$/i.exec(name);\r\n if (match) {\r\n const rank = match[1];\r\n const suit = match[2];\r\n const col = RANK_MAP[rank];\r\n const row = SUIT_MAP[suit];\r\n if (col !== undefined && row !== undefined) {\r\n return {\r\n atlasPath: resolveJamlAssetUrl('deck'),\r\n gridCol: col,\r\n gridRow: row,\r\n gridCols: 13,\r\n gridRows: 4,\r\n displayName: name,\r\n category: \"playing\"\r\n };\r\n }\r\n }\r\n }\r\n\r\n const sprite = getSpriteData(name);\r\n if (!sprite) return null;\r\n\r\n const meta = SHEET_META[sprite.type];\r\n if (!meta) return null;\r\n\r\n return {\r\n atlasPath: resolveJamlAssetUrl(meta.assetKey),\r\n gridCol: sprite.pos.x,\r\n gridRow: sprite.pos.y,\r\n gridCols: meta.cols,\r\n gridRows: meta.rows,\r\n displayName: name,\r\n category\r\n };\r\n}\r\n","import { Motely } from \"./
|
|
1
|
+
{"version":3,"file":"motely.js","names":[],"sources":["../src/decode/motelySprite.ts","../src/motelyDisplay.ts"],"sourcesContent":["import { decodeMotelyItem, type MotelyRenderableCategory } from \"./motelyItemDecoder.js\";\r\nimport { getSpriteData, SHEET_META } from \"../sprites/spriteMapper.js\";\r\nimport { RANK_MAP, SUIT_MAP } from \"../sprites/spriteData.js\";\r\nimport { resolveJamlAssetUrl } from \"../assets.js\";\r\n\r\nexport interface MotelySpriteData {\r\n atlasPath: string;\r\n gridCol: number;\r\n gridRow: number;\r\n gridCols: number;\r\n gridRows: number;\r\n displayName: string;\r\n category: MotelyRenderableCategory;\r\n}\r\n\r\n/**\r\n * Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),\r\n * resolves it to a sprite atlas path and grid coordinates for rendering.\r\n */\r\nexport function motelyItemToSprite(rawValue: number): MotelySpriteData | null {\r\n const decoded = decodeMotelyItem(rawValue);\r\n if (!decoded) return null;\r\n\r\n if (decoded.category === \"playing\" && decoded.rank && decoded.suit) {\r\n const col = RANK_MAP[decoded.rank];\r\n const row = SUIT_MAP[decoded.suit];\r\n if (col !== undefined && row !== undefined) {\r\n return {\r\n atlasPath: resolveJamlAssetUrl('deck'),\r\n gridCol: col,\r\n gridRow: row,\r\n gridCols: 13,\r\n gridRows: 4,\r\n displayName: decoded.displayName,\r\n category: \"playing\"\r\n };\r\n }\r\n }\r\n\r\n const sprite = getSpriteData(decoded.displayName);\r\n if (!sprite) return null;\r\n\r\n const meta = SHEET_META[sprite.type];\r\n if (!meta) return null;\r\n\r\n return {\r\n atlasPath: resolveJamlAssetUrl(meta.assetKey),\r\n gridCol: sprite.pos.x,\r\n gridRow: sprite.pos.y,\r\n gridCols: meta.cols,\r\n gridRows: meta.rows,\r\n displayName: decoded.displayName,\r\n category: decoded.category\r\n };\r\n}\r\n\r\n/**\r\n * Resolves a sprite by name and category without needing a Motely integer.\r\n */\r\nexport function getMotelySpriteByName(name: string, category: MotelyRenderableCategory = \"unknown\"): MotelySpriteData | null {\r\n if (category === \"playing\") {\r\n // Attempt to parse \"Rank of Suit\"\r\n const match = /^(.*?)\\s+of\\s+(.*?)$/i.exec(name);\r\n if (match) {\r\n const rank = match[1];\r\n const suit = match[2];\r\n const col = RANK_MAP[rank];\r\n const row = SUIT_MAP[suit];\r\n if (col !== undefined && row !== undefined) {\r\n return {\r\n atlasPath: resolveJamlAssetUrl('deck'),\r\n gridCol: col,\r\n gridRow: row,\r\n gridCols: 13,\r\n gridRows: 4,\r\n displayName: name,\r\n category: \"playing\"\r\n };\r\n }\r\n }\r\n }\r\n\r\n const sprite = getSpriteData(name);\r\n if (!sprite) return null;\r\n\r\n const meta = SHEET_META[sprite.type];\r\n if (!meta) return null;\r\n\r\n return {\r\n atlasPath: resolveJamlAssetUrl(meta.assetKey),\r\n gridCol: sprite.pos.x,\r\n gridRow: sprite.pos.y,\r\n gridCols: meta.cols,\r\n gridRows: meta.rows,\r\n displayName: name,\r\n category\r\n };\r\n}\r\n","import { Motely } from \"motely-wasm\";\nimport { MOTELY_ITEM_FORMATS_BY_VALUE } from \"./decode/motelyItemFormats.js\";\n\ntype RuntimeEnum = Record<string, string | number>;\ntype MotelyRuntimeEnums = typeof Motely & Record<string, RuntimeEnum>;\n\nconst MotelyEnums = Motely as MotelyRuntimeEnums;\n\nfunction runtimeEnumKey(\n enumObject: Record<string, unknown>,\n value: number,\n): string | null {\n const key = enumObject[String(value)];\n return typeof key === \"string\" && key.length > 0 ? key : null;\n}\n\nexport function motelyBossDisplayName(value: number): string {\n const key = runtimeEnumKey(MotelyEnums.MotelyBossBlind, value & 0xff);\n return key ?? `boss#${value}`;\n}\n\nexport function motelyBossDisplayNameFromKey(key: string): string {\n return key;\n}\n\nexport function motelyVoucherDisplayName(value: number): string {\n const key = runtimeEnumKey(MotelyEnums.MotelyVoucher, value);\n return key ?? `voucher#${value}`;\n}\n\nexport function motelyVoucherDisplayNameFromKey(key: string): string {\n return key;\n}\n\nexport function motelyTagDisplayName(value: number): string {\n const key = runtimeEnumKey(MotelyEnums.MotelyTag, value);\n return key ?? `tag#${value}`;\n}\n\nexport function motelyTagDisplayNameFromKey(key: string): string {\n return key;\n}\n\nexport function motelyBoosterPackDisplayName(value: number): string {\n const key = runtimeEnumKey(MotelyEnums.MotelyBoosterPack, value);\n return key ?? `pack#${value}`;\n}\n\nexport function motelyBoosterPackDisplayNameFromKey(key: string): string {\n return `${key} Pack`;\n}\n\nexport function motelyItemDisplayNameFromKey(key: string): string {\n return key;\n}\n\nexport function motelyItemDisplayNameFromValue(value: number): string {\n const itemType = value & 0xffff;\n return MOTELY_ITEM_FORMATS_BY_VALUE[itemType as keyof typeof MOTELY_ITEM_FORMATS_BY_VALUE]?.displayName ?? `item#${value}`;\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAmB,GAA2C;CAC5E,IAAM,IAAU,EAAiB,CAAQ;CACzC,IAAI,CAAC,GAAS,OAAO;CAErB,IAAI,EAAQ,aAAa,aAAa,EAAQ,QAAQ,EAAQ,MAAM;EAClE,IAAM,IAAM,EAAS,EAAQ,OACvB,IAAM,EAAS,EAAQ;EAC7B,IAAI,MAAQ,KAAA,KAAa,MAAQ,KAAA,GAC/B,OAAO;GACL,WAAW,EAAoB,MAAM;GACrC,SAAS;GACT,SAAS;GACT,UAAU;GACV,UAAU;GACV,aAAa,EAAQ;GACrB,UAAU;EACZ;CAEJ;CAEA,IAAM,IAAS,EAAc,EAAQ,WAAW;CAChD,IAAI,CAAC,GAAQ,OAAO;CAEpB,IAAM,IAAO,EAAW,EAAO;CAG/B,OAFK,IAEE;EACL,WAAW,EAAoB,EAAK,QAAQ;EAC5C,SAAS,EAAO,IAAI;EACpB,SAAS,EAAO,IAAI;EACpB,UAAU,EAAK;EACf,UAAU,EAAK;EACf,aAAa,EAAQ;EACrB,UAAU,EAAQ;CACpB,IAVkB;AAWpB;AAKA,SAAgB,EAAsB,GAAc,IAAqC,WAAoC;CAC3H,IAAI,MAAa,WAAW;EAE1B,IAAM,IAAQ,wBAAwB,KAAK,CAAI;EAC/C,IAAI,GAAO;GACP,IAAM,IAAO,EAAM,IACb,IAAO,EAAM,IACb,IAAM,EAAS,IACf,IAAM,EAAS;GACrB,IAAI,MAAQ,KAAA,KAAa,MAAQ,KAAA,GAC/B,OAAO;IACL,WAAW,EAAoB,MAAM;IACrC,SAAS;IACT,SAAS;IACT,UAAU;IACV,UAAU;IACV,aAAa;IACb,UAAU;GACZ;EAEN;CACF;CAEA,IAAM,IAAS,EAAc,CAAI;CACjC,IAAI,CAAC,GAAQ,OAAO;CAEpB,IAAM,IAAO,EAAW,EAAO;CAG/B,OAFK,IAEE;EACL,WAAW,EAAoB,EAAK,QAAQ;EAC5C,SAAS,EAAO,IAAI;EACpB,SAAS,EAAO,IAAI;EACpB,UAAU,EAAK;EACf,UAAU,EAAK;EACf,aAAa;EACb;CACF,IAVkB;AAWpB;;;AC3FA,IAAM,IAAc;AAEpB,SAAS,EACP,GACA,GACe;CACf,IAAM,IAAM,EAAW,OAAO,CAAK;CACnC,OAAO,OAAO,KAAQ,YAAY,EAAI,SAAS,IAAI,IAAM;AAC3D;AAEA,SAAgB,EAAsB,GAAuB;CAE3D,OADY,EAAe,EAAY,iBAAiB,IAAQ,GACzD,KAAO,QAAQ;AACxB;AAEA,SAAgB,EAA6B,GAAqB;CAChE,OAAO;AACT;AAEA,SAAgB,EAAyB,GAAuB;CAE9D,OADY,EAAe,EAAY,eAAe,CAC/C,KAAO,WAAW;AAC3B;AAEA,SAAgB,EAAgC,GAAqB;CACnE,OAAO;AACT;AAEA,SAAgB,EAAqB,GAAuB;CAE1D,OADY,EAAe,EAAY,WAAW,CAC3C,KAAO,OAAO;AACvB;AAEA,SAAgB,EAA4B,GAAqB;CAC/D,OAAO;AACT;AAEA,SAAgB,EAA6B,GAAuB;CAElE,OADY,EAAe,EAAY,mBAAmB,CACnD,KAAO,QAAQ;AACxB;AAEA,SAAgB,EAAoC,GAAqB;CACvE,OAAO,GAAG,EAAI;AAChB;AAEA,SAAgB,EAA6B,GAAqB;CAChE,OAAO;AACT;AAEA,SAAgB,EAA+B,GAAuB;CAEpE,OAAO,EADU,IAAQ,QACmE,eAAe,QAAQ;AACrH"}
|
package/dist/r3f.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r3f.js","names":[],"sources":["../src/r3f/Card3D.tsx","../src/r3f/JimboBillboard.tsx","../src/r3f/JimboText3D.tsx"],"sourcesContent":["'use client'\r\n\r\nimport { useRef, useMemo, useState, memo } from 'react'\r\nimport { useFrame, useLoader, type ThreeEvent } from '@react-three/fiber'\r\nimport { useSpring, animated } from '@react-spring/three'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_DIMENSIONS = { WIDTH: 0.7, HEIGHT: 0.95, DEPTH: 0.02 } as const\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_MAGNET = {\r\n MAX_TILT_X: 0.36,\r\n MAX_TILT_Y: 0.42,\r\n MAX_SHIFT: 0.038,\r\n TWIST_Z: 0.11,\r\n LERP_IN: 18,\r\n LERP_OUT: 10,\r\n} as const\r\n\r\nfunction useSpriteTexture(sprite: MotelySpriteData) {\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n \r\n return useMemo(() => {\r\n const t = texture.clone();\r\n t.colorSpace = THREE.SRGBColorSpace;\r\n t.magFilter = THREE.NearestFilter;\r\n t.minFilter = THREE.NearestFilter;\r\n t.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n t.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n t.needsUpdate = true;\r\n return t;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n}\r\n\r\nexport interface Card3DProps {\r\n sprite: MotelySpriteData\r\n position?: [number, number, number]\r\n rotation?: [number, number, number]\r\n selected?: boolean\r\n highlighted?: boolean\r\n onClick?: () => void\r\n onPointerEnter?: () => void\r\n onPointerLeave?: () => void\r\n}\r\n\r\nexport const Card3D = memo(function Card3D({\r\n sprite,\r\n position = [0, 0, 0],\r\n rotation = [0, 0, 0],\r\n selected = false,\r\n highlighted = false,\r\n onClick,\r\n onPointerEnter,\r\n onPointerLeave,\r\n}: Card3DProps) {\r\n const tiltRef = useRef<THREE.Group>(null)\r\n const target = useRef({ rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 })\r\n const [hovered, setHovered] = useState(false)\r\n\r\n const texture = useSpriteTexture(sprite)\r\n\r\n const { posY, scale } = useSpring({\r\n posY: selected ? 0.3 : hovered ? 0.15 : 0,\r\n scale: hovered ? 1.08 : selected ? 1.05 : 1,\r\n config: { tension: 300, friction: 20 },\r\n })\r\n\r\n useFrame((_state, dt) => {\r\n const g = tiltRef.current\r\n if (!g) return\r\n const t = target.current\r\n const rate = hovered ? CARD_MAGNET.LERP_IN : CARD_MAGNET.LERP_OUT\r\n const a = 1 - Math.exp(-rate * dt)\r\n g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, t.rx, a)\r\n g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, t.ry, a)\r\n g.rotation.z = THREE.MathUtils.lerp(g.rotation.z, t.rz, a)\r\n g.position.x = THREE.MathUtils.lerp(g.position.x, t.ox, a)\r\n g.position.y = THREE.MathUtils.lerp(g.position.y, t.oy, a)\r\n })\r\n\r\n const glowColor = useMemo(() => highlighted ? '#e4b643' : '#ffffff', [highlighted])\r\n\r\n const onMove = (e: ThreeEvent<PointerEvent>) => {\r\n e.stopPropagation()\r\n const uv = e.uv\r\n if (!uv) return\r\n const nx = THREE.MathUtils.clamp((uv.x - 0.5) * 2, -1, 1)\r\n const ny = THREE.MathUtils.clamp((uv.y - 0.5) * 2, -1, 1)\r\n target.current.ry = -nx * CARD_MAGNET.MAX_TILT_Y\r\n target.current.rx = ny * CARD_MAGNET.MAX_TILT_X\r\n target.current.rz = -nx * ny * CARD_MAGNET.TWIST_Z\r\n target.current.ox = nx * CARD_MAGNET.MAX_SHIFT\r\n target.current.oy = -ny * CARD_MAGNET.MAX_SHIFT * 0.65\r\n }\r\n\r\n const reset = () => { target.current = { rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 } }\r\n\r\n if (!texture) return null\r\n\r\n return (\r\n <animated.group\r\n position-x={position[0]}\r\n position-y={posY.to((y) => position[1] + y)}\r\n position-z={position[2]}\r\n rotation-x={rotation[0]}\r\n rotation-y={rotation[1]}\r\n rotation-z={rotation[2]}\r\n scale={scale}\r\n >\r\n {/* Invisible hit mesh that does not shift or twist, preventing boop-cancel loops */}\r\n <mesh\r\n visible={false}\r\n onClick={(e) => { e.stopPropagation(); onClick?.() }}\r\n onPointerMove={onMove}\r\n onPointerEnter={(e) => { e.stopPropagation(); setHovered(true); onPointerEnter?.(); document.body.style.cursor = 'pointer' }}\r\n onPointerLeave={(e) => { e.stopPropagation(); setHovered(false); reset(); onPointerLeave?.(); document.body.style.cursor = 'auto' }}\r\n >\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH * 2]} />\r\n <meshBasicMaterial />\r\n </mesh>\r\n\r\n <group ref={tiltRef}>\r\n {highlighted && (\r\n <pointLight color={glowColor} intensity={1.5} distance={1} position={[0, 0, 0.1]} />\r\n )}\r\n <mesh castShadow receiveShadow>\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH]} />\r\n <meshBasicMaterial attach=\"material-4\" map={texture} toneMapped={false} />\r\n <meshStandardMaterial attach=\"material-5\" color=\"#1a1a2e\" metalness={0.2} roughness={0.8} />\r\n <meshStandardMaterial attach=\"material-0\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-1\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-2\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-3\" color=\"#f5f5dc\" />\r\n </mesh>\r\n {selected && (\r\n <mesh position={[0, 0, -CARD_DIMENSIONS.DEPTH]}>\r\n <ringGeometry args={[0.45, 0.5, 32]} />\r\n <meshBasicMaterial color=\"#e4b643\" transparent opacity={0.8} />\r\n </mesh>\r\n )}\r\n </group>\r\n </animated.group>\r\n )\r\n})\r\n","import React, { useMemo } from 'react'\r\nimport { Billboard } from '@react-three/drei'\r\nimport { useLoader } from '@react-three/fiber'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\nexport interface JimboBillboardProps {\r\n sprite: MotelySpriteData | null\r\n label?: string\r\n width?: number\r\n height?: number\r\n yLockOnly?: boolean\r\n position?: [number, number, number]\r\n}\r\n\r\nexport function JimboBillboard(props: JimboBillboardProps) {\r\n if (!props.sprite) return null;\r\n return <JimboBillboardInner {...props} sprite={props.sprite} />;\r\n}\r\n\r\nfunction JimboBillboardInner({\r\n sprite,\r\n width = 3.4,\r\n height = 4.5,\r\n yLockOnly = false,\r\n position = [0, 0, 0]\r\n}: Omit<JimboBillboardProps, 'sprite'> & { sprite: MotelySpriteData }) {\r\n // Memoize texture to avoid per-render allocation\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n\r\n const clonedTexture = useMemo(() => {\r\n const tex = texture.clone();\r\n tex.magFilter = THREE.NearestFilter;\r\n tex.minFilter = THREE.NearestFilter;\r\n // Set up sprite cropping\r\n tex.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n tex.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n tex.needsUpdate = true;\r\n return tex;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n\r\n const material = useMemo(() => {\r\n return new THREE.MeshBasicMaterial({\r\n map: clonedTexture,\r\n transparent: true,\r\n alphaTest: 0.5\r\n });\r\n }, [clonedTexture]);\r\n\r\n return (\r\n <Billboard\r\n lockY={yLockOnly}\r\n lockX={false}\r\n lockZ={false}\r\n position={position}\r\n >\r\n <mesh material={material}>\r\n <planeGeometry args={[width, height]} />\r\n </mesh>\r\n </Billboard>\r\n )\r\n}\r\n","import React from 'react'\r\nimport { Text } from '@react-three/drei'\r\nimport { resolveJamlAssetUrl } from '../assets.js'\r\nimport { JimboColorOption } from '../ui/tokens.js'\r\n\r\nexport interface JimboText3DProps {\r\n children: string\r\n color?: string\r\n outlineColor?: string\r\n outlineWidth?: number\r\n position?: [number, number, number]\r\n fontSize?: number\r\n}\r\n\r\nexport function JimboText3D({\r\n children,\r\n color = JimboColorOption.WHITE,\r\n outlineColor = JimboColorOption.BLACK,\r\n outlineWidth = 0.05,\r\n position = [0, 0, 0],\r\n fontSize = 1\r\n}: JimboText3DProps) {\r\n // We use the m6x11plus font from assets if possible, or fallback\r\n return (\r\n <Text\r\n position={position}\r\n fontSize={fontSize}\r\n color={color}\r\n outlineColor={outlineColor}\r\n outlineWidth={outlineWidth}\r\n font={resolveJamlAssetUrl('font')}\r\n anchorX=\"center\"\r\n anchorY=\"middle\"\r\n >\r\n {children}\r\n </Text>\r\n )\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"r3f.js","names":[],"sources":["../src/r3f/Card3D.tsx","../src/r3f/JimboBillboard.tsx","../src/r3f/JimboText3D.tsx"],"sourcesContent":["'use client'\r\n\r\nimport { useRef, useMemo, useState, memo } from 'react'\r\nimport { useFrame, useLoader, type ThreeEvent } from '@react-three/fiber'\r\nimport { useSpring, animated } from '@react-spring/three'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_DIMENSIONS = { WIDTH: 0.7, HEIGHT: 0.95, DEPTH: 0.02 } as const\r\n// eslint-disable-next-line react-refresh/only-export-components\r\nexport const CARD_MAGNET = {\r\n MAX_TILT_X: 0.36,\r\n MAX_TILT_Y: 0.42,\r\n MAX_SHIFT: 0.038,\r\n TWIST_Z: 0.11,\r\n LERP_IN: 18,\r\n LERP_OUT: 10,\r\n} as const\r\n\r\nfunction useSpriteTexture(sprite: MotelySpriteData) {\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n \r\n return useMemo(() => {\r\n const t = texture.clone();\r\n t.colorSpace = THREE.SRGBColorSpace;\r\n t.magFilter = THREE.NearestFilter;\r\n t.minFilter = THREE.NearestFilter;\r\n t.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n t.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n t.needsUpdate = true;\r\n return t;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n}\r\n\r\nexport interface Card3DProps {\r\n sprite: MotelySpriteData\r\n position?: [number, number, number]\r\n rotation?: [number, number, number]\r\n selected?: boolean\r\n highlighted?: boolean\r\n onClick?: () => void\r\n onPointerEnter?: () => void\r\n onPointerLeave?: () => void\r\n}\r\n\r\nexport const Card3D = memo(function Card3D({\r\n sprite,\r\n position = [0, 0, 0],\r\n rotation = [0, 0, 0],\r\n selected = false,\r\n highlighted = false,\r\n onClick,\r\n onPointerEnter,\r\n onPointerLeave,\r\n}: Card3DProps) {\r\n const tiltRef = useRef<THREE.Group>(null)\r\n const target = useRef({ rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 })\r\n const [hovered, setHovered] = useState(false)\r\n\r\n const texture = useSpriteTexture(sprite)\r\n\r\n const { posY, scale } = useSpring({\r\n posY: selected ? 0.3 : hovered ? 0.15 : 0,\r\n scale: hovered ? 1.08 : selected ? 1.05 : 1,\r\n config: { tension: 300, friction: 20 },\r\n })\r\n\r\n useFrame((_state, dt) => {\r\n const g = tiltRef.current\r\n if (!g) return\r\n const t = target.current\r\n const rate = hovered ? CARD_MAGNET.LERP_IN : CARD_MAGNET.LERP_OUT\r\n const a = 1 - Math.exp(-rate * dt)\r\n g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, t.rx, a)\r\n g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, t.ry, a)\r\n g.rotation.z = THREE.MathUtils.lerp(g.rotation.z, t.rz, a)\r\n g.position.x = THREE.MathUtils.lerp(g.position.x, t.ox, a)\r\n g.position.y = THREE.MathUtils.lerp(g.position.y, t.oy, a)\r\n })\r\n\r\n const glowColor = useMemo(() => highlighted ? '#e4b643' : '#ffffff', [highlighted])\r\n\r\n const onMove = (e: ThreeEvent<PointerEvent>) => {\r\n e.stopPropagation()\r\n const uv = e.uv\r\n if (!uv) return\r\n const nx = THREE.MathUtils.clamp((uv.x - 0.5) * 2, -1, 1)\r\n const ny = THREE.MathUtils.clamp((uv.y - 0.5) * 2, -1, 1)\r\n target.current.ry = -nx * CARD_MAGNET.MAX_TILT_Y\r\n target.current.rx = ny * CARD_MAGNET.MAX_TILT_X\r\n target.current.rz = -nx * ny * CARD_MAGNET.TWIST_Z\r\n target.current.ox = nx * CARD_MAGNET.MAX_SHIFT\r\n target.current.oy = -ny * CARD_MAGNET.MAX_SHIFT * 0.65\r\n }\r\n\r\n const reset = () => { target.current = { rx: 0, ry: 0, rz: 0, ox: 0, oy: 0 } }\r\n\r\n if (!texture) return null\r\n\r\n return (\r\n <animated.group\r\n position-x={position[0]}\r\n position-y={posY.to((y) => position[1] + y)}\r\n position-z={position[2]}\r\n rotation-x={rotation[0]}\r\n rotation-y={rotation[1]}\r\n rotation-z={rotation[2]}\r\n scale={scale}\r\n >\r\n {/* Invisible hit mesh that does not shift or twist, preventing boop-cancel loops */}\r\n <mesh\r\n visible={false}\r\n onClick={(e) => { e.stopPropagation(); onClick?.() }}\r\n onPointerMove={onMove}\r\n onPointerEnter={(e) => { e.stopPropagation(); setHovered(true); onPointerEnter?.(); document.body.style.cursor = 'pointer' }}\r\n onPointerLeave={(e) => { e.stopPropagation(); setHovered(false); reset(); onPointerLeave?.(); document.body.style.cursor = 'auto' }}\r\n >\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH * 2]} />\r\n <meshBasicMaterial />\r\n </mesh>\r\n\r\n <group ref={tiltRef}>\r\n {highlighted && (\r\n <pointLight color={glowColor} intensity={1.5} distance={1} position={[0, 0, 0.1]} />\r\n )}\r\n <mesh castShadow receiveShadow>\r\n <boxGeometry args={[CARD_DIMENSIONS.WIDTH, CARD_DIMENSIONS.HEIGHT, CARD_DIMENSIONS.DEPTH]} />\r\n <meshBasicMaterial attach=\"material-4\" map={texture} toneMapped={false} />\r\n <meshStandardMaterial attach=\"material-5\" color=\"#1a1a2e\" metalness={0.2} roughness={0.8} />\r\n <meshStandardMaterial attach=\"material-0\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-1\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-2\" color=\"#f5f5dc\" />\r\n <meshStandardMaterial attach=\"material-3\" color=\"#f5f5dc\" />\r\n </mesh>\r\n {selected && (\r\n <mesh position={[0, 0, -CARD_DIMENSIONS.DEPTH]}>\r\n <ringGeometry args={[0.45, 0.5, 32]} />\r\n <meshBasicMaterial color=\"#e4b643\" transparent opacity={0.8} />\r\n </mesh>\r\n )}\r\n </group>\r\n </animated.group>\r\n )\r\n})\r\n","import React, { useMemo } from 'react'\r\nimport { Billboard } from '@react-three/drei'\r\nimport { useLoader } from '@react-three/fiber'\r\nimport * as THREE from 'three'\r\nimport type { MotelySpriteData } from '../decode/motelySprite.js'\r\n\r\nexport interface JimboBillboardProps {\r\n sprite: MotelySpriteData | null\r\n label?: string\r\n width?: number\r\n height?: number\r\n yLockOnly?: boolean\r\n position?: [number, number, number]\r\n}\r\n\r\nexport function JimboBillboard(props: JimboBillboardProps) {\r\n if (!props.sprite) return null;\r\n return <JimboBillboardInner {...props} sprite={props.sprite} />;\r\n}\r\n\r\nfunction JimboBillboardInner({\r\n sprite,\r\n width = 3.4,\r\n height = 4.5,\r\n yLockOnly = false,\r\n position = [0, 0, 0]\r\n}: Omit<JimboBillboardProps, 'sprite'> & { sprite: MotelySpriteData }) {\r\n // Memoize texture to avoid per-render allocation\r\n const texture = useLoader(THREE.TextureLoader, sprite.atlasPath);\r\n\r\n const clonedTexture = useMemo(() => {\r\n const tex = texture.clone();\r\n tex.magFilter = THREE.NearestFilter;\r\n tex.minFilter = THREE.NearestFilter;\r\n // Set up sprite cropping\r\n tex.repeat.set(1 / sprite.gridCols, 1 / sprite.gridRows);\r\n tex.offset.set(\r\n sprite.gridCol / sprite.gridCols,\r\n 1 - ((sprite.gridRow + 1) / sprite.gridRows)\r\n );\r\n tex.needsUpdate = true;\r\n return tex;\r\n }, [texture, sprite.gridCol, sprite.gridRow, sprite.gridCols, sprite.gridRows]);\r\n\r\n const material = useMemo(() => {\r\n return new THREE.MeshBasicMaterial({\r\n map: clonedTexture,\r\n transparent: true,\r\n alphaTest: 0.5\r\n });\r\n }, [clonedTexture]);\r\n\r\n return (\r\n <Billboard\r\n lockY={yLockOnly}\r\n lockX={false}\r\n lockZ={false}\r\n position={position}\r\n >\r\n <mesh material={material}>\r\n <planeGeometry args={[width, height]} />\r\n </mesh>\r\n </Billboard>\r\n )\r\n}\r\n","import React from 'react'\r\nimport { Text } from '@react-three/drei'\r\nimport { resolveJamlAssetUrl } from '../assets.js'\r\nimport { JimboColorOption } from '../ui/tokens.js'\r\n\r\nexport interface JimboText3DProps {\r\n children: string\r\n color?: string\r\n outlineColor?: string\r\n outlineWidth?: number\r\n position?: [number, number, number]\r\n fontSize?: number\r\n}\r\n\r\nexport function JimboText3D({\r\n children,\r\n color = JimboColorOption.WHITE,\r\n outlineColor = JimboColorOption.BLACK,\r\n outlineWidth = 0.05,\r\n position = [0, 0, 0],\r\n fontSize = 1\r\n}: JimboText3DProps) {\r\n // We use the m6x11plus font from assets if possible, or fallback\r\n return (\r\n <Text\r\n position={position}\r\n fontSize={fontSize}\r\n color={color}\r\n outlineColor={outlineColor}\r\n outlineWidth={outlineWidth}\r\n font={resolveJamlAssetUrl('font')}\r\n anchorX=\"center\"\r\n anchorY=\"middle\"\r\n >\r\n {children}\r\n </Text>\r\n )\r\n}\r\n"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface JimboPanelSpinnerProps {
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
title: React.ReactNode;
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
media?: React.ReactNode;
|
|
7
|
+
meta?: React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
onPrev?: () => void;
|
|
11
|
+
onNext?: () => void;
|
|
12
|
+
prevDisabled?: boolean;
|
|
13
|
+
nextDisabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function JimboPanelSpinner({ label, title, description, media, meta, className, style, onPrev, onNext, prevDisabled, nextDisabled, }: JimboPanelSpinnerProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/ui/footer.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
1
2
|
export interface JimboBalatroFooterProps {
|
|
3
|
+
/** Fade the footer out */
|
|
4
|
+
hidden?: boolean;
|
|
5
|
+
/** Extra className */
|
|
2
6
|
className?: string;
|
|
7
|
+
/** Optional inline children */
|
|
8
|
+
children?: React.ReactNode;
|
|
3
9
|
}
|
|
4
10
|
/**
|
|
5
11
|
* Attribution footer with animated suit cycle.
|
|
6
12
|
* Always rendered — required attribution for using Balatro art.
|
|
7
13
|
*/
|
|
8
|
-
export declare function JimboBalatroFooter({ className }: JimboBalatroFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function JimboBalatroFooter({ hidden, className, children }: JimboBalatroFooterProps): import("react/jsx-runtime").JSX.Element | null;
|
package/dist/ui/hooks.d.ts
CHANGED
|
@@ -49,8 +49,8 @@ export declare function useJamlCardRenderer({ layers, invert, hoverTilt, }: {
|
|
|
49
49
|
hoverTilt?: boolean;
|
|
50
50
|
}): {
|
|
51
51
|
canvasRef: React.RefObject<HTMLCanvasElement | null>;
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
53
|
+
ratio: number;
|
|
54
54
|
handlers: {
|
|
55
55
|
onPointerEnter: ((event: React.PointerEvent) => void) | undefined;
|
|
56
56
|
onPointerLeave: (() => void) | undefined;
|
package/dist/ui/jimbo.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@font-face{font-family:m6x11plus;src:url(/fonts/m6x11plus.otf)format("opentype");font-weight:400;font-style:normal;font-display:swap}*{scrollbar-width:none!important;-ms-overflow-style:none!important}::-webkit-scrollbar{display:none!important}:root{--j-red:#fe5148;--j-blue:#0093ff;--j-green:#429f79;--j-orange:#ff9800;--j-gold:#e4b643;--j-purple:#9e74ce;--j-dark-red:#a02721;--j-dark-blue:#0057a1;--j-dark-orange:#a05b00;--j-dark-green:#215f46;--j-dark-purple:#5e437e;--j-dark-grey:#3a5055;--j-darkest:#1e2b2d;--j-grey:#708386;--j-teal-grey:#404c4e;--j-panel-edge:#1e2e32;--j-inner-border:#334461;--j-border-silver:#b9c2d2;--j-border-south:#777e89;--j-gold-text:#e4b643;--j-green-text:#35bd86;--j-orange-text:#ff8f00;--j-white:#fff;--j-black:#000;--j-tarot-btn:#9e74ce;--j-planet-btn:#00a7ca;--j-spectral-btn:#2e76fd;--j-tarot-btn-dark:#5e437e;--j-planet-btn-dark:#00657c;--j-spectral-btn-dark:#14449e;--j-font:"m6x11plus", "Courier New", monospace;--j-font-code:"JetBrains Mono", "Cascadia Code", "Fira Code", "SF Mono", SFMono-Regular, Menlo, Consolas, ui-monospace, "Courier New", monospace;--j-text-shadow:1px 1px 0 #000c;--j-space-xs:2px;--j-space-sm:4px;--j-space-md:8px;--j-space-lg:12px;--j-space-xl:16px;--j-radius-sm:4px;--j-radius-md:6px;--j-radius-lg:8px;--j-radius-pill:10px;--j-ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--j-press-y:3px;--j-press-speed:55ms}.j-text{font-family:var(--j-font);text-shadow:var(--j-text-shadow);color:var(--j-white);font-weight:400;line-height:1.2}.j-text--no-shadow{text-shadow:none}.j-text--upper{letter-spacing:.08em}.j-text--dance-container{white-space:pre-wrap;display:inline-block}@keyframes j-font-dance{0%{transform:translateY(0)}25%{transform:translateY(-1px)}50%{transform:translateY(0)}75%{transform:translateY(1px)}to{transform:translateY(0)}}.j-text--display{letter-spacing:.04em;font-size:26px;line-height:1}.j-text--xl{letter-spacing:.04em;font-size:24px}.j-text--lg{letter-spacing:.04em;font-size:18px}.j-text--heading{letter-spacing:.08em;font-size:14px;line-height:1.2}.j-text--md{font-size:14px}.j-text--sm{font-size:12px}.j-text--body{letter-spacing:.05em;font-size:11px;line-height:1.3}.j-text--xs{font-size:10px}.j-text--label{letter-spacing:.1em;font-size:9px;line-height:1}.j-text--micro{letter-spacing:.08em;font-size:8px;line-height:1}.j-text--default{color:var(--j-white)}.j-text--mult,.j-text--red{color:var(--j-red)}.j-text--chips,.j-text--blue{color:var(--j-blue)}.j-text--gold{color:var(--j-gold-text)}.j-text--green{color:var(--j-green-text)}.j-text--orange{color:var(--j-orange-text)}.j-text--purple{color:var(--j-purple)}.j-text--grey{color:var(--j-grey)}.j-panel{background-color:var(--j-dark-grey);border:2px solid var(--j-border-silver);border-bottom-color:var(--j-border-south);border-radius:var(--j-radius-md);padding:var(--j-space-lg);flex-direction:column;align-items:stretch;display:flex;position:relative;overflow:visible;box-shadow:0 3px #0000008c,inset 0 0 0 1px #ffffff0a}.j-panel__body{flex:1;overflow:auto}.j-panel__back{margin-top:var(--j-space-lg);padding-top:var(--j-space-md);flex-shrink:0}.j-back-btn .j-btn__face{padding-top:8px;padding-bottom:8px}.j-inner-panel{background-color:var(--j-inner-border);border:2px solid var(--j-panel-edge);border-radius:var(--j-radius-md);padding:var(--j-space-lg)}.j-btn{appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;padding:0;display:inline-block;position:relative}.j-btn:after{content:"";z-index:10;background:0 0;position:absolute;inset:0}.j-btn--full{width:100%}.j-btn--disabled{opacity:.55;cursor:not-allowed}.j-btn__face{border-radius:var(--j-radius-md);background:var(--j-btn-face-color,var(--j-orange));text-align:center;text-shadow:1px 1px #000c;box-shadow:0 var(--j-press-y) 0 0 #0009;transition:transform var(--j-press-speed) linear, box-shadow var(--j-press-speed) linear;position:relative;transform:translate(0)}.j-btn[data-pressed=true] .j-btn__face,.j-btn:active:not(:disabled):not(.j-btn--disabled) .j-btn__face{transform:translateY(var(--j-press-y));box-shadow:0 0 #0009}.j-btn:not(.j-btn--disabled):hover .j-btn__face{filter:brightness(1.1)}.j-btn--xs .j-btn__face{padding:4px 8px;font-size:11px}.j-btn--sm .j-btn__face{padding:6px 12px;font-size:13px}.j-btn--md .j-btn__face{padding:10px 18px;font-size:16px}.j-btn--lg .j-btn__face{padding:8px 18px;font-size:20px}.j-btn--orange{--j-btn-face-color:var(--j-orange)}.j-btn--red{--j-btn-face-color:var(--j-red)}.j-btn--blue{--j-btn-face-color:var(--j-blue)}.j-btn--green{--j-btn-face-color:var(--j-green)}.j-btn--tarot{--j-btn-face-color:var(--j-tarot-btn)}.j-btn--planet{--j-btn-face-color:var(--j-planet-btn)}.j-btn--spectral{--j-btn-face-color:var(--j-spectral-btn)}.j-badge{border-radius:var(--j-radius-sm);font-family:var(--j-font);letter-spacing:.04em;white-space:nowrap;text-shadow:1px 1px #000c;border:none;align-items:center;font-weight:400;display:inline-flex}.j-badge--sm{padding:2px 6px;font-size:10px}.j-badge--md{padding:4px 8px;font-size:12px}.j-badge--dark{background:var(--j-darkest);color:var(--j-white)}.j-badge--blue{background:var(--j-blue);color:var(--j-white)}.j-badge--red{background:var(--j-red);color:var(--j-white)}.j-badge--green{background:var(--j-green);color:var(--j-white)}.j-badge--orange{background:var(--j-orange);color:var(--j-white)}.j-badge--purple{background:var(--j-purple);color:var(--j-white)}.j-tabs{gap:var(--j-space-sm);scrollbar-width:none;flex-wrap:nowrap;justify-content:center;align-items:flex-end;width:100%;display:flex;overflow-x:auto}.j-tabs::-webkit-scrollbar{display:none}.j-tab{flex-direction:column;flex:none;align-items:center;display:flex;position:relative}.j-tab__indicator{justify-content:center;align-items:flex-end;height:10px;margin-bottom:3px;display:flex}.j-tab__indicator svg{fill:var(--j-red);display:block}.j-tab__indicator[data-active=true]{animation:.6s linear infinite jimbo-bounce}.j-tab__indicator[data-active=false]{visibility:hidden}.j-tab__btn{appearance:none;cursor:pointer;border-radius:var(--j-radius-md);background:var(--j-red);min-height:28px;color:var(--j-white);text-align:center;text-shadow:1px 1px #000c;border:none;outline:none;padding:6px 14px;transition:filter 80ms linear,opacity 80ms linear;box-shadow:0 3px #0009}.j-tab__btn[data-active=true]{opacity:1}.j-tab__btn[data-active=true]:hover{background:var(--j-red)}.j-tab__btn[data-active=false]:hover{background:var(--j-red);filter:brightness(1.08)}@keyframes jimbo-bounce{0%,15%{transform:translateY(0)}50%{transform:translateY(-3px)}85%,to{transform:translateY(0)}}.j-vtabs{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-vtab{cursor:pointer;writing-mode:vertical-rl;text-orientation:mixed;background-color:#0000;border:none;border-radius:8px 0 0 8px;padding:16px 8px;transition:none;transform:rotate(180deg)}.j-vtab[data-active=true]{background-color:var(--j-gold,#e4b643);color:var(--j-black,#000)}.j-toggle-list{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-toggle-list__title{font-family:var(--j-font);color:var(--j-grey);letter-spacing:.04em;margin-bottom:var(--j-space-sm);font-size:12px}.j-toggle-item{align-items:center;gap:var(--j-space-md);border-radius:var(--j-radius-sm);cursor:pointer;font-family:var(--j-font);color:var(--j-white);letter-spacing:.04em;text-align:left;background:#ffffff0d;border:1px solid #0003;justify-content:flex-start;padding:6px 8px;display:flex}.j-toggle-check{border:1px solid var(--j-dark-grey);flex-shrink:0;width:10px;height:10px;box-shadow:inset 0 1px 2px #00000080}.j-toggle-check[data-on=true]{background:var(--j-orange)}.j-toggle-check[data-on=false]{background:var(--j-darkest)}.j-tooltip{border-radius:var(--j-radius-md);background:var(--j-darkest);border:2px solid var(--j-border-silver);max-width:280px;color:var(--j-white);pointer-events:none;z-index:9999;padding:6px 10px;transition:opacity .12s;position:fixed;box-shadow:0 2px #000c}.j-items-start{align-items:flex-start}.j-justify-between{justify-content:space-between}.j-h-full{height:100%}.j-flank{justify-content:center;align-items:stretch;gap:var(--j-space-md);width:100%;display:flex;position:relative}.j-flank__content{flex-direction:column;flex:1;min-width:0;display:flex;position:relative}.j-flank__btn{width:40px;margin:var(--j-press-y) 0;border-radius:var(--j-radius-lg);cursor:pointer;background-color:var(--j-red);color:var(--j-white);text-shadow:1px 1px #000c;box-shadow:0 var(--j-press-y) 0 0 var(--j-dark-red);transition:transform var(--j-press-speed) ease, box-shadow var(--j-press-speed) ease, background-color var(--j-press-speed) ease;border:none;flex-shrink:0;justify-content:center;align-items:center;display:flex}.j-flank__btn:disabled{background-color:var(--j-dark-red);cursor:default;box-shadow:none}.j-copy-row{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-copy-row__field{align-items:center;gap:var(--j-space-md);display:flex}.j-copy-row__value{background:var(--j-darkest);border:2px solid var(--j-panel-edge);border-radius:var(--j-radius-sm);word-break:break-all;flex:1;padding:6px 10px}.j-copy-row__btn{font-family:var(--j-font);letter-spacing:.08em;border-radius:var(--j-radius-sm);cursor:pointer;flex-shrink:0;padding:4px 12px;font-size:11px;transition:color .15s,background .15s,border-color .15s}.j-copy-row__btn[data-copied=false]{color:var(--j-gold-text);border:1px solid var(--j-gold-text);background:#e4b6431f}.j-copy-row__btn[data-copied=true]{color:var(--j-green-text);border:1px solid var(--j-green-text);background:#35bd861f}.j-code-block{background-color:var(--j-darkest);border:2px solid var(--j-panel-edge);border-radius:var(--j-radius-lg);flex-direction:column;display:flex;overflow:hidden;box-shadow:0 3px #00000080}.j-code-block__header{border-bottom:1px solid var(--j-inner-border);justify-content:space-between;align-items:center;padding:8px 12px;display:flex}.j-code-block__meta{align-items:center;gap:8px;display:flex}.j-code-block__filename{opacity:.6;font-size:10px}.j-code-block__lang{color:#60a5fa;background:#0006;border-radius:3px;padding:1px 6px;font-size:9px}.j-code-block__copy{cursor:pointer;background:0 0;border:none;padding:4px;display:flex}.j-code-block__copy[data-copied=false]{color:#ffffff80}.j-code-block__copy[data-copied=true]{color:#4ade80}.j-code-block__pre{font-family:var(--j-font-code);color:#f6f0d5;margin:0;padding:12px;font-size:.875rem;line-height:1.6;overflow-x:auto}.j-filter-bar{padding:var(--j-space-xl);background-color:var(--j-dark-grey);border:4px solid var(--j-border-silver);box-shadow:0 3px 0 0 var(--j-border-south);border-radius:var(--j-radius-lg);flex-wrap:wrap;gap:24px;display:flex;position:relative}.j-filter-bar__field{flex:1;min-width:200px;margin-top:10px;position:relative}.j-filter-bar__pill{background-color:var(--j-red);border:2px solid var(--j-dark-red);border-radius:var(--j-radius-md);z-index:2;padding:4px 12px;position:absolute;top:-14px;left:16px}.j-filter-bar__input{background-color:var(--j-darkest);border:none;border-bottom:4px solid var(--j-panel-edge);border-radius:var(--j-radius-lg);width:100%;color:var(--j-white);font-family:var(--j-font);letter-spacing:.08em;outline:none;padding:14px 16px 14px 48px;font-size:20px}.j-filter-bar__search-icon{pointer-events:none;width:48px;color:var(--j-blue);z-index:1;justify-content:center;align-items:center;display:flex;position:absolute;top:0;bottom:0;left:0}.j-filter-bar__select{appearance:none;background-color:var(--j-orange);color:var(--j-white);border:none;border-bottom:4px solid var(--j-dark-orange);border-radius:var(--j-radius-lg);cursor:pointer;font-family:var(--j-font);letter-spacing:.08em;text-align:center;outline:none;min-width:200px;padding:14px 48px 14px 24px;font-size:18px}.j-filter-bar__sort-icon{pointer-events:none;color:var(--j-white);opacity:.85;position:absolute;top:50%;right:16px;transform:translateY(-50%)}.j-flank{justify-content:center;align-items:center;gap:var(--j-space-md);width:100%;display:flex}.j-flank__content{flex:1;justify-content:center;display:flex}.j-flank__btn{background:var(--j-red);color:var(--j-white);border-radius:var(--j-radius-sm);padding:var(--j-space-sm) var(--j-space-xs);cursor:pointer;box-shadow:0 var(--j-press-y) 0 var(--j-dark-grey);border:none;justify-content:center;align-items:center;transition:transform 50ms,box-shadow 50ms,opacity .2s;display:flex}.j-flank__btn[data-pressed=true]{transform:translateY(var(--j-press-y));box-shadow:0 0 0 var(--j-dark-grey)}.j-flank__btn:disabled{opacity:.5;cursor:not-allowed;box-shadow:0 var(--j-press-y) 0 var(--j-dark-grey);transform:none}@keyframes jimbo-modal-in{0%{opacity:0;transform:scale(.88)}to{opacity:1;transform:scale(1)}}.j-modal-overlay{z-index:1000;padding:var(--j-space-md);background:#000000d1;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.j-modal{border-radius:12px;flex-direction:column;width:100%;max-width:345px;max-height:calc(100% - 32px);animation:.14s cubic-bezier(.2,0,.2,1.4) both jimbo-modal-in;display:flex;overflow:hidden}.j-modal .j-panel__body{min-height:0;overflow-y:auto}.j-modal__title{text-align:center;margin:0 0 var(--j-space-xl);flex-shrink:0}.j-footer{z-index:9998;pointer-events:auto;width:100%;position:fixed;bottom:0;left:0;right:0}.j-footer__bar{text-align:center;background:0 0;width:100%;padding:0 1rem 3px}.j-footer__text{font-family:var(--j-font);color:#fff;flex-wrap:wrap;justify-content:center;align-items:center;gap:0 .5rem;margin:0;font-size:clamp(11px,.8vw + 8px,14px);display:flex}.j-footer__link{color:var(--j-gold);text-decoration:none}.j-floating{z-index:20;position:absolute}.j-glow--must{box-shadow:0 0 0 2px var(--j-blue), 0 0 10px var(--j-blue);animation:1.6s ease-in-out infinite j-glow-pulse}.j-glow--should{box-shadow:0 0 0 2px var(--j-gold), 0 0 10px var(--j-gold);animation:1.6s ease-in-out infinite j-glow-pulse}.j-glow--match{box-shadow:0 0 0 2px var(--j-green-text), 0 0 18px var(--j-green-text), 0 0 36px var(--j-green-text);animation:1.6s ease-in-out infinite j-glow-pulse}@keyframes j-glow-pulse{0%,to{opacity:.55}50%{opacity:1}}.j-flex{display:flex}.j-flex-col{flex-direction:column;display:flex}.j-flex-wrap{flex-wrap:wrap}.j-items-center{align-items:center}.j-justify-center{justify-content:center}.j-gap-xs{gap:var(--j-space-xs)}.j-gap-sm{gap:var(--j-space-sm)}.j-gap-md{gap:var(--j-space-md)}.j-gap-lg{gap:var(--j-space-lg)}.j-gap-xl{gap:var(--j-space-xl)}.j-w-full{width:100%}.j-shrink-0{flex-shrink:0}.j-flex-1{flex:1}.j-min-w-0{min-width:0}.j-text-center{text-align:center}.j-overflow-hidden{overflow:hidden}.j-overflow-auto{overflow:auto}.j-relative{position:relative}.j-border--red{border-color:var(--j-red)}.j-border--blue{border-color:var(--j-blue)}.j-border--green{border-color:var(--j-green)}.j-border--gold{border-color:var(--j-gold)}.j-border--orange{border-color:var(--j-orange)}.j-border--purple{border-color:var(--j-purple)}.j-bg--red{background-color:var(--j-red)}.j-bg--blue{background-color:var(--j-blue)}.j-bg--green{background-color:var(--j-green)}.j-bg--gold{background-color:var(--j-gold)}.j-bg--orange{background-color:var(--j-orange)}.j-bg--purple{background-color:var(--j-purple)}.j-bg--dark-grey{background-color:var(--j-dark-grey)}.j-bg--darkest{background-color:var(--j-darkest)}.j-app{background:var(--j-darkest);width:375px;max-width:375px;height:667px;max-height:667px;font-family:var(--j-font);color:var(--j-white);flex-direction:column;margin:0 auto;display:flex;position:relative;overflow:hidden;container:jimbo/inline-size}.j-app--fluid{width:100%;max-width:750px;height:auto;min-height:400px;max-height:none}.j-app__content{min-height:0;padding:var(--j-space-lg) var(--j-space-lg) var(--j-space-md);flex-direction:column;flex:1;display:flex;overflow:hidden}.j-app__scroll{min-height:0;padding:var(--j-space-lg) var(--j-space-lg) var(--j-space-md);scrollbar-width:none;-ms-overflow-style:none;flex:1;overflow:hidden auto}.j-app__scroll::-webkit-scrollbar{display:none}.j-app__footer{padding:var(--j-space-md) var(--j-space-lg) var(--j-space-md);border-top:2px solid var(--j-black);background:var(--j-dark-grey);gap:var(--j-space-sm);flex-direction:column;flex-shrink:0;display:flex}@container jimbo (width>=401px){.j-app__content,.j-app__scroll{padding:var(--j-space-xl) var(--j-space-xl) var(--j-space-lg)}.j-app__footer{padding:var(--j-space-md) var(--j-space-xl) var(--j-space-lg)}.j-stat-grid__value{font-size:20px}.j-info-card{padding:var(--j-space-md) var(--j-space-lg)}.j-info-card__title{font-size:14px}.j-section-header__tag{font-size:12px}}.j-section-header{align-items:center;gap:var(--j-space-md);margin-bottom:var(--j-space-md);display:flex}.j-section-header__tag{font-family:var(--j-font);letter-spacing:2px;color:var(--j-white);text-shadow:var(--j-text-shadow);border-radius:3px;flex-shrink:0;padding:2px 8px;font-size:11px}.j-section-header__rule{opacity:.33;border-radius:1px;flex:1;height:2px}.j-stat-grid{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);gap:var(--j-space-md);text-align:center;grid-template-columns:1fr 1fr 1fr;display:grid}.j-stat-grid__value{font-family:var(--j-font);color:var(--j-gold-text);text-shadow:var(--j-text-shadow);font-size:16px}.j-stat-grid__label{font-family:var(--j-font);color:var(--j-grey);letter-spacing:2px;margin-top:2px;font-size:9px}.j-info-card{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md);box-shadow:0 2px 0 var(--j-black);align-items:center;gap:var(--j-space-md);cursor:pointer;border:2px solid #0000;display:flex}.j-info-card:hover{filter:brightness(1.08)}.j-info-card__body{flex:1;min-width:0}.j-info-card__title{font-family:var(--j-font);color:var(--j-white);letter-spacing:1px;text-shadow:var(--j-text-shadow);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.j-info-card__sub{font-family:var(--j-font);color:var(--j-gold-text);letter-spacing:1px;margin-top:2px;font-size:9px}.j-info-card__aside{text-align:right;flex-shrink:0}.j-inset{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md) var(--j-space-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);font-family:var(--j-font);color:var(--j-grey);letter-spacing:1px;font-size:11px;line-height:1.7}.j-wordmark{text-align:center;margin-bottom:var(--j-space-xl)}.j-wordmark__title{font-family:var(--j-font);letter-spacing:3px;color:var(--j-gold-text);text-shadow:2px 2px #000c;font-size:32px;line-height:1}.j-wordmark__sub{font-family:var(--j-font);letter-spacing:4px;color:var(--j-grey);text-shadow:var(--j-text-shadow);margin-top:4px;font-size:14px}.j-seed-input{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-seed-input__field{border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);background:var(--j-darkest);width:100%;color:var(--j-gold-text);font-size:18px;font-family:var(--j-font);letter-spacing:.12em;text-shadow:var(--j-text-shadow);box-sizing:border-box;outline:none;padding:8px 12px;font-weight:400;transition:border-color .1s}.j-seed-input__field::placeholder{color:var(--j-grey);opacity:.6;letter-spacing:.06em;font-size:12px}.j-seed-input__field:focus{border-color:var(--j-gold)}.j-seed-input__field[data-valid=true]{border-color:var(--j-green)}.j-seed-input__field[data-valid=false]{border-color:var(--j-red)}.j-seed-input__field[data-valid=partial]{border-color:var(--j-panel-edge)}.j-seed-input__hint{font-family:var(--j-font);letter-spacing:.08em;color:var(--j-grey);text-shadow:var(--j-text-shadow);font-size:9px}.j-aesthetic-selector{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-aesthetic-selector__list{gap:var(--j-space-sm);flex-wrap:wrap;display:flex}.j-aesthetic-pill{border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);background:var(--j-darkest);color:var(--j-grey);cursor:pointer;font-size:11px;font-family:var(--j-font);letter-spacing:.04em;text-shadow:var(--j-text-shadow);padding:4px 10px;font-weight:400;transition:border-color .1s,background .1s,color .1s;box-shadow:0 2px #0000004d}.j-aesthetic-pill:hover{border-color:var(--j-gold);color:var(--j-gold-text)}.j-aesthetic-pill[data-active=true]{border-color:var(--j-gold);color:var(--j-gold-text);box-shadow:0 0 0 1px var(--j-gold), 0 2px 0 0 #0000004d;background:#e4b64321}.j-showcase{background:var(--j-darkest);width:100%;height:100%;font-family:var(--j-font);color:var(--j-white);flex-direction:column;display:flex;overflow:hidden}.j-showcase__scroll{flex:1;min-height:0;padding:18px 14px 10px;overflow-y:auto}.j-showcase__wordmark{text-align:center;margin-bottom:18px}.j-showcase__wordmark-title{letter-spacing:3px;color:var(--j-gold-text);text-shadow:2px 2px #000c;font-size:32px;line-height:1}.j-showcase__wordmark-sub{letter-spacing:4px;color:var(--j-grey);text-shadow:var(--j-text-shadow);margin-top:4px;font-size:14px}.j-showcase__stats{background:var(--j-dark-grey);border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);gap:var(--j-space-md);text-align:center;margin-bottom:var(--j-space-xl);grid-template-columns:1fr 1fr 1fr;padding:10px;display:grid}.j-showcase__stat-value{color:var(--j-gold-text);text-shadow:var(--j-text-shadow);font-size:16px}.j-showcase__stat-label{color:var(--j-grey);letter-spacing:2px;margin-top:2px;font-size:9px}.j-showcase__section-header{align-items:center;gap:var(--j-space-md);margin-bottom:var(--j-space-md);display:flex}.j-showcase__section-tag{letter-spacing:2px;color:var(--j-white);text-shadow:var(--j-text-shadow);border-radius:3px;padding:2px 8px;font-size:11px}.j-showcase__section-rule{border-radius:1px;flex:1;height:2px}.j-showcase__filter-list{gap:var(--j-space-md);margin-bottom:var(--j-space-xl);flex-direction:column;display:flex}.j-showcase__filter-card{background:var(--j-dark-grey);border-radius:var(--j-radius-md);box-shadow:0 2px 0 var(--j-black);cursor:pointer;align-items:center;gap:10px;padding:10px;display:flex}.j-showcase__filter-sprites{gap:2px;display:flex}.j-showcase__filter-sprite{justify-content:center;align-items:center;width:30px;height:40px;display:flex}.j-showcase__filter-info{flex:1;min-width:0}.j-showcase__filter-name{color:var(--j-white);letter-spacing:1px;text-shadow:var(--j-text-shadow);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.j-showcase__filter-author{color:var(--j-gold-text);letter-spacing:1px;margin-top:2px;font-size:9px}.j-showcase__filter-hits{text-align:right}.j-showcase__filter-hits-value{text-shadow:var(--j-text-shadow);font-size:14px}.j-showcase__filter-hits-label{color:var(--j-grey);letter-spacing:1px;font-size:8px}.j-showcase__recent{background:var(--j-dark-grey);border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);color:var(--j-grey);letter-spacing:1px;padding:8px 10px;font-size:11px;line-height:1.7}.j-showcase__actions{border-top:2px solid var(--j-black);background:var(--j-dark-grey);flex-direction:column;gap:6px;padding:8px 10px 10px;display:flex}.j-footer__suits{align-items:center;display:inline-flex}.j-footer__suit-stage{vertical-align:middle;width:1.5em;height:1em;display:inline-block;position:relative}.j-footer__suit-char{opacity:0;justify-content:center;align-items:center;animation-duration:5s;animation-timing-function:ease-out;animation-iteration-count:infinite;animation-delay:0s;display:inline-flex;position:absolute;inset:0}.j-copy-row__label{letter-spacing:2px}.j-motely-badge{align-items:center;gap:6px;display:inline-flex}.j-motely-badge--chip{border-radius:var(--j-radius-sm);background:var(--j-darkest);border:1px solid var(--j-panel-edge);padding:3px 8px}.j-text--dance-container{display:inline-flex}.j-font-dance-char{animation:3s ease-in-out infinite j-font-dance;display:inline-block}.hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.hide-scrollbar::-webkit-scrollbar{display:none}.j-juice-hover{transition:transform .2s cubic-bezier(.175,.885,.32,1.275)}.j-juice-hover:hover{z-index:5;transform:scale(1.05)translateY(-2px)}
|
|
1
|
+
@font-face{font-family:m6x11plus;src:url(/fonts/m6x11plus.otf)format("opentype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:m6x11;src:url(/fonts/m6x11plus.otf)format("opentype");font-weight:400;font-style:normal;font-display:swap}*{scrollbar-width:none!important;-ms-overflow-style:none!important}::-webkit-scrollbar{display:none!important}:root{--j-red:#fe5148;--j-blue:#0093ff;--j-green:#429f79;--j-orange:#ff9800;--j-gold:#e4b643;--j-purple:#9e74ce;--j-dark-red:#a02721;--j-dark-blue:#0057a1;--j-dark-orange:#a05b00;--j-dark-green:#215f46;--j-dark-purple:#5e437e;--j-dark-grey:#3a5055;--j-darkest:#1e2b2d;--j-grey:#708386;--j-teal-grey:#404c4e;--j-panel-edge:#1e2e32;--j-inner-border:#334461;--j-border-silver:#b9c2d2;--j-border-south:#777e89;--j-gold-text:#e4b643;--j-green-text:#35bd86;--j-orange-text:#ff8f00;--j-white:#fff;--j-black:#000;--j-tarot-btn:#9e74ce;--j-planet-btn:#00a7ca;--j-spectral-btn:#2e76fd;--j-tarot-btn-dark:#5e437e;--j-planet-btn-dark:#00657c;--j-spectral-btn-dark:#14449e;--j-font:"m6x11plus", "m6x11", monospace;--j-font-code:"JetBrains Mono", "Cascadia Code", "Fira Code", "SF Mono", SFMono-Regular, Menlo, Consolas, ui-monospace, "Courier New", monospace;--j-text-shadow:1px 1px 0 #000c;--j-space-xs:2px;--j-space-sm:4px;--j-space-md:8px;--j-space-lg:12px;--j-space-xl:16px;--j-radius-sm:4px;--j-radius-md:6px;--j-radius-lg:8px;--j-radius-pill:10px;--j-ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--j-press-y:3px;--j-press-speed:55ms;--j-panel-border-width:4px;--j-panel-border-width-heavy:6px;--j-surface-shadow-depth:4px;--j-idle-sheen-duration:5.6s;--j-idle-bob-duration:3.4s}.j-text{font-family:var(--j-font);text-shadow:var(--j-text-shadow);color:var(--j-white);font-weight:400;line-height:1.2}.j-text--no-shadow{text-shadow:none}.j-text--upper{letter-spacing:.08em}.j-text--dance-container{white-space:pre-wrap;display:inline-block}@keyframes j-font-dance{0%{transform:translateY(0)}25%{transform:translateY(-1px)}50%{transform:translateY(0)}75%{transform:translateY(1px)}to{transform:translateY(0)}}.j-text--display{letter-spacing:.04em;font-size:26px;line-height:1}.j-text--xl{letter-spacing:.04em;font-size:24px}.j-text--lg{letter-spacing:.04em;font-size:18px}.j-text--heading{letter-spacing:.08em;font-size:14px;line-height:1.2}.j-text--md{font-size:14px}.j-text--sm{font-size:12px}.j-text--body{letter-spacing:.05em;font-size:11px;line-height:1.3}.j-text--xs{font-size:10px}.j-text--label{letter-spacing:.1em;font-size:9px;line-height:1}.j-text--micro{letter-spacing:.08em;font-size:8px;line-height:1}.j-text--default{color:var(--j-white)}.j-text--mult,.j-text--red{color:var(--j-red)}.j-text--chips,.j-text--blue{color:var(--j-blue)}.j-text--gold{color:var(--j-gold-text)}.j-text--green{color:var(--j-green-text)}.j-text--orange{color:var(--j-orange-text)}.j-text--purple{color:var(--j-purple)}.j-text--grey{color:var(--j-grey)}.j-panel{background-color:var(--j-dark-grey);border:var(--j-panel-border-width) solid var(--j-border-silver);border-radius:var(--j-radius-md);box-shadow:0 var(--j-panel-border-width-heavy) 0 0 var(--j-border-south), 0 calc(var(--j-surface-shadow-depth) + var(--j-panel-border-width-heavy)) 0 0 #0000008c, inset 0 0 0 1px #ffffff0a;padding:var(--j-space-lg);isolation:isolate;flex-direction:column;align-items:stretch;display:flex;position:relative;overflow:hidden}.j-panel:before{content:"";border-radius:inherit;opacity:.75;pointer-events:none;z-index:0;animation:j-surface-sheen var(--j-idle-sheen-duration) ease-in-out infinite;background:radial-gradient(circle at 18% 12%,#ffffff24,#0000 34%) 0 0/160% 160%,linear-gradient(125deg,#0000 0%,#ffffff12 26%,#0000 52%,#ffffff0d 74%,#0000 100%) 12% 18%/220% 220%;position:absolute;inset:0}.j-panel__body{z-index:1;flex:1;position:relative;overflow:auto}.j-panel__back{margin-top:var(--j-space-lg);padding-top:var(--j-space-md);z-index:1;flex-shrink:0;position:relative}.j-back-btn .j-btn__face{padding-top:8px;padding-bottom:8px}.j-back-btn-wrap{padding:4px 0}.j-inner-panel{background-color:var(--j-inner-border);border:var(--j-panel-border-width) solid var(--j-panel-edge);border-radius:var(--j-radius-md);padding:var(--j-space-lg);isolation:isolate;position:relative;overflow:hidden}.j-inner-panel:before{content:"";border-radius:inherit;opacity:.7;pointer-events:none;z-index:0;animation:j-surface-sheen calc(var(--j-idle-sheen-duration) + .8s) ease-in-out infinite reverse;background:radial-gradient(circle at 16% 10%,#ffffff1f,#0000 28%) 0 0/180% 180%,linear-gradient(145deg,#0000 0%,#ffffff0f 30%,#0000 58%,#ffffff0a 84%,#0000 100%) 0 0/230% 230%;position:absolute;inset:0}.j-inner-panel>*{z-index:1;position:relative}.j-btn{appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;padding:0;display:inline-block;position:relative}.j-btn:after{content:"";z-index:10;background:0 0;position:absolute;inset:0}.j-btn--full{width:100%}.j-btn--disabled{opacity:.55;cursor:not-allowed}.j-btn__face{border-radius:var(--j-radius-md);background:var(--j-btn-face-color,var(--j-orange));text-align:center;text-shadow:1px 1px #000c;box-shadow:0 calc(var(--j-press-y) + 2px) 0 0 #0009;transition:transform var(--j-press-speed) linear, box-shadow var(--j-press-speed) linear;isolation:isolate;position:relative;overflow:hidden;transform:translate(0)}.j-btn__face:before{content:"";border-radius:inherit;opacity:.58;pointer-events:none;z-index:0;animation:j-surface-sheen calc(var(--j-idle-sheen-duration) - .9s) ease-in-out infinite;background:radial-gradient(circle at 20% 16%,#ffffff2e,#0000 36%) 0 0/165% 165%,linear-gradient(130deg,#0000 0%,#ffffff1a 26%,#0000 58%,#ffffff0f 82%,#0000 100%) 0 0/225% 225%;position:absolute;inset:0}.j-btn__face>*{z-index:1;position:relative}.j-btn[data-pressed=true] .j-btn__face,.j-btn:active:not(:disabled):not(.j-btn--disabled) .j-btn__face{transform:translateY(var(--j-press-y));box-shadow:0 0 #0009}.j-btn:not(.j-btn--disabled):hover .j-btn__face{filter:brightness(1.1)}.j-btn--xs .j-btn__face{padding:6px 8px 3px;font-size:11px}.j-btn--sm .j-btn__face{padding:6px 12px 5px;font-size:13px}.j-btn--md .j-btn__face{padding:6px 18px 9px;font-size:16px}.j-btn--lg .j-btn__face{padding:6px 18px 11px;font-size:20px}.j-btn--orange{--j-btn-face-color:var(--j-orange)}.j-btn--red{--j-btn-face-color:var(--j-red)}.j-btn--blue{--j-btn-face-color:var(--j-blue)}.j-btn--green{--j-btn-face-color:var(--j-green)}.j-btn--tarot{--j-btn-face-color:var(--j-tarot-btn)}.j-btn--planet{--j-btn-face-color:var(--j-planet-btn)}.j-btn--spectral{--j-btn-face-color:var(--j-spectral-btn)}.j-badge{border-radius:var(--j-radius-sm);font-family:var(--j-font);letter-spacing:.04em;white-space:nowrap;text-shadow:1px 1px #000c;border:none;align-items:center;font-weight:400;display:inline-flex}.j-badge--sm{padding:2px 6px;font-size:10px}.j-badge--md{padding:4px 8px;font-size:12px}.j-badge--dark{background:var(--j-darkest);color:var(--j-white)}.j-badge--blue{background:var(--j-blue);color:var(--j-white)}.j-badge--red{background:var(--j-red);color:var(--j-white)}.j-badge--green{background:var(--j-green);color:var(--j-white)}.j-badge--orange{background:var(--j-orange);color:var(--j-white)}.j-badge--purple{background:var(--j-purple);color:var(--j-white)}.j-tabs{gap:var(--j-space-sm);scrollbar-width:none;flex-wrap:nowrap;justify-content:center;align-items:flex-end;width:100%;display:flex;overflow-x:auto}.j-tabs::-webkit-scrollbar{display:none}.j-tab{flex-direction:column;flex:none;align-items:center;display:flex;position:relative}.j-tab__indicator{justify-content:center;align-items:flex-end;height:10px;margin-bottom:3px;display:flex}.j-tab__indicator svg{fill:var(--j-red);display:block}.j-tab__indicator[data-active=true]{animation:.6s linear infinite jimbo-bounce}.j-tab__indicator[data-active=false]{visibility:hidden}.j-tab__btn{appearance:none;cursor:pointer;border-radius:var(--j-radius-md);background:var(--j-red);min-height:28px;color:var(--j-white);text-align:center;text-shadow:1px 1px #000c;isolation:isolate;border:none;outline:none;padding:6px 14px;transition:filter 80ms linear,opacity 80ms linear;position:relative;overflow:hidden;box-shadow:0 5px #0009}.j-tab__btn:before{content:"";border-radius:inherit;opacity:.52;pointer-events:none;z-index:0;animation:j-surface-sheen calc(var(--j-idle-sheen-duration) - 1.2s) ease-in-out infinite;background:linear-gradient(135deg,#0000 0%,#ffffff1a 32%,#0000 60%,#ffffff0f 86%,#0000 100%) 0 0/220% 220%;position:absolute;inset:0}.j-tab__btn>*{z-index:1;position:relative}.j-tab__btn[data-active=true]{opacity:1;animation:j-idle-bob var(--j-idle-bob-duration) ease-in-out infinite}.j-tab__btn[data-active=true]:hover{background:var(--j-red)}.j-tab__btn[data-active=false]:hover{background:var(--j-red);filter:brightness(1.08)}@keyframes jimbo-bounce{0%,15%{transform:translateY(0)}50%{transform:translateY(-3px)}85%,to{transform:translateY(0)}}@keyframes j-surface-sheen{0%,to{opacity:.52;background-position:0 0,10% 20%}50%{opacity:.8;background-position:100% 100%,88% 74%}}@keyframes j-idle-bob{0%,to{transform:translateY(0)}50%{transform:translateY(-1px)}}.j-vtabs{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-vtab{cursor:pointer;writing-mode:vertical-rl;text-orientation:mixed;background-color:#0000;border:none;border-radius:8px 0 0 8px;padding:16px 8px;transition:none;transform:rotate(180deg)}.j-vtab[data-active=true]{background-color:var(--j-gold,#e4b643);color:var(--j-black,#000)}.j-toggle-list{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-toggle-list__title{font-family:var(--j-font);color:var(--j-grey);letter-spacing:.04em;margin-bottom:var(--j-space-sm);font-size:12px}.j-toggle-item{align-items:center;gap:var(--j-space-md);border-radius:var(--j-radius-sm);cursor:pointer;font-family:var(--j-font);color:var(--j-white);letter-spacing:.04em;text-align:left;background:#ffffff0d;border:1px solid #0003;justify-content:flex-start;padding:6px 8px;display:flex}.j-toggle-check{border:1px solid var(--j-dark-grey);flex-shrink:0;width:10px;height:10px;box-shadow:inset 0 1px 2px #00000080}.j-toggle-check[data-on=true]{background:var(--j-orange)}.j-toggle-check[data-on=false]{background:var(--j-darkest)}.j-tooltip{border-radius:var(--j-radius-md);background:var(--j-darkest);border:var(--j-panel-border-width) solid var(--j-border-silver);max-width:280px;color:var(--j-white);pointer-events:none;z-index:9999;padding:6px 10px;transition:opacity .12s;position:fixed;box-shadow:0 2px #000c}.j-items-start{align-items:flex-start}.j-justify-between{justify-content:space-between}.j-h-full{height:100%}.j-flank{justify-content:center;align-items:stretch;gap:var(--j-space-md);width:100%;display:flex;position:relative}.j-flank__content{flex-direction:column;flex:1;min-width:0;display:flex;position:relative}.j-flank__btn{width:40px;margin:var(--j-press-y) 0;border-radius:var(--j-radius-lg);cursor:pointer;background-color:var(--j-red);color:var(--j-white);text-shadow:1px 1px #000c;box-shadow:0 var(--j-press-y) 0 0 var(--j-dark-red);transition:transform var(--j-press-speed) ease, box-shadow var(--j-press-speed) ease, background-color var(--j-press-speed) ease;border:none;flex-shrink:0;justify-content:center;align-items:center;display:flex}.j-flank__btn:disabled{background-color:var(--j-dark-red);cursor:default;box-shadow:none}.j-copy-row{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-copy-row__field{align-items:center;gap:var(--j-space-md);display:flex}.j-copy-row__value{background:var(--j-darkest);border:var(--j-panel-border-width) solid var(--j-panel-edge);border-radius:var(--j-radius-sm);word-break:break-all;flex:1;padding:6px 10px}.j-copy-row__btn{font-family:var(--j-font);letter-spacing:.08em;border-radius:var(--j-radius-sm);cursor:pointer;flex-shrink:0;padding:4px 12px;font-size:11px;transition:color .15s,background .15s,border-color .15s}.j-copy-row__btn[data-copied=false]{color:var(--j-gold-text);border:1px solid var(--j-gold-text);background:#e4b6431f}.j-copy-row__btn[data-copied=true]{color:var(--j-green-text);border:1px solid var(--j-green-text);background:#35bd861f}.j-code-block{background-color:var(--j-darkest);border:var(--j-panel-border-width) solid var(--j-panel-edge);border-radius:var(--j-radius-lg);flex-direction:column;display:flex;overflow:hidden;box-shadow:0 3px #00000080}.j-code-block__header{border-bottom:1px solid var(--j-inner-border);justify-content:space-between;align-items:center;padding:8px 12px;display:flex}.j-code-block__meta{align-items:center;gap:8px;display:flex}.j-code-block__filename{opacity:.6;font-size:10px}.j-code-block__lang{color:#60a5fa;background:#0006;border-radius:3px;padding:1px 6px;font-size:9px}.j-code-block__copy{cursor:pointer;background:0 0;border:none;padding:4px;display:flex}.j-code-block__copy[data-copied=false]{color:#ffffff80}.j-code-block__copy[data-copied=true]{color:#4ade80}.j-code-block__pre{font-family:var(--j-font-code);color:#f6f0d5;margin:0;padding:12px;font-size:.875rem;line-height:1.6;overflow-x:auto}.j-filter-bar{padding:var(--j-space-xl);background-color:var(--j-dark-grey);border:4px solid var(--j-border-silver);box-shadow:0 3px 0 0 var(--j-border-south);border-radius:var(--j-radius-lg);flex-wrap:wrap;gap:24px;display:flex;position:relative}.j-filter-bar__field{flex:1;min-width:200px;margin-top:10px;position:relative}.j-filter-bar__pill{background-color:var(--j-red);border:2px solid var(--j-dark-red);border-radius:var(--j-radius-md);z-index:2;padding:4px 12px;position:absolute;top:-14px;left:16px}.j-filter-bar__input{background-color:var(--j-darkest);border:none;border-bottom:4px solid var(--j-panel-edge);border-radius:var(--j-radius-lg);width:100%;color:var(--j-white);font-family:var(--j-font);letter-spacing:.08em;outline:none;padding:14px 16px 14px 48px;font-size:20px}.j-filter-bar__search-icon{pointer-events:none;width:48px;color:var(--j-blue);z-index:1;justify-content:center;align-items:center;display:flex;position:absolute;top:0;bottom:0;left:0}.j-filter-bar__select{appearance:none;background-color:var(--j-orange);color:var(--j-white);border:none;border-bottom:4px solid var(--j-dark-orange);border-radius:var(--j-radius-lg);cursor:pointer;font-family:var(--j-font);letter-spacing:.08em;text-align:center;outline:none;min-width:200px;padding:14px 48px 14px 24px;font-size:18px}.j-filter-bar__sort-icon{pointer-events:none;color:var(--j-white);opacity:.85;position:absolute;top:50%;right:16px;transform:translateY(-50%)}.j-flank{justify-content:center;align-items:center;gap:var(--j-space-md);width:100%;display:flex}.j-flank__content{flex:1;justify-content:center;display:flex}.j-flank__btn{background:var(--j-red);color:var(--j-white);border-radius:var(--j-radius-sm);padding:var(--j-space-sm) var(--j-space-xs);cursor:pointer;box-shadow:0 var(--j-press-y) 0 var(--j-dark-grey);border:none;justify-content:center;align-items:center;transition:transform 50ms,box-shadow 50ms,opacity .2s;display:flex}.j-flank__btn[data-pressed=true]{transform:translateY(var(--j-press-y));box-shadow:0 0 0 var(--j-dark-grey)}.j-flank__btn:disabled{opacity:.5;cursor:not-allowed;box-shadow:0 var(--j-press-y) 0 var(--j-dark-grey);transform:none}@keyframes jimbo-modal-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.j-modal-overlay{z-index:1000;padding:var(--j-space-md);background:#000000d1;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.j-modal{border-radius:12px;flex-direction:column;width:100%;max-width:345px;max-height:calc(100% - 32px);animation:.14s cubic-bezier(.2,0,.2,1.4) both jimbo-modal-in;display:flex;overflow:hidden}.j-modal .j-panel__body{min-height:0;overflow-y:auto}.j-modal__title{text-align:center;margin:0 0 var(--j-space-xl);flex-shrink:0}.j-footer{z-index:9998;pointer-events:auto;width:100%;position:fixed;bottom:0;left:0;right:0}.j-footer__bar{background:var(--j-black);border-top:var(--j-panel-border-width) solid var(--j-panel-edge);text-align:center;width:100%;padding:4px 8px 5px}.j-footer__line{font-family:var(--j-font);color:#fff;z-index:1;margin:0;font-size:10px;line-height:1;position:relative}.j-footer__line--wrap{flex-wrap:wrap;justify-content:center;align-items:center;gap:.25rem;display:flex}.j-footer__sep{color:var(--j-grey)}.j-footer__link{color:var(--j-gold);text-decoration:none}.j-footer__extra{align-items:center;margin-left:.25rem;display:inline-flex}@media (width<=320px){.j-footer__bar{padding:4px 6px 5px}.j-footer__line,.j-footer__line--wrap{font-size:9px}}.j-floating{z-index:20;position:absolute}.j-glow--must{box-shadow:0 0 0 2px var(--j-blue), 0 0 10px var(--j-blue);animation:1.6s ease-in-out infinite j-glow-pulse}.j-glow--should{box-shadow:0 0 0 2px var(--j-gold), 0 0 10px var(--j-gold);animation:1.6s ease-in-out infinite j-glow-pulse}.j-glow--match{box-shadow:0 0 0 2px var(--j-green-text), 0 0 18px var(--j-green-text), 0 0 36px var(--j-green-text);animation:1.6s ease-in-out infinite j-glow-pulse}@keyframes j-glow-pulse{0%,to{opacity:.55}50%{opacity:1}}.j-flex{display:flex}.j-flex-col{flex-direction:column;display:flex}.j-flex-wrap{flex-wrap:wrap}.j-items-center{align-items:center}.j-justify-center{justify-content:center}.j-gap-xs{gap:var(--j-space-xs)}.j-gap-sm{gap:var(--j-space-sm)}.j-gap-md{gap:var(--j-space-md)}.j-gap-lg{gap:var(--j-space-lg)}.j-gap-xl{gap:var(--j-space-xl)}.j-mt-sm{margin-top:var(--j-space-sm)}.j-w-full{width:100%}.j-shrink-0{flex-shrink:0}.j-flex-1{flex:1}.j-min-w-0{min-width:0}.j-text-center{text-align:center}.j-overflow-hidden{overflow:hidden}.j-overflow-auto{overflow:auto}.j-relative{position:relative}.j-border--red{border-color:var(--j-red)}.j-border--blue{border-color:var(--j-blue)}.j-border--green{border-color:var(--j-green)}.j-border--gold{border-color:var(--j-gold)}.j-border--orange{border-color:var(--j-orange)}.j-border--purple{border-color:var(--j-purple)}.j-bg--red{background-color:var(--j-red)}.j-bg--blue{background-color:var(--j-blue)}.j-bg--green{background-color:var(--j-green)}.j-bg--gold{background-color:var(--j-gold)}.j-bg--orange{background-color:var(--j-orange)}.j-bg--purple{background-color:var(--j-purple)}.j-bg--dark-grey{background-color:var(--j-dark-grey)}.j-bg--darkest{background-color:var(--j-darkest)}.j-app{background:var(--j-darkest);width:min(100%,320px);max-width:320px;height:min(568px,100dvh);max-height:568px;font-family:var(--j-font);color:var(--j-white);flex-direction:column;margin:0 auto;display:flex;position:relative;overflow:hidden;container:jimbo/inline-size}.j-app--fluid{width:100%;max-width:750px;height:auto;min-height:400px;max-height:none}.j-app__content{min-height:0;padding:var(--j-space-lg) var(--j-space-lg) var(--j-space-md);flex-direction:column;flex:1;display:flex;overflow:hidden}.j-app__scroll{min-height:0;padding:var(--j-space-lg) var(--j-space-lg) var(--j-space-md);scrollbar-width:none;-ms-overflow-style:none;flex:1;overflow:hidden auto}.j-app__scroll::-webkit-scrollbar{display:none}.j-app__footer{padding:var(--j-space-md) var(--j-space-lg) var(--j-space-md);border-top:var(--j-panel-border-width) solid var(--j-black);background:var(--j-dark-grey);gap:var(--j-space-sm);flex-direction:column;flex-shrink:0;display:flex}@container jimbo (width>=401px){.j-app__content,.j-app__scroll{padding:var(--j-space-xl) var(--j-space-xl) var(--j-space-lg)}.j-app__footer{padding:var(--j-space-md) var(--j-space-xl) var(--j-space-lg)}.j-stat-grid__value{font-size:20px}.j-info-card{padding:var(--j-space-md) var(--j-space-lg)}.j-info-card__title{font-size:14px}.j-section-header__tag{font-size:12px}}.j-section-header{align-items:center;gap:var(--j-space-md);margin-bottom:var(--j-space-md);display:flex}.j-section-header__tag{font-family:var(--j-font);letter-spacing:2px;color:var(--j-white);text-shadow:var(--j-text-shadow);border-radius:3px;flex-shrink:0;padding:2px 8px;font-size:11px}.j-section-header__rule{opacity:.33;border-radius:1px;flex:1;height:2px}.j-stat-grid{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md);border:var(--j-panel-border-width) solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);gap:var(--j-space-md);text-align:center;grid-template-columns:1fr 1fr 1fr;display:grid}.j-stat-grid__value{font-family:var(--j-font);color:var(--j-gold-text);text-shadow:var(--j-text-shadow);font-size:16px}.j-stat-grid__label{font-family:var(--j-font);color:var(--j-grey);letter-spacing:2px;margin-top:2px;font-size:9px}.j-info-card{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md);box-shadow:0 2px 0 var(--j-black);align-items:center;gap:var(--j-space-md);cursor:pointer;border:var(--j-panel-border-width) solid transparent;display:flex}.j-info-card:hover{filter:brightness(1.08)}.j-info-card__body{flex:1;min-width:0}.j-info-card__title{font-family:var(--j-font);color:var(--j-white);letter-spacing:1px;text-shadow:var(--j-text-shadow);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.j-info-card__sub{font-family:var(--j-font);color:var(--j-gold-text);letter-spacing:1px;margin-top:2px;font-size:9px}.j-info-card__aside{text-align:right;flex-shrink:0}.j-inset{background:var(--j-dark-grey);border-radius:var(--j-radius-md);padding:var(--j-space-md) var(--j-space-md);border:var(--j-panel-border-width) solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);font-family:var(--j-font);color:var(--j-grey);letter-spacing:1px;font-size:11px;line-height:1.7}.j-wordmark{text-align:center;margin-bottom:var(--j-space-xl)}.j-wordmark__title{font-family:var(--j-font);letter-spacing:3px;color:var(--j-gold-text);text-shadow:2px 2px #000c;font-size:32px;line-height:1}.j-wordmark__sub{font-family:var(--j-font);letter-spacing:4px;color:var(--j-grey);text-shadow:var(--j-text-shadow);margin-top:4px;font-size:14px}.j-game-card{width:var(--j-card-width,71px)}.j-card-renderer{--j-card-aspect:.75;--j-card-glare-x:50%;--j-card-glare-y:50%;aspect-ratio:var(--j-card-aspect);perspective:900px;-webkit-user-select:none;user-select:none;width:100%;display:flex;position:relative}.j-card-renderer__canvas{border-radius:var(--j-radius-md);width:100%;height:100%;image-rendering:pixelated;pointer-events:none;transform-origin:50%;transform-style:preserve-3d;will-change:transform;box-shadow:0 3px 8px #00000052}.j-card-renderer[data-hover-tilt=true]:before{content:"";border-radius:var(--j-radius-md);background:radial-gradient(circle at var(--j-card-glare-x) var(--j-card-glare-y), #ffffff47, transparent 32%), linear-gradient(135deg, transparent 0%, #ffffff14 42%, transparent 68%);opacity:0;pointer-events:none;z-index:2;mix-blend-mode:screen;transition:opacity .12s linear;position:absolute;inset:0}.j-card-renderer[data-hover-tilt=true][data-hovered=true]:before{opacity:.72}.j-card-renderer[data-hover-tilt=true][data-hovered=true] .j-card-renderer__canvas{box-shadow:0 8px 16px #0000006b}.j-card-renderer__hit{z-index:3;position:absolute;inset:0}.j-card-renderer[data-hover-tilt=true] .j-card-renderer__hit{cursor:pointer}.j-panel-spinner{gap:var(--j-space-xs);flex-direction:column;display:flex}.j-panel-spinner__label{align-items:center;display:flex}.j-panel-spinner__row{align-items:stretch;gap:var(--j-space-sm);display:flex}.j-panel-spinner__panel{justify-content:center;align-items:center;gap:var(--j-space-xs);background:var(--j-darkest);border:var(--j-panel-border-width) solid var(--j-panel-edge);border-radius:var(--j-radius-md);text-align:center;min-width:0;box-shadow:0 var(--j-panel-border-width-heavy) 0 0 var(--j-black);flex-direction:column;flex:1;padding:8px 10px;display:flex}.j-panel-spinner__media{justify-content:center;align-items:center;min-height:60px;display:flex}.j-panel-spinner__title,.j-panel-spinner__meta,.j-panel-spinner__description{width:100%}.j-panel-spinner__description .j-text{white-space:pre-line}.j-ide{overscroll-behavior-x:none;border-radius:var(--j-radius-lg);border:var(--j-panel-border-width) solid var(--j-border-silver);background:var(--j-darkest);max-width:100%;min-height:420px;color:var(--j-white);box-shadow:0 var(--j-panel-border-width-heavy) 0 0 var(--j-border-south), 0 calc(var(--j-surface-shadow-depth) + var(--j-panel-border-width-heavy)) 0 0 #0000008c;flex-direction:column;display:flex;overflow:clip hidden}.j-ide__header{border-bottom:var(--j-panel-border-width) solid var(--j-panel-edge);background:var(--j-darkest);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;display:flex}.j-ide__header--compact{gap:8px;padding:8px 10px}.j-ide__header-copy{min-width:0}.j-ide__title{font-family:var(--j-font);color:var(--j-gold-text);text-shadow:var(--j-text-shadow);font-size:16px}.j-ide__subtitle{font-family:var(--j-font);color:var(--j-grey);text-shadow:var(--j-text-shadow);margin-top:2px;font-size:11px}.j-ide__actions{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.j-ide-toolbar{border-bottom:var(--j-panel-border-width) solid var(--j-panel-edge);background:var(--j-dark-grey);align-items:center;gap:8px;min-width:0;padding:10px 10px 6px;display:flex}.j-ide-toolbar__tabs{flex:1;min-width:0;padding-bottom:3px}.j-ide-toolbar__action{flex-shrink:0}.j-ide__body{background:var(--j-darkest);flex:1;min-height:0;overflow:auto}.j-ide__body--map{overflow:hidden}.j-ide__results{padding:12px}.j-seed-input{gap:var(--j-space-xs);flex-direction:column;width:fit-content;max-width:100%;display:flex}.j-seed-input--normal{--j-seed-shell-bg:var(--j-dark-grey);--j-seed-shell-border:var(--j-border-silver);--j-seed-shell-border-bottom:var(--j-border-south);--j-seed-field-bg:var(--j-darkest)}.j-seed-input--dark{--j-seed-shell-bg:var(--j-darkest);--j-seed-shell-border:var(--j-panel-edge);--j-seed-shell-border-bottom:var(--j-black);--j-seed-field-bg:var(--j-darkest)}.j-seed-input--alt{--j-seed-shell-bg:var(--j-teal-grey);--j-seed-shell-border:var(--j-border-silver);--j-seed-shell-border-bottom:var(--j-panel-edge);--j-seed-field-bg:var(--j-dark-grey)}.j-seed-input__shell{border-radius:var(--j-radius-md);border:var(--j-panel-border-width) solid var(--j-seed-shell-border);background:var(--j-seed-shell-bg);width:fit-content;max-width:100%;box-shadow:0 var(--j-panel-border-width-heavy) 0 0 var(--j-seed-shell-border-bottom), 0 calc(var(--j-surface-shadow-depth) + var(--j-panel-border-width-heavy)) 0 0 #0000008c;cursor:text;padding:3px}.j-seed-input__shell[data-valid=true]{border-color:var(--j-green);border-bottom-color:var(--j-dark-green)}.j-seed-input__field{border-radius:calc(var(--j-radius-md) - 2px);border:var(--j-panel-border-width) solid var(--j-panel-edge);background:var(--j-seed-field-bg);width:calc(8ch + 16px);min-width:calc(8ch + 16px);max-width:calc(8ch + 16px);color:var(--j-gold-text);font-size:18px;font-family:var(--j-font);letter-spacing:.08em;text-align:center;text-shadow:var(--j-text-shadow);box-sizing:border-box;outline:none;padding:6px 8px 8px;font-weight:400}.j-seed-input__field::placeholder{color:var(--j-grey);letter-spacing:.08em;opacity:1;font-size:18px}.j-seed-input__field:focus{border-color:var(--j-gold)}.j-seed-spinner{gap:var(--j-space-xs);flex-direction:column;width:fit-content;max-width:100%;display:flex}.j-seed-spinner__meta{justify-content:space-between;align-items:center;gap:var(--j-space-sm);display:flex}.j-seed-spinner__label{font-family:var(--j-font);letter-spacing:.08em;color:var(--j-grey);text-shadow:var(--j-text-shadow);font-size:10px}.j-seed-spinner__row{align-items:flex-end;gap:var(--j-space-sm);display:flex}.j-seed-spinner__copy{align-items:center;gap:4px;display:inline-flex}@media (prefers-reduced-motion:reduce){.j-panel:before,.j-inner-panel:before,.j-btn__face:before,.j-tab__btn:before,.j-footer__bar:before,.j-seed-input__shell:before,.j-tab__btn[data-active=true]{animation:none}}.j-aesthetic-selector{gap:var(--j-space-sm);flex-direction:column;display:flex}.j-aesthetic-selector__list{gap:var(--j-space-sm);flex-wrap:wrap;display:flex}.j-aesthetic-pill{border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);background:var(--j-darkest);color:var(--j-grey);cursor:pointer;font-size:11px;font-family:var(--j-font);letter-spacing:.04em;text-shadow:var(--j-text-shadow);padding:4px 10px;font-weight:400;transition:border-color .1s,background .1s,color .1s;box-shadow:0 2px #0000004d}.j-aesthetic-pill:hover{border-color:var(--j-gold);color:var(--j-gold-text)}.j-aesthetic-pill[data-active=true]{border-color:var(--j-gold);color:var(--j-gold-text);box-shadow:0 0 0 1px var(--j-gold), 0 2px 0 0 #0000004d;background:#e4b64321}.j-showcase{background:var(--j-darkest);width:100%;height:100%;font-family:var(--j-font);color:var(--j-white);flex-direction:column;display:flex;overflow:hidden}.j-showcase__scroll{flex:1;min-height:0;padding:18px 14px 10px;overflow-y:auto}.j-showcase__wordmark{text-align:center;margin-bottom:18px}.j-showcase__wordmark-title{letter-spacing:3px;color:var(--j-gold-text);text-shadow:2px 2px #000c;font-size:32px;line-height:1}.j-showcase__wordmark-sub{letter-spacing:4px;color:var(--j-grey);text-shadow:var(--j-text-shadow);margin-top:4px;font-size:14px}.j-showcase__stats{background:var(--j-dark-grey);border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);gap:var(--j-space-md);text-align:center;margin-bottom:var(--j-space-xl);grid-template-columns:1fr 1fr 1fr;padding:10px;display:grid}.j-showcase__stat-value{color:var(--j-gold-text);text-shadow:var(--j-text-shadow);font-size:16px}.j-showcase__stat-label{color:var(--j-grey);letter-spacing:2px;margin-top:2px;font-size:9px}.j-showcase__section-header{align-items:center;gap:var(--j-space-md);margin-bottom:var(--j-space-md);display:flex}.j-showcase__section-tag{letter-spacing:2px;color:var(--j-white);text-shadow:var(--j-text-shadow);border-radius:3px;padding:2px 8px;font-size:11px}.j-showcase__section-rule{border-radius:1px;flex:1;height:2px}.j-showcase__filter-list{gap:var(--j-space-md);margin-bottom:var(--j-space-xl);flex-direction:column;display:flex}.j-showcase__filter-card{background:var(--j-dark-grey);border-radius:var(--j-radius-md);box-shadow:0 2px 0 var(--j-black);cursor:pointer;align-items:center;gap:10px;padding:10px;display:flex}.j-showcase__filter-sprites{gap:2px;display:flex}.j-showcase__filter-sprite{justify-content:center;align-items:center;width:30px;height:40px;display:flex}.j-showcase__filter-info{flex:1;min-width:0}.j-showcase__filter-name{color:var(--j-white);letter-spacing:1px;text-shadow:var(--j-text-shadow);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.j-showcase__filter-author{color:var(--j-gold-text);letter-spacing:1px;margin-top:2px;font-size:9px}.j-showcase__filter-hits{text-align:right}.j-showcase__filter-hits-value{text-shadow:var(--j-text-shadow);font-size:14px}.j-showcase__filter-hits-label{color:var(--j-grey);letter-spacing:1px;font-size:8px}.j-showcase__recent{background:var(--j-dark-grey);border-radius:var(--j-radius-md);border:2px solid var(--j-panel-edge);box-shadow:0 2px 0 var(--j-black);color:var(--j-grey);letter-spacing:1px;padding:8px 10px;font-size:11px;line-height:1.7}.j-showcase__actions{border-top:2px solid var(--j-black);background:var(--j-dark-grey);flex-direction:column;gap:6px;padding:8px 10px 10px;display:flex}.j-footer__suits{align-items:center;display:inline-flex}.j-footer__suit-stage{vertical-align:middle;width:1.5em;height:1em;display:inline-block;position:relative}.j-footer__suit-char{opacity:0;justify-content:center;align-items:center;animation-duration:5s;animation-timing-function:ease-out;animation-iteration-count:infinite;animation-delay:0s;display:inline-flex;position:absolute;inset:0}.j-copy-row__label{letter-spacing:2px}.j-motely-badge{align-items:center;gap:6px;display:inline-flex}.j-motely-badge--chip{border-radius:var(--j-radius-sm);background:var(--j-darkest);border:1px solid var(--j-panel-edge);padding:3px 8px}.j-text--dance-container{display:inline-flex}.j-font-dance-char{animation:3s ease-in-out infinite j-font-dance;display:inline-block}.hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.hide-scrollbar::-webkit-scrollbar{display:none}.j-juice-hover{transition:transform .2s cubic-bezier(.175,.885,.32,1.275)}.j-juice-hover:hover{z-index:5;transform:translateY(-2px)}
|
|
2
2
|
/*$vite$:1*/
|
package/dist/ui/jimboText.d.ts
CHANGED
|
@@ -6,12 +6,10 @@ export interface JimboTextProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
6
6
|
size?: JimboTextSize;
|
|
7
7
|
/** Canonical Balatro drop shadow (1px right, 1px down, ${BLACK}cc). Default true. */
|
|
8
8
|
shadow?: boolean;
|
|
9
|
-
/**
|
|
10
|
-
uppercase?: boolean;
|
|
11
|
-
/** Wiggle effect for text characters. Default false. */
|
|
12
|
-
dance?: boolean;
|
|
13
|
-
/** Letter-spacing override; defaults depend on uppercase prop. */
|
|
9
|
+
/** Letter-spacing override */
|
|
14
10
|
letterSpacing?: number | string;
|
|
11
|
+
/** Whether to animate letters with a dancing effect */
|
|
12
|
+
dance?: boolean;
|
|
15
13
|
as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label';
|
|
16
14
|
children?: React.ReactNode;
|
|
17
15
|
}
|
|
@@ -25,4 +23,4 @@ export interface JimboTextProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
25
23
|
* so the @font-face declaration lands (font is base64-embedded, no
|
|
26
24
|
* runtime fetch).
|
|
27
25
|
*/
|
|
28
|
-
export declare function JimboText({ tone, size, shadow,
|
|
26
|
+
export declare function JimboText({ tone, size, shadow, dance, letterSpacing, as: Tag, className, style, children, ...rest }: JimboTextProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/ui/panel.d.ts
CHANGED
|
@@ -13,13 +13,12 @@ export interface JimboButtonProps {
|
|
|
13
13
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
14
14
|
fullWidth?: boolean;
|
|
15
15
|
disabled?: boolean;
|
|
16
|
-
uppercase?: boolean;
|
|
17
16
|
onClick?: () => void;
|
|
18
17
|
style?: React.CSSProperties;
|
|
19
18
|
className?: string;
|
|
20
19
|
children?: React.ReactNode;
|
|
21
20
|
}
|
|
22
|
-
export declare function JimboButton({ tone, size, fullWidth, disabled,
|
|
21
|
+
export declare function JimboButton({ tone, size, fullWidth, disabled, onClick, style, className, children, }: JimboButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
23
22
|
export declare function JimboBackButton({ onClick }: {
|
|
24
23
|
onClick?: () => void;
|
|
25
24
|
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/ui.d.ts
CHANGED
|
@@ -21,7 +21,9 @@ export * from './ui/jimboSectionHeader.js';
|
|
|
21
21
|
export * from './ui/jimboInfoCard.js';
|
|
22
22
|
export * from './ui/jimboInset.js';
|
|
23
23
|
export * from './ui/ide/JamlEditor.js';
|
|
24
|
+
export { default as JamlEditor } from './ui/ide/JamlEditor.js';
|
|
24
25
|
export * from './ui/JimboIconButton.js';
|
|
25
26
|
export * from './ui/JimboInputModal.js';
|
|
27
|
+
export * from './ui/JimboPanelSpinner.js';
|
|
26
28
|
export * from './ui/JimboSelect.js';
|
|
27
29
|
export * from './ui/PanelSplitter.js';
|
package/dist/ui.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as e,
|
|
2
|
-
import { n as
|
|
3
|
-
export {
|
|
1
|
+
import { A as e, B as t, C as n, D as r, E as i, F as a, I as o, L as s, M as c, N as l, O as u, P as d, R as f, S as p, T as m, _ as h, a as g, b as _, c as v, d as y, f as b, g as x, h as S, i as C, j as w, k as T, l as E, m as D, n as O, o as k, p as A, r as j, s as M, t as N, u as P, v as F, w as I, x as L, y as R, z } from "./chunks/ui-BEtq_JNg.js";
|
|
2
|
+
import { n as B, r as V, t as H } from "./chunks/tokens-B65Fzble.js";
|
|
3
|
+
export { f as DeckSprite, H as JIMBO_ANIMATIONS, k as JamlEditor, F as JimboApp, R as JimboAppFooter, _ as JimboAppScroll, c as JimboBackButton, n as JimboBackground, T as JimboBadge, I as JimboBalatroFooter, l as JimboButton, L as JimboCodeBlock, B as JimboColorOption, P as JimboCopyRow, m as JimboFilterBar, u as JimboFlankNav, r as JimboFloating, g as JimboIconButton, b as JimboInfoCard, A as JimboInfoCardAside, D as JimboInfoCardBody, S as JimboInfoCardSub, x as JimboInfoCardTitle, d as JimboInnerPanel, C as JimboInputModal, M as JimboInset, a as JimboModal, o as JimboPanel, j as JimboPanelSpinner, h as JimboSectionHeader, O as JimboSelect, z as JimboSprite, v as JimboStatGrid, e as JimboTabs, s as JimboText, i as JimboToggleList, p as JimboTooltip, w as JimboVerticalTabs, E as JimboWordmark, N as PanelSplitter, y as Showcase, t as StakeSprite, V as withAlpha };
|