dirk-cfx-react 1.1.94 → 1.1.96
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/dist/{chunk-ZPFW7C2A.js → chunk-3QPPR6OY.js} +3 -3
- package/dist/{chunk-ZPFW7C2A.js.map → chunk-3QPPR6OY.js.map} +1 -1
- package/dist/{chunk-MYNNCLMA.js → chunk-4QSXUENK.js} +325 -43
- package/dist/chunk-4QSXUENK.js.map +1 -0
- package/dist/{chunk-YA2PXBP6.cjs → chunk-5XODUPUE.cjs} +325 -43
- package/dist/chunk-5XODUPUE.cjs.map +1 -0
- package/dist/{chunk-3T2CCW5E.cjs → chunk-6CNT35D6.cjs} +9 -9
- package/dist/{chunk-3T2CCW5E.cjs.map → chunk-6CNT35D6.cjs.map} +1 -1
- package/dist/{chunk-KGLO3ZAS.js → chunk-ABBZHNLP.js} +3 -3
- package/dist/{chunk-KGLO3ZAS.js.map → chunk-ABBZHNLP.js.map} +1 -1
- package/dist/{chunk-QOQQ3ERZ.cjs → chunk-EOSHBZQK.cjs} +17 -17
- package/dist/{chunk-QOQQ3ERZ.cjs.map → chunk-EOSHBZQK.cjs.map} +1 -1
- package/dist/{chunk-Z7N5AQJW.js → chunk-GFODSZSP.js} +3 -3
- package/dist/{chunk-Z7N5AQJW.js.map → chunk-GFODSZSP.js.map} +1 -1
- package/dist/{chunk-K3GDU6EY.cjs → chunk-HG2J72NC.cjs} +167 -167
- package/dist/{chunk-K3GDU6EY.cjs.map → chunk-HG2J72NC.cjs.map} +1 -1
- package/dist/{chunk-E2SFEBBB.cjs → chunk-HOVRS2Z6.cjs} +4 -4
- package/dist/{chunk-E2SFEBBB.cjs.map → chunk-HOVRS2Z6.cjs.map} +1 -1
- package/dist/{chunk-VKYONY7E.cjs → chunk-MY3Z7LPB.cjs} +5 -5
- package/dist/{chunk-VKYONY7E.cjs.map → chunk-MY3Z7LPB.cjs.map} +1 -1
- package/dist/{chunk-HAB6H274.js → chunk-UGDMOWVM.js} +6 -6
- package/dist/{chunk-HAB6H274.js.map → chunk-UGDMOWVM.js.map} +1 -1
- package/dist/{chunk-NVMOGS7Y.js → chunk-WUALULEH.js} +4 -4
- package/dist/{chunk-NVMOGS7Y.js.map → chunk-WUALULEH.js.map} +1 -1
- package/dist/components/index.cjs +76 -76
- package/dist/components/index.js +5 -5
- package/dist/hooks/index.cjs +22 -22
- package/dist/hooks/index.js +4 -4
- package/dist/index.cjs +147 -147
- package/dist/index.js +6 -6
- package/dist/providers/index.cjs +5 -5
- package/dist/providers/index.js +3 -3
- package/dist/utils/index.cjs +48 -48
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MYNNCLMA.js.map +0 -1
- package/dist/chunk-YA2PXBP6.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useNuiEvent.ts","../src/hooks/useScriptConfig.ts"],"names":["useRef","noop","useEffect","create","fetchNui"],"mappings":"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","file":"chunk-3T2CCW5E.cjs","sourcesContent":["import { MutableRefObject, useEffect, useRef } from \"react\";\r\nimport { noop } from \"../utils/misc\";\r\n\r\nexport interface NuiMessageData<T = unknown> {\r\n action: string;\r\n data: T;\r\n}\r\n\r\nexport type NuiHandlerSignature<T> = ( data: T) => void;\r\n\r\n/**\r\n * A hook that manage events listeners for receiving data from the client scripts\r\n * @param action The specific `action` that should be listened for.\r\n * @param handler The callback function that will handle data relayed by this hook\r\n *\r\n * @example\r\n * useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {\r\n * // whatever logic you want\r\n * })\r\n *\r\n **/\r\n\r\nexport const useNuiEvent = <T = unknown>(\r\n action: string,\r\n handler: ( data: T) => void,\r\n) => {\r\n const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef(noop);\r\n\r\n // Make sure we handle for a reactive handler\r\n useEffect(() => {\r\n savedHandler.current = handler;\r\n }, [handler]);\r\n\r\n useEffect(() => {\r\n const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {\r\n const { action: eventAction, data } = event.data;\r\n\r\n if (savedHandler.current) {\r\n if (eventAction === action) {\r\n savedHandler.current( data);\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener(\"message\", eventListener);\r\n // Remove Event Listener on component dirkup\r\n return () => window.removeEventListener(\"message\", eventListener);\r\n }, [action]);\r\n};\r\n","import { fetchNui } from \"@/utils\";\r\nimport { useNuiEvent } from \"@/hooks/useNuiEvent\";\r\nimport { create } from \"zustand\";\r\n\r\ntype ScriptConfigUpdateMeta<T> = {\r\n client_version?: number;\r\n latestVersion?: number;\r\n latestData?: Partial<T>;\r\n changed_paths?: Array<{ path: string; old: unknown; new: unknown }>;\r\n lastEditor?: { source?: number; name?: string; identifier?: string };\r\n};\r\n\r\nexport type ScriptConfigHistoryChange = {\r\n path: string;\r\n old: unknown;\r\n new: unknown;\r\n};\r\n\r\nexport type ScriptConfigHistoryEntry = {\r\n at_unix: number;\r\n at_utc: string;\r\n script: string;\r\n admin?: { source?: number; name?: string; identifier?: string };\r\n expected_version?: number;\r\n applied_version?: number;\r\n changes: ScriptConfigHistoryChange[];\r\n};\r\n\r\nexport type ScriptConfigHistoryRequest = {\r\n offset?: number;\r\n limit?: number;\r\n query?: string;\r\n path?: string;\r\n admin?: string;\r\n fromUnix?: number;\r\n toUnix?: number;\r\n};\r\n\r\nexport type ScriptConfigHistoryResponse = {\r\n success: boolean;\r\n _error?: string;\r\n data?: {\r\n items: ScriptConfigHistoryEntry[];\r\n total: number;\r\n limit: number;\r\n offset: number;\r\n nextOffset?: number;\r\n };\r\n};\r\n\r\ntype NuiResponse<T> = {\r\n success: boolean;\r\n message?: string;\r\n _error?: string;\r\n meta?: ScriptConfigUpdateMeta<T>;\r\n};\r\n\r\n// Server-only \"sliver\" response (GET_SERVER_ONLY_SCRIPT_CONFIG). Identical\r\n// envelope to GET_FULL_SCRIPT_CONFIG — only the inner key differs (`serverOnly`\r\n// instead of `config`). `serverOnly` is the x-serverOnly subtree in the SAME\r\n// nested shape/paths as `config` (a deep object, NOT flat dot-paths); `{}` when\r\n// the schema declares no server-only fields.\r\nexport type ServerOnlyScriptConfigResponse<T> = {\r\n success: boolean;\r\n _error?: string;\r\n data?: { serverOnly: Partial<T>; clientVersion: number };\r\n};\r\n\r\n// Recursive deep-merge: returns a new object where `patch`'s plain-object\r\n// branches are merged INTO `base`'s, and non-object values (scalars, arrays)\r\n// from `patch` replace `base`. Used to top up the server-only sliver onto the\r\n// already-cached client-visible config WITHOUT the top-level spread-replace\r\n// `fetchScriptConfig` uses — a shallow spread would clobber sibling\r\n// client-visible keys that live under a shared parent section. By construction\r\n// the two views are disjoint (serverOnly is the set-complement of the\r\n// client-visible view), but we still deep-merge so a shared parent section keeps\r\n// both its client-visible and server-only children.\r\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\r\n}\r\n\r\nfunction deepMerge<T>(base: T, patch: Partial<T>): T {\r\n if (!isPlainObject(base) || !isPlainObject(patch)) {\r\n // patch wins for non-object values (arrays/scalars are replaced wholesale).\r\n return (patch as unknown as T) ?? base;\r\n }\r\n const out: Record<string, unknown> = { ...(base as Record<string, unknown>) };\r\n for (const key of Object.keys(patch as Record<string, unknown>)) {\r\n const patchVal = (patch as Record<string, unknown>)[key];\r\n const baseVal = out[key];\r\n out[key] = isPlainObject(baseVal) && isPlainObject(patchVal)\r\n ? deepMerge(baseVal, patchVal as Record<string, unknown>)\r\n : patchVal;\r\n }\r\n return out as T;\r\n}\r\n\r\n// Derive the set of changed TOP-LEVEL section keys from a useForm changed-set\r\n// of dotted paths (e.g. [\"stores.0.name\", \"basic.weightUnit\"] → {\"stores\",\"basic\"}).\r\n// CONSERVATIVE by design: any changed path under section X marks the WHOLE\r\n// section X as changed, so the caller sends its full current value. This is what\r\n// lets deletions inside a section (a removed array element) propagate, because\r\n// the server/client overwrite the section wholesale rather than deep-merging.\r\nfunction changedTopLevelSections(changedFields?: readonly string[]): string[] {\r\n if (!changedFields || changedFields.length === 0) return [];\r\n const sections = new Set<string>();\r\n for (const path of changedFields) {\r\n if (typeof path !== \"string\" || path.length === 0) continue;\r\n const dot = path.indexOf(\".\");\r\n sections.add(dot === -1 ? path : path.slice(0, dot));\r\n }\r\n return Array.from(sections);\r\n}\r\n\r\n// ── Singleton registry ────────────────────────────────────────────────────────\r\n\r\nexport interface ScriptConfigInstance<T = any> {\r\n store: { getState: () => T; setState: (partial: Partial<T> | ((prev: T) => T)) => void };\r\n updateConfig: (newConfig: Partial<T>, changedFields?: readonly string[]) => Promise<NuiResponse<T>>;\r\n resetConfig: () => Promise<{ success: boolean; _error?: string }>;\r\n getHistory: (params?: ScriptConfigHistoryRequest) => Promise<ScriptConfigHistoryResponse>;\r\n fetchConfig: () => Promise<T | null>;\r\n // Admin-only top-up: fetches the server-only sliver and DEEP-MERGES it onto\r\n // the already-cached client-visible store state. Returns the merged full\r\n // config, or null on failure / no-permission. The sliver is in-memory only —\r\n // it is NEVER persisted to KVP or any cache (KVP holds client-visible only).\r\n fetchServerOnly: () => Promise<T | null>;\r\n}\r\n\r\nlet _instance: ScriptConfigInstance | null = null;\r\n\r\nexport function getScriptConfigInstance<T = any>(): ScriptConfigInstance<T> {\r\n if (!_instance) throw new Error(\"[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel\");\r\n return _instance as ScriptConfigInstance<T>;\r\n}\r\n\r\nexport function createScriptConfig<T>(defaultValue: T) {\r\n const store = create<T>(() => defaultValue);\r\n let clientVersion = 0;\r\n\r\n const useScriptConfigHooks = () => {\r\n useNuiEvent<{ config?: Partial<T>; clientVersion?: number; sectionReplace?: boolean }>(\r\n \"UPDATE_SCRIPT_CONFIG\",\r\n (data) => {\r\n if (!data) return;\r\n\r\n if (typeof data.clientVersion === \"number\") {\r\n clientVersion = data.clientVersion as number;\r\n }\r\n\r\n if (data.config && typeof data.config === \"object\") {\r\n // Both apply modes use the same top-level merge-by-replace:\r\n // - sectionReplace: `config` holds only the changed sections; spreading\r\n // them over `prev` wholesale-overwrites those keys and leaves every\r\n // other (unsent) section exactly as-is. We MUST NOT full-replace here\r\n // or the unsent sections would vanish from the UI.\r\n // - fullReplace / merge / initial: `config` holds the FULL config, so\r\n // the same spread effectively replaces every key.\r\n store.setState((prev) => ({ ...prev, ...(data.config as Partial<T>) }));\r\n }\r\n }\r\n );\r\n };\r\n\r\n const fetchScriptConfig = async (): Promise<T | null> => {\r\n try {\r\n const response = await fetchNui<{\r\n success: boolean;\r\n data?: { config: T; clientVersion: number };\r\n }>(\"GET_FULL_SCRIPT_CONFIG\");\r\n\r\n if (response?.success && response.data?.config) {\r\n store.setState(() => response.data!.config as T);\r\n if (typeof response.data.clientVersion === \"number\") {\r\n clientVersion = response.data.clientVersion;\r\n }\r\n return response.data.config;\r\n }\r\n } catch { /* fallback to current store state */ }\r\n return null;\r\n };\r\n\r\n // Option B top-up. Called ONLY by the admin editor on panel open. The\r\n // baseline (client-visible config) is already in the store — pushed by Lua\r\n // via UPDATE_SCRIPT_CONFIG (handled in useScriptConfigHooks above) and cached\r\n // in KVP on the Lua side. Here we fetch ONLY the server-only sliver\r\n // (permission-gated server-side on canEditScript) and DEEP-MERGE it onto that\r\n // baseline to form the full editor view. We deliberately do NOT spread-replace\r\n // (as fetchScriptConfig does) — that would clobber sibling client-visible keys\r\n // under a shared parent section.\r\n //\r\n // SECURITY: this sliver is admin-only and in-memory only. It is NEVER written\r\n // to KVP or any cache; it is re-fetched fresh on every panel open.\r\n const fetchServerOnlyScriptConfig = async (): Promise<T | null> => {\r\n try {\r\n const response = await fetchNui<ServerOnlyScriptConfigResponse<T>>(\r\n \"GET_SERVER_ONLY_SCRIPT_CONFIG\"\r\n );\r\n\r\n if (response?.success && response.data) {\r\n // clientVersion here matches the value already in the store from the\r\n // Lua push; keep it in sync (safe no-op if identical).\r\n if (typeof response.data.clientVersion === \"number\") {\r\n clientVersion = response.data.clientVersion;\r\n }\r\n const sliver = response.data.serverOnly;\r\n // An empty sliver ({} — schema declares no server-only paths) is a valid\r\n // no-op merge: deepMerge returns the baseline unchanged.\r\n const merged = deepMerge(store.getState() as T, (sliver ?? {}) as Partial<T>);\r\n store.setState(() => merged as T);\r\n return merged;\r\n }\r\n } catch { /* fall back to the client-visible baseline already in the store */ }\r\n return null;\r\n };\r\n\r\n // `changedFields` is the useForm tracked changed-set (dotted paths). When\r\n // supplied AND it resolves to ≥1 top-level section, we send a SECTION-DELTA:\r\n // only the changed sections, each as its WHOLE current value, with\r\n // sectionReplace:true. The server/client then overwrite those keys wholesale\r\n // (which is what lets deletions inside a section propagate). When omitted or\r\n // empty, we fall back to the legacy full-send (server defaults to\r\n // fullReplace=true when no `sectionReplace` flag is present).\r\n const updateScriptConfig = async (\r\n newConfig: Partial<T>,\r\n changedFields?: readonly string[]\r\n ): Promise<NuiResponse<T>> => {\r\n store.setState((prev) => ({ ...prev, ...newConfig }));\r\n\r\n const sections = changedTopLevelSections(changedFields);\r\n\r\n let payload: { data: Partial<T>; expectedVersion: number; sectionReplace?: boolean };\r\n if (sections.length > 0) {\r\n // Section-delta: flat object keyed by top-level section name → whole\r\n // current section value (pulled from the just-applied store state so it\r\n // reflects the user's edits, including deletions).\r\n const current = store.getState() as Record<string, unknown>;\r\n const delta: Record<string, unknown> = {};\r\n for (const key of sections) delta[key] = current[key];\r\n payload = { data: delta as Partial<T>, expectedVersion: clientVersion, sectionReplace: true };\r\n } else {\r\n // No changed-set provided (or nothing resolved) but the user explicitly\r\n // saved → legacy full-send. No sectionReplace flag ⇒ server fullReplace.\r\n payload = { data: newConfig, expectedVersion: clientVersion };\r\n }\r\n\r\n const response = await fetchNui<NuiResponse<T>>(\"UPDATE_SCRIPT_CONFIG\", payload);\r\n\r\n if (response?.meta?.client_version != null) {\r\n clientVersion = response.meta.client_version as number;\r\n }\r\n\r\n if (response?.success === false && response?.meta?.latestData) {\r\n store.setState((prev) => ({ ...prev, ...(response.meta!.latestData as Partial<T>) }));\r\n }\r\n\r\n return response;\r\n };\r\n\r\n const getScriptConfigHistory = async (\r\n params: ScriptConfigHistoryRequest = {}\r\n ): Promise<ScriptConfigHistoryResponse> => {\r\n return fetchNui<ScriptConfigHistoryResponse>('GET_SCRIPT_CONFIG_HISTORY', params);\r\n };\r\n\r\n const resetConfig = async (): Promise<{ success: boolean; _error?: string }> => {\r\n const response = await fetchNui<{ success: boolean; _error?: string }>('RESET_SCRIPT_CONFIG');\r\n if (response?.success) {\r\n // Option B: after a reset the server restores defaults and broadcasts a\r\n // fresh client-visible UPDATE_SCRIPT_CONFIG push (handled in\r\n // useScriptConfigHooks → store baseline). We only need to re-merge the\r\n // server-only sliver onto that refreshed baseline to rebuild the full\r\n // editor view — no GET_FULL_SCRIPT_CONFIG round-trip. fetchServerOnly\r\n // merges onto whatever client-visible baseline is currently in the store,\r\n // so it's safe regardless of broadcast/callback ordering.\r\n await fetchServerOnlyScriptConfig();\r\n }\r\n return response;\r\n };\r\n\r\n _instance = {\r\n store,\r\n updateConfig: updateScriptConfig,\r\n resetConfig,\r\n getHistory: getScriptConfigHistory,\r\n fetchConfig: fetchScriptConfig,\r\n fetchServerOnly: fetchServerOnlyScriptConfig,\r\n };\r\n\r\n return {store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig, fetchServerOnlyScriptConfig}\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useNuiEvent.ts","../src/hooks/useScriptConfig.ts"],"names":["useRef","noop","useEffect","create","fetchNui"],"mappings":"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","file":"chunk-6CNT35D6.cjs","sourcesContent":["import { MutableRefObject, useEffect, useRef } from \"react\";\r\nimport { noop } from \"../utils/misc\";\r\n\r\nexport interface NuiMessageData<T = unknown> {\r\n action: string;\r\n data: T;\r\n}\r\n\r\nexport type NuiHandlerSignature<T> = ( data: T) => void;\r\n\r\n/**\r\n * A hook that manage events listeners for receiving data from the client scripts\r\n * @param action The specific `action` that should be listened for.\r\n * @param handler The callback function that will handle data relayed by this hook\r\n *\r\n * @example\r\n * useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {\r\n * // whatever logic you want\r\n * })\r\n *\r\n **/\r\n\r\nexport const useNuiEvent = <T = unknown>(\r\n action: string,\r\n handler: ( data: T) => void,\r\n) => {\r\n const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef(noop);\r\n\r\n // Make sure we handle for a reactive handler\r\n useEffect(() => {\r\n savedHandler.current = handler;\r\n }, [handler]);\r\n\r\n useEffect(() => {\r\n const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {\r\n const { action: eventAction, data } = event.data;\r\n\r\n if (savedHandler.current) {\r\n if (eventAction === action) {\r\n savedHandler.current( data);\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener(\"message\", eventListener);\r\n // Remove Event Listener on component dirkup\r\n return () => window.removeEventListener(\"message\", eventListener);\r\n }, [action]);\r\n};\r\n","import { fetchNui } from \"@/utils\";\r\nimport { useNuiEvent } from \"@/hooks/useNuiEvent\";\r\nimport { create } from \"zustand\";\r\n\r\ntype ScriptConfigUpdateMeta<T> = {\r\n client_version?: number;\r\n latestVersion?: number;\r\n latestData?: Partial<T>;\r\n changed_paths?: Array<{ path: string; old: unknown; new: unknown }>;\r\n lastEditor?: { source?: number; name?: string; identifier?: string };\r\n};\r\n\r\nexport type ScriptConfigHistoryChange = {\r\n path: string;\r\n old: unknown;\r\n new: unknown;\r\n};\r\n\r\nexport type ScriptConfigHistoryEntry = {\r\n at_unix: number;\r\n at_utc: string;\r\n script: string;\r\n admin?: { source?: number; name?: string; identifier?: string };\r\n expected_version?: number;\r\n applied_version?: number;\r\n changes: ScriptConfigHistoryChange[];\r\n};\r\n\r\nexport type ScriptConfigHistoryRequest = {\r\n offset?: number;\r\n limit?: number;\r\n query?: string;\r\n path?: string;\r\n admin?: string;\r\n fromUnix?: number;\r\n toUnix?: number;\r\n};\r\n\r\nexport type ScriptConfigHistoryResponse = {\r\n success: boolean;\r\n _error?: string;\r\n data?: {\r\n items: ScriptConfigHistoryEntry[];\r\n total: number;\r\n limit: number;\r\n offset: number;\r\n nextOffset?: number;\r\n };\r\n};\r\n\r\ntype NuiResponse<T> = {\r\n success: boolean;\r\n message?: string;\r\n _error?: string;\r\n meta?: ScriptConfigUpdateMeta<T>;\r\n};\r\n\r\n// Server-only \"sliver\" response (GET_SERVER_ONLY_SCRIPT_CONFIG). Identical\r\n// envelope to GET_FULL_SCRIPT_CONFIG — only the inner key differs (`serverOnly`\r\n// instead of `config`). `serverOnly` is the x-serverOnly subtree in the SAME\r\n// nested shape/paths as `config` (a deep object, NOT flat dot-paths); `{}` when\r\n// the schema declares no server-only fields.\r\nexport type ServerOnlyScriptConfigResponse<T> = {\r\n success: boolean;\r\n _error?: string;\r\n data?: { serverOnly: Partial<T>; clientVersion: number };\r\n};\r\n\r\n// Recursive deep-merge: returns a new object where `patch`'s plain-object\r\n// branches are merged INTO `base`'s, and non-object values (scalars, arrays)\r\n// from `patch` replace `base`. Used to top up the server-only sliver onto the\r\n// already-cached client-visible config WITHOUT the top-level spread-replace\r\n// `fetchScriptConfig` uses — a shallow spread would clobber sibling\r\n// client-visible keys that live under a shared parent section. By construction\r\n// the two views are disjoint (serverOnly is the set-complement of the\r\n// client-visible view), but we still deep-merge so a shared parent section keeps\r\n// both its client-visible and server-only children.\r\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\r\n}\r\n\r\nfunction deepMerge<T>(base: T, patch: Partial<T>): T {\r\n if (!isPlainObject(base) || !isPlainObject(patch)) {\r\n // patch wins for non-object values (arrays/scalars are replaced wholesale).\r\n return (patch as unknown as T) ?? base;\r\n }\r\n const out: Record<string, unknown> = { ...(base as Record<string, unknown>) };\r\n for (const key of Object.keys(patch as Record<string, unknown>)) {\r\n const patchVal = (patch as Record<string, unknown>)[key];\r\n const baseVal = out[key];\r\n out[key] = isPlainObject(baseVal) && isPlainObject(patchVal)\r\n ? deepMerge(baseVal, patchVal as Record<string, unknown>)\r\n : patchVal;\r\n }\r\n return out as T;\r\n}\r\n\r\n// Derive the set of changed TOP-LEVEL section keys from a useForm changed-set\r\n// of dotted paths (e.g. [\"stores.0.name\", \"basic.weightUnit\"] → {\"stores\",\"basic\"}).\r\n// CONSERVATIVE by design: any changed path under section X marks the WHOLE\r\n// section X as changed, so the caller sends its full current value. This is what\r\n// lets deletions inside a section (a removed array element) propagate, because\r\n// the server/client overwrite the section wholesale rather than deep-merging.\r\nfunction changedTopLevelSections(changedFields?: readonly string[]): string[] {\r\n if (!changedFields || changedFields.length === 0) return [];\r\n const sections = new Set<string>();\r\n for (const path of changedFields) {\r\n if (typeof path !== \"string\" || path.length === 0) continue;\r\n const dot = path.indexOf(\".\");\r\n sections.add(dot === -1 ? path : path.slice(0, dot));\r\n }\r\n return Array.from(sections);\r\n}\r\n\r\n// ── Singleton registry ────────────────────────────────────────────────────────\r\n\r\nexport interface ScriptConfigInstance<T = any> {\r\n store: { getState: () => T; setState: (partial: Partial<T> | ((prev: T) => T)) => void };\r\n updateConfig: (newConfig: Partial<T>, changedFields?: readonly string[]) => Promise<NuiResponse<T>>;\r\n resetConfig: () => Promise<{ success: boolean; _error?: string }>;\r\n getHistory: (params?: ScriptConfigHistoryRequest) => Promise<ScriptConfigHistoryResponse>;\r\n fetchConfig: () => Promise<T | null>;\r\n // Admin-only top-up: fetches the server-only sliver and DEEP-MERGES it onto\r\n // the already-cached client-visible store state. Returns the merged full\r\n // config, or null on failure / no-permission. The sliver is in-memory only —\r\n // it is NEVER persisted to KVP or any cache (KVP holds client-visible only).\r\n fetchServerOnly: () => Promise<T | null>;\r\n}\r\n\r\nlet _instance: ScriptConfigInstance | null = null;\r\n\r\nexport function getScriptConfigInstance<T = any>(): ScriptConfigInstance<T> {\r\n if (!_instance) throw new Error(\"[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel\");\r\n return _instance as ScriptConfigInstance<T>;\r\n}\r\n\r\nexport function createScriptConfig<T>(defaultValue: T) {\r\n const store = create<T>(() => defaultValue);\r\n let clientVersion = 0;\r\n\r\n const useScriptConfigHooks = () => {\r\n useNuiEvent<{ config?: Partial<T>; clientVersion?: number; sectionReplace?: boolean }>(\r\n \"UPDATE_SCRIPT_CONFIG\",\r\n (data) => {\r\n if (!data) return;\r\n\r\n if (typeof data.clientVersion === \"number\") {\r\n clientVersion = data.clientVersion as number;\r\n }\r\n\r\n if (data.config && typeof data.config === \"object\") {\r\n // Both apply modes use the same top-level merge-by-replace:\r\n // - sectionReplace: `config` holds only the changed sections; spreading\r\n // them over `prev` wholesale-overwrites those keys and leaves every\r\n // other (unsent) section exactly as-is. We MUST NOT full-replace here\r\n // or the unsent sections would vanish from the UI.\r\n // - fullReplace / merge / initial: `config` holds the FULL config, so\r\n // the same spread effectively replaces every key.\r\n store.setState((prev) => ({ ...prev, ...(data.config as Partial<T>) }));\r\n }\r\n }\r\n );\r\n };\r\n\r\n const fetchScriptConfig = async (): Promise<T | null> => {\r\n try {\r\n const response = await fetchNui<{\r\n success: boolean;\r\n data?: { config: T; clientVersion: number };\r\n }>(\"GET_FULL_SCRIPT_CONFIG\");\r\n\r\n if (response?.success && response.data?.config) {\r\n store.setState(() => response.data!.config as T);\r\n if (typeof response.data.clientVersion === \"number\") {\r\n clientVersion = response.data.clientVersion;\r\n }\r\n return response.data.config;\r\n }\r\n } catch { /* fallback to current store state */ }\r\n return null;\r\n };\r\n\r\n // Option B top-up. Called ONLY by the admin editor on panel open. The\r\n // baseline (client-visible config) is already in the store — pushed by Lua\r\n // via UPDATE_SCRIPT_CONFIG (handled in useScriptConfigHooks above) and cached\r\n // in KVP on the Lua side. Here we fetch ONLY the server-only sliver\r\n // (permission-gated server-side on canEditScript) and DEEP-MERGE it onto that\r\n // baseline to form the full editor view. We deliberately do NOT spread-replace\r\n // (as fetchScriptConfig does) — that would clobber sibling client-visible keys\r\n // under a shared parent section.\r\n //\r\n // SECURITY: this sliver is admin-only and in-memory only. It is NEVER written\r\n // to KVP or any cache; it is re-fetched fresh on every panel open.\r\n const fetchServerOnlyScriptConfig = async (): Promise<T | null> => {\r\n try {\r\n const response = await fetchNui<ServerOnlyScriptConfigResponse<T>>(\r\n \"GET_SERVER_ONLY_SCRIPT_CONFIG\"\r\n );\r\n\r\n if (response?.success && response.data) {\r\n // clientVersion here matches the value already in the store from the\r\n // Lua push; keep it in sync (safe no-op if identical).\r\n if (typeof response.data.clientVersion === \"number\") {\r\n clientVersion = response.data.clientVersion;\r\n }\r\n const sliver = response.data.serverOnly;\r\n // An empty sliver ({} — schema declares no server-only paths) is a valid\r\n // no-op merge: deepMerge returns the baseline unchanged.\r\n const merged = deepMerge(store.getState() as T, (sliver ?? {}) as Partial<T>);\r\n store.setState(() => merged as T);\r\n return merged;\r\n }\r\n } catch { /* fall back to the client-visible baseline already in the store */ }\r\n return null;\r\n };\r\n\r\n // `changedFields` is the useForm tracked changed-set (dotted paths). When\r\n // supplied AND it resolves to ≥1 top-level section, we send a SECTION-DELTA:\r\n // only the changed sections, each as its WHOLE current value, with\r\n // sectionReplace:true. The server/client then overwrite those keys wholesale\r\n // (which is what lets deletions inside a section propagate). When omitted or\r\n // empty, we fall back to the legacy full-send (server defaults to\r\n // fullReplace=true when no `sectionReplace` flag is present).\r\n const updateScriptConfig = async (\r\n newConfig: Partial<T>,\r\n changedFields?: readonly string[]\r\n ): Promise<NuiResponse<T>> => {\r\n store.setState((prev) => ({ ...prev, ...newConfig }));\r\n\r\n const sections = changedTopLevelSections(changedFields);\r\n\r\n let payload: { data: Partial<T>; expectedVersion: number; sectionReplace?: boolean };\r\n if (sections.length > 0) {\r\n // Section-delta: flat object keyed by top-level section name → whole\r\n // current section value (pulled from the just-applied store state so it\r\n // reflects the user's edits, including deletions).\r\n const current = store.getState() as Record<string, unknown>;\r\n const delta: Record<string, unknown> = {};\r\n for (const key of sections) delta[key] = current[key];\r\n payload = { data: delta as Partial<T>, expectedVersion: clientVersion, sectionReplace: true };\r\n } else {\r\n // No changed-set provided (or nothing resolved) but the user explicitly\r\n // saved → legacy full-send. No sectionReplace flag ⇒ server fullReplace.\r\n payload = { data: newConfig, expectedVersion: clientVersion };\r\n }\r\n\r\n const response = await fetchNui<NuiResponse<T>>(\"UPDATE_SCRIPT_CONFIG\", payload);\r\n\r\n if (response?.meta?.client_version != null) {\r\n clientVersion = response.meta.client_version as number;\r\n }\r\n\r\n if (response?.success === false && response?.meta?.latestData) {\r\n store.setState((prev) => ({ ...prev, ...(response.meta!.latestData as Partial<T>) }));\r\n }\r\n\r\n return response;\r\n };\r\n\r\n const getScriptConfigHistory = async (\r\n params: ScriptConfigHistoryRequest = {}\r\n ): Promise<ScriptConfigHistoryResponse> => {\r\n return fetchNui<ScriptConfigHistoryResponse>('GET_SCRIPT_CONFIG_HISTORY', params);\r\n };\r\n\r\n const resetConfig = async (): Promise<{ success: boolean; _error?: string }> => {\r\n const response = await fetchNui<{ success: boolean; _error?: string }>('RESET_SCRIPT_CONFIG');\r\n if (response?.success) {\r\n // Option B: after a reset the server restores defaults and broadcasts a\r\n // fresh client-visible UPDATE_SCRIPT_CONFIG push (handled in\r\n // useScriptConfigHooks → store baseline). We only need to re-merge the\r\n // server-only sliver onto that refreshed baseline to rebuild the full\r\n // editor view — no GET_FULL_SCRIPT_CONFIG round-trip. fetchServerOnly\r\n // merges onto whatever client-visible baseline is currently in the store,\r\n // so it's safe regardless of broadcast/callback ordering.\r\n await fetchServerOnlyScriptConfig();\r\n }\r\n return response;\r\n };\r\n\r\n _instance = {\r\n store,\r\n updateConfig: updateScriptConfig,\r\n resetConfig,\r\n getHistory: getScriptConfigHistory,\r\n fetchConfig: fetchScriptConfig,\r\n fetchServerOnly: fetchServerOnlyScriptConfig,\r\n };\r\n\r\n return {store, updateScriptConfig, resetConfig, getScriptConfigHistory, useScriptConfigHooks, fetchScriptConfig, fetchServerOnlyScriptConfig}\r\n}\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchNui } from './chunk-
|
|
1
|
+
import { fetchNui } from './chunk-4QSXUENK.js';
|
|
2
2
|
import { create, createStore, useStore } from 'zustand';
|
|
3
3
|
import clickSoundUrl from './click_sound-PNCRRTM4.mp3';
|
|
4
4
|
import hoverSoundUrl from './hover_sound-NBUA222C.mp3';
|
|
@@ -427,5 +427,5 @@ function usePlayers(opts = {}) {
|
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
export { FormProvider, clearAdminState, createFormStore, useAdminState, useAudio, useForm, useFormActions, useFormError, useFormErrors, useFormField, useFormFields, usePlayers };
|
|
430
|
-
//# sourceMappingURL=chunk-
|
|
431
|
-
//# sourceMappingURL=chunk-
|
|
430
|
+
//# sourceMappingURL=chunk-ABBZHNLP.js.map
|
|
431
|
+
//# sourceMappingURL=chunk-ABBZHNLP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAudio/store.ts","../src/hooks/useForm.tsx","../src/hooks/useAdminState.ts","../src/hooks/usePlayers.ts"],"names":["store"],"mappings":"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","file":"chunk-KGLO3ZAS.js","sourcesContent":["import { create } from 'zustand';\r\n\r\n// Import sounds as URLs (TypeScript-safe)\r\nimport clickSoundUrl from './click_sound.mp3';\r\nimport hoverSoundUrl from './hover_sound.mp3';\r\n\r\n// Define a type for the store state and actions\r\ntype AudioPlayerStore = {\r\n play: (sound: string) => void;\r\n stop: (sound: string) => void;\r\n};\r\n\r\n// Create the store using Zustand\r\nexport const useAudio = create<AudioPlayerStore>(() => {\r\n const audioRefs: Record<string, HTMLAudioElement> = {};\r\n\r\n // Predefined sounds\r\n const sounds: Record<string, string> = {\r\n click: clickSoundUrl,\r\n hover: hoverSoundUrl,\r\n };\r\n\r\n // Initialize audio elements for each sound\r\n for (const [key, src] of Object.entries(sounds)) {\r\n audioRefs[key] = new Audio(src);\r\n }\r\n\r\n return {\r\n play: (sound) => {\r\n const audio = audioRefs[sound];\r\n if (!audio) return console.warn(`Sound '${sound}' not found.`);\r\n audio.currentTime = 0;\r\n audio.volume = 0.1;\r\n audio.play();\r\n },\r\n stop: (sound) => {\r\n const audio = audioRefs[sound];\r\n if (!audio) return console.warn(`Sound '${sound}' not found.`);\r\n audio.pause();\r\n audio.currentTime = 0;\r\n },\r\n };\r\n});\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\n\r\nimport React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { createStore, StoreApi, useStore } from \"zustand\";\r\n\r\n/* ======================================================\r\n Type Utilities\r\n====================================================== */\r\n\r\ntype PathValue<T, P extends string> = P extends keyof T\r\n ? T[P]\r\n : P extends `${infer K}.${infer R}`\r\n ? K extends keyof T\r\n ? PathValue<T[K], R>\r\n : never\r\n : never;\r\n\r\ntype Paths<T> = T extends object\r\n ? {\r\n [K in keyof T]: K extends string\r\n ? T[K] extends object\r\n ? T[K] extends any[] | Date | Function\r\n ? K\r\n : K | `${K}.${Paths<T[K]>}`\r\n : K\r\n : never;\r\n }[keyof T]\r\n : never;\r\n\r\ntype LoosePaths<T> = Paths<T> | (string & {});\r\n\r\n/* ======================================================\r\n Utilities\r\n====================================================== */\r\n\r\nfunction getNested(obj: any, path: string): any {\r\n return path.split(\".\").reduce((acc, key) => (acc ? acc[key] : undefined), obj);\r\n}\r\n\r\nfunction setNested(obj: any, path: string, value: any): any {\r\n const keys = path.split(\".\");\r\n const root = Array.isArray(obj) ? [...obj] : { ...obj };\r\n let current = root;\r\n\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n const nextKey = keys[i + 1];\r\n const isIndex = !isNaN(Number(nextKey));\r\n\r\n const existing = current[key];\r\n\r\n current[key] =\r\n existing != null\r\n ? Array.isArray(existing)\r\n ? [...existing]\r\n : { ...existing }\r\n : isIndex\r\n ? []\r\n : {};\r\n\r\n current = current[key];\r\n }\r\n\r\n current[keys[keys.length - 1]] = value;\r\n return root;\r\n}\r\n\r\nfunction deleteNested(obj: any, path: string): any {\r\n const keys = path.split(\".\");\r\n const newObj = { ...obj };\r\n let current = newObj;\r\n\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n if (!current[key]) return obj;\r\n current[key] = { ...current[key] };\r\n current = current[key];\r\n }\r\n\r\n delete current[keys[keys.length - 1]];\r\n return newObj;\r\n}\r\n\r\nfunction isPlainObject(value: any): value is Record<string, any> {\r\n return (\r\n value !== null &&\r\n typeof value === \"object\" &&\r\n !Array.isArray(value) &&\r\n !(value instanceof Date)\r\n );\r\n}\r\n\r\nfunction collectChangedPaths(values: any, initial: any, prefix = \"\"): string[] {\r\n if (Object.is(values, initial)) return [];\r\n\r\n const valuesIsObj = isPlainObject(values);\r\n const initialIsObj = isPlainObject(initial);\r\n const valuesIsArr = Array.isArray(values);\r\n const initialIsArr = Array.isArray(initial);\r\n\r\n if (valuesIsArr || initialIsArr) {\r\n const maxLen = Math.max(values?.length ?? 0, initial?.length ?? 0);\r\n const fields: string[] = [];\r\n for (let i = 0; i < maxLen; i++) {\r\n const nextPrefix = prefix ? `${prefix}.${i}` : `${i}`;\r\n fields.push(...collectChangedPaths(values?.[i], initial?.[i], nextPrefix));\r\n }\r\n return fields;\r\n }\r\n\r\n if (valuesIsObj || initialIsObj) {\r\n const keys = new Set([\r\n ...Object.keys(values ?? {}),\r\n ...Object.keys(initial ?? {}),\r\n ]);\r\n const fields: string[] = [];\r\n for (const key of keys) {\r\n const nextPrefix = prefix ? `${prefix}.${key}` : key;\r\n fields.push(...collectChangedPaths(values?.[key], initial?.[key], nextPrefix));\r\n }\r\n return fields;\r\n }\r\n\r\n return prefix ? [prefix] : [];\r\n}\r\n\r\nfunction computeChangedState<T>(\r\n values: Partial<T>,\r\n initialVals: Partial<T>\r\n): { fields: string[]; partial: Partial<T> } {\r\n const fields = collectChangedPaths(values, initialVals);\r\n let partial: Partial<T> = {};\r\n\r\n for (const path of fields) {\r\n partial = setNested(partial, path, getNested(values, path));\r\n }\r\n\r\n return { fields, partial };\r\n}\r\n\r\nfunction flattenRules(\r\n rules: any,\r\n prefix = \"\"\r\n): Record<string, ValidatorFn> {\r\n const result: Record<string, ValidatorFn> = {};\r\n\r\n for (const key in rules) {\r\n const fullPath = prefix ? `${prefix}.${key}` : key;\r\n const val = rules[key];\r\n\r\n if (typeof val === \"function\") result[fullPath] = val;\r\n else if (typeof val === \"object\")\r\n Object.assign(result, flattenRules(val, fullPath));\r\n }\r\n\r\n return result;\r\n}\r\n\r\nasync function runRule(\r\n rule: ValidatorFn,\r\n value: any,\r\n values: any\r\n): Promise<string | null> {\r\n const result = rule(value, values);\r\n return result instanceof Promise ? await result : result;\r\n}\r\n\r\n/* ======================================================\r\n Types\r\n====================================================== */\r\n\r\nexport type ValidatorFn<T = any> =\r\n | ((value: any, values: Partial<T>) => string | null)\r\n | ((value: any, values: Partial<T>) => Promise<string | null>);\r\n\r\nexport type ValidationRules<T> = {\r\n [K in keyof T]?: T[K] extends object\r\n ? ValidationRules<T[K]>\r\n : ValidatorFn<T>;\r\n};\r\n\r\nexport type FormState<T> = {\r\n values: Partial<T>;\r\n initialValues: Partial<T>;\r\n errors: Record<string, string>;\r\n\r\n partialChanged: Partial<T>;\r\n\r\n getInputProps: (path: string) => {\r\n value: any;\r\n error?: string;\r\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\r\n };\r\n\r\n setValue: (path: string, value: any, options?: { validate?: boolean }) => void;\r\n setInitialValues: (newInitialValues: Partial<T>) => void;\r\n\r\n setError: (path: string, message: string) => void;\r\n clearError: (path: string) => void;\r\n\r\n validate: () => Promise<boolean>;\r\n validateField: (path: string) => Promise<boolean>;\r\n\r\n reset: () => void;\r\n reinitialize: (newValues: Partial<T>) => void;\r\n\r\n back: () => void;\r\n forward: () => void;\r\n canBack: boolean;\r\n canForward: boolean;\r\n\r\n changedFields: string[];\r\n changedCount: number;\r\n resetChangeCount: () => void;\r\n\r\n onSubmit?: (form: FormState<T>) => void;\r\n submit: () => Promise<void>;\r\n};\r\n\r\n/* ======================================================\r\n Store\r\n====================================================== */\r\n\r\nexport function createFormStore<T>(\r\n initialValues: Partial<T>,\r\n validationRules?: ValidationRules<T>,\r\n onSubmit?: (form: FormState<T>) => void\r\n) {\r\n const flatRules = validationRules ? flattenRules(validationRules) : {};\r\n\r\n const history: Partial<T>[] = [];\r\n const future: Partial<T>[] = [];\r\n const changed = new Set<string>();\r\n\r\n return createStore<FormState<T>>((set, get) => ({\r\n initialValues,\r\n values: initialValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n onSubmit,\r\n\r\n submit: async () => {\r\n const state = get();\r\n const isValid = await state.validate();\r\n if (isValid && state.onSubmit) {\r\n state.onSubmit(get());\r\n }\r\n },\r\n\r\n getInputProps: (path: string) => {\r\n return {\r\n value: getNested(get().values, path) ?? \"\",\r\n error: get().errors[path],\r\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\r\n get().setValue(path, e.target.value, { validate: true });\r\n },\r\n };\r\n },\r\n\r\n resetChangeCount: () => {\r\n changed.clear();\r\n set({ changedFields: [], changedCount: 0, partialChanged: {} });\r\n },\r\n\r\n setInitialValues: (newInitialValues) =>\r\n set({ initialValues: newInitialValues }),\r\n\r\n setValue: (path, value, options) => {\r\n const state = get();\r\n const currentValues = state.values;\r\n const newValues = setNested(currentValues, path, value);\r\n\r\n const oldValue = getNested(state.initialValues, path);\r\n const hasChanged = value !== oldValue;\r\n\r\n history.push(currentValues);\r\n future.length = 0;\r\n\r\n let newPartial = state.partialChanged;\r\n\r\n if (hasChanged) {\r\n changed.add(path);\r\n newPartial = setNested(newPartial, path, value);\r\n } else {\r\n changed.delete(path);\r\n newPartial = deleteNested(newPartial, path);\r\n }\r\n\r\n set({\r\n values: newValues,\r\n partialChanged: newPartial,\r\n canBack: history.length > 0,\r\n canForward: false,\r\n changedFields: Array.from(changed),\r\n changedCount: changed.size,\r\n });\r\n\r\n if (!options?.validate) return;\r\n\r\n const rule = flatRules[path];\r\n if (!rule) return;\r\n\r\n Promise.resolve(runRule(rule, value, newValues)).then((error) => {\r\n if (error)\r\n set((s) => ({ errors: setNested(s.errors, path, error) }));\r\n else\r\n set((s) => ({ errors: deleteNested(s.errors, path) }));\r\n });\r\n },\r\n\r\n setError: (path, message) =>\r\n set((s) => ({ errors: setNested(s.errors, path, message) })),\r\n\r\n clearError: (path) =>\r\n set((s) => ({ errors: deleteNested(s.errors, path) })),\r\n\r\n validateField: async (path) => {\r\n const state = get();\r\n const rule = flatRules[path];\r\n if (!rule) return true;\r\n\r\n const value = getNested(state.values, path);\r\n const error = await runRule(rule, value, state.values);\r\n\r\n if (error) {\r\n set((s) => ({ errors: setNested(s.errors, path, error) }));\r\n return false;\r\n }\r\n\r\n set((s) => ({ errors: deleteNested(s.errors, path) }));\r\n return true;\r\n },\r\n\r\n validate: async () => {\r\n const state = get();\r\n let isValid = true;\r\n let newErrors: Record<string, string> = {};\r\n\r\n for (const path in flatRules) {\r\n const rule = flatRules[path];\r\n const value = getNested(state.values, path);\r\n const error = await runRule(rule, value, state.values);\r\n\r\n if (error) {\r\n isValid = false;\r\n newErrors = setNested(newErrors, path, error);\r\n }\r\n }\r\n\r\n set({ errors: newErrors });\r\n return isValid;\r\n },\r\n\r\n reset: () => {\r\n history.length = 0;\r\n future.length = 0;\r\n changed.clear();\r\n\r\n set({\r\n values: get().initialValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n });\r\n },\r\n\r\n reinitialize: (newValues) => {\r\n history.length = 0;\r\n future.length = 0;\r\n changed.clear();\r\n\r\n set({\r\n values: newValues,\r\n initialValues: newValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n });\r\n },\r\n\r\n back: () => {\r\n if (!history.length) return;\r\n\r\n const prev = history.pop()!;\r\n future.push(get().values);\r\n\r\n const { fields, partial } = computeChangedState(prev, get().initialValues);\r\n\r\n set({\r\n values: prev,\r\n partialChanged: partial,\r\n canBack: history.length > 0,\r\n canForward: true,\r\n changedFields: fields,\r\n changedCount: fields.length,\r\n });\r\n },\r\n\r\n forward: () => {\r\n if (!future.length) return;\r\n\r\n const next = future.pop()!;\r\n history.push(get().values);\r\n\r\n const { fields, partial } = computeChangedState(next, get().initialValues);\r\n\r\n set({\r\n values: next,\r\n partialChanged: partial,\r\n canBack: true,\r\n canForward: future.length > 0,\r\n changedFields: fields,\r\n changedCount: fields.length,\r\n });\r\n },\r\n }));\r\n}\r\n\r\n/* ======================================================\r\n Context + Hook\r\n====================================================== */\r\n\r\nconst FormContext = createContext<StoreApi<FormState<any>> | null>(null);\r\n\r\nexport function FormProvider<T>({\r\n initialValues,\r\n validate,\r\n onSubmit,\r\n children,\r\n}: {\r\n initialValues: Partial<T>;\r\n validate?: ValidationRules<T>;\r\n onSubmit?: (form: FormState<T>) => void;\r\n children: React.ReactNode;\r\n}) {\r\n const storeRef = useRef(\r\n createFormStore<T>(initialValues, validate, onSubmit)\r\n );\r\n\r\n return (\r\n <FormContext.Provider value={storeRef.current}>\r\n {children}\r\n </FormContext.Provider>\r\n );\r\n}\r\n\r\nexport function useForm<T>() {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useForm must be used inside <FormProvider>\");\r\n }\r\n const state = useStore(store) as FormState<T>;\r\n\r\n const changedFields = useMemo(() => {\r\n return collectChangedPaths(state.values, state.initialValues);\r\n }, [state.values, state.initialValues]);\r\n\r\n return { ...state, changedFields, changedCount: changedFields.length } as FormState<T>;\r\n}\r\n\r\nexport function useFormField<T, P extends LoosePaths<T> = LoosePaths<T>>(\r\n path: P\r\n): P extends Paths<T> ? PathValue<T, P> | undefined : any {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormField must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => getNested(s.values, path));\r\n}\r\n\r\nexport function useFormFields<T, P extends LoosePaths<T> = LoosePaths<T>>(\r\n ...paths: P[]\r\n): { [K in P]: K extends Paths<T> ? PathValue<T, K> | undefined : any } {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormFields must be used inside <FormProvider>\");\r\n }\r\n \r\n return useStore(store, (s) => {\r\n const result = {} as { [K in P]: K extends Paths<T> ? PathValue<T, K> | undefined : any };\r\n for (const path of paths) {\r\n result[path] = getNested(s.values, path);\r\n }\r\n return result;\r\n });\r\n}\r\n\r\nexport function useFormError(path: string) {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormError must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => s.errors[path]);\r\n}\r\n\r\nexport function useFormErrors(...paths: string[]) {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormErrors must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => {\r\n const result: Record<string, string | undefined> = {};\r\n for (const path of paths) {\r\n result[path] = s.errors[path];\r\n }\r\n return result;\r\n });\r\n}\r\n\r\nexport function useFormActions<T>() {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormActions must be used inside <FormProvider>\");\r\n }\r\n return store.getState() as Pick<\r\n FormState<T>,\r\n | \"setValue\"\r\n | \"setError\"\r\n | \"clearError\"\r\n | \"validate\"\r\n | \"validateField\"\r\n | \"reset\"\r\n | \"reinitialize\"\r\n | \"back\"\r\n | \"forward\"\r\n | \"canBack\"\r\n | \"canForward\"\r\n | \"submit\"\r\n >;\r\n}","// Drop-in replacement for React's useState that survives unmount/remount.\r\n//\r\n// Why: ConfigPanel returns null when closed, which tears down the entire\r\n// React tree under it. Local component state (active tab, current edit\r\n// target, search text, view-mode toggles, etc.) is lost. Admins who close\r\n// and reopen the panel land back at the default state every time.\r\n//\r\n// useAdminState writes to a module-scope store keyed by an arbitrary\r\n// string. The store survives close/reopen because the NUI iframe stays\r\n// alive across SetNuiFocus toggles — only React unmounts.\r\n//\r\n// Reset scope: keys live for the lifetime of the iframe (= until the\r\n// resource restarts or the player reloads the NUI). That's exactly what\r\n// admins want — \"remember where I was in this session, but don't carry\r\n// over after a restart\".\r\n//\r\n// Usage:\r\n// const [activeTab, setActiveTab] = useAdminState(\"labs:viewMode\", \"list\");\r\n//\r\n// Key collisions: state is scoped per dirk-cfx-react module-scope, which\r\n// is per-iframe — so two different consumer resources can both use the key\r\n// \"viewMode\" without colliding. Within a single consumer, keep keys unique\r\n// (prefix with section name: \"labs:viewMode\", \"shells:editingId\" etc).\r\n\r\nimport { useEffect, useState } from \"react\";\r\n\r\ntype Listener = () => void;\r\n\r\nconst store: Map<string, unknown> = new Map();\r\nconst listeners: Map<string, Set<Listener>> = new Map();\r\n\r\nfunction notify(key: string) {\r\n const ls = listeners.get(key);\r\n if (!ls) return;\r\n ls.forEach((fn) => fn());\r\n}\r\n\r\n/**\r\n * Like `useState` but the value persists in a module-scope store across\r\n * unmount/remount cycles. Identical API otherwise.\r\n *\r\n * @param key Stable string identifying this slot. Must be unique per\r\n * intended slot within the consumer's iframe.\r\n * @param initial Initial value if no value has been stored under `key` yet.\r\n */\r\nexport function useAdminState<T>(key: string, initial: T): [T, (v: T | ((prev: T) => T)) => void] {\r\n // useState here is just for triggering re-renders on cross-component\r\n // updates. The real value lives in the module-scope `store`.\r\n const [, setTick] = useState(0);\r\n\r\n useEffect(() => {\r\n const set = listeners.get(key) ?? new Set<Listener>();\r\n listeners.set(key, set);\r\n const handler = () => setTick((t) => t + 1);\r\n set.add(handler);\r\n return () => {\r\n set.delete(handler);\r\n if (set.size === 0) listeners.delete(key);\r\n };\r\n }, [key]);\r\n\r\n const value = (store.has(key) ? store.get(key) : initial) as T;\r\n\r\n const setValue = (v: T | ((prev: T) => T)) => {\r\n const next = typeof v === \"function\" ? (v as (p: T) => T)(value) : v;\r\n store.set(key, next);\r\n notify(key);\r\n };\r\n\r\n return [value, setValue];\r\n}\r\n\r\n/** Imperative clear — useful for \"reset everything\" admin actions. */\r\nexport function clearAdminState(key?: string) {\r\n if (key) {\r\n store.delete(key);\r\n notify(key);\r\n } else {\r\n const keys = Array.from(store.keys());\r\n store.clear();\r\n keys.forEach(notify);\r\n }\r\n}\r\n","// Fetches normalised player lists from dirk_lib's admin tool subsystem.\r\n//\r\n// Two modes, controlled by `includeOffline`:\r\n// • false (default) → online-only. Fast, cheap, refreshed every ~5s.\r\n// • true → DB-backed search across online + offline players.\r\n// Filtered server-side by `search`. Cap of 50.\r\n//\r\n// Caching: react-query handles dedupe + stale-while-revalidate, keyed on\r\n// (mode, search, limit). Multiple <PlayerSelect>s on screen share one\r\n// fetch. Typing \"j\" → \"jo\" → \"joh\" creates separate cache entries — each\r\n// stays cached for 5 minutes (gcTime) so re-typing reuses instantly.\r\n//\r\n// Admin gating: lives behind the ADMIN_TOOL_QUERY perm check on the\r\n// server. Outside an open admin panel, fetches return empty arrays.\r\n\r\nimport { useQuery, keepPreviousData } from \"@tanstack/react-query\";\r\nimport { fetchNui } from \"../utils/fetchNui\";\r\n\r\nexport type Player = {\r\n /** Server id when online, null when offline. */\r\n id: number | null;\r\n /** Persistent identifier — citizenid (qb/qbx) or license (esx). */\r\n citizenId: string;\r\n /** Steam/Discord display name from GetPlayerName(src). Blank for some\r\n * framework + offline combos (ESX users table doesn't store it). */\r\n name: string;\r\n /** First name + last name from the framework's character data. */\r\n charName: string;\r\n online: boolean;\r\n};\r\n\r\nexport type UsePlayersOptions = {\r\n /** When true, hits the DB and returns offline + online matches.\r\n * When false (default), returns only currently-connected players. */\r\n includeOffline?: boolean;\r\n /** Required when includeOffline=true; ignored otherwise. Server-side\r\n * filtered (LIKE) across char name, citizenId, account name. */\r\n search?: string;\r\n /** Default 50, max 50 (capped server-side). */\r\n limit?: number;\r\n /** Override staleTime — how long the cache stays fresh before background\r\n * refetch. Default 5s for online, 30s for search. */\r\n staleTimeMs?: number;\r\n /** Set to e.g. 10000 to auto-refresh every 10s. Off by default. */\r\n refetchIntervalMs?: number;\r\n};\r\n\r\nexport type UsePlayersResult = {\r\n players: Player[];\r\n isLoading: boolean;\r\n isFetching: boolean;\r\n error: Error | null;\r\n refresh: () => void;\r\n};\r\n\r\nexport function usePlayers(opts: UsePlayersOptions = {}): UsePlayersResult {\r\n const {\r\n includeOffline = false,\r\n search = \"\",\r\n limit = 50,\r\n staleTimeMs,\r\n refetchIntervalMs,\r\n } = opts;\r\n\r\n const query = useQuery<Player[]>({\r\n queryKey: includeOffline\r\n ? [\"dirk:players\", \"search\", search.trim().toLowerCase(), limit]\r\n : [\"dirk:players\", \"online\"],\r\n queryFn: async () => {\r\n const toolId = includeOffline ? \"searchPlayers\" : \"getOnlinePlayers\";\r\n const payload = includeOffline\r\n ? { id: toolId, value: { search: search.trim(), limit } }\r\n : { id: toolId };\r\n const result = await fetchNui<Player[] | null>(\r\n \"ADMIN_TOOL_QUERY\",\r\n payload,\r\n // Browser-dev fallback. Returns a couple of mock players so the\r\n // dev shell isn't blank.\r\n includeOffline\r\n ? [\r\n { id: 1, citizenId: \"ABC12345\", name: \"Dev User\", charName: \"John Doe\", online: true },\r\n { id: null, citizenId: \"DEF67890\", name: \"\", charName: \"Jane Offline\", online: false },\r\n ]\r\n : [\r\n { id: 1, citizenId: \"ABC12345\", name: \"Dev User\", charName: \"John Doe\", online: true },\r\n ],\r\n );\r\n return Array.isArray(result) ? result : [];\r\n },\r\n staleTime: staleTimeMs ?? (includeOffline ? 30_000 : 5_000),\r\n gcTime: 5 * 60_000,\r\n refetchInterval: refetchIntervalMs ?? false,\r\n refetchOnWindowFocus: false,\r\n placeholderData: includeOffline ? keepPreviousData : undefined,\r\n });\r\n\r\n return {\r\n players: query.data ?? [],\r\n isLoading: query.isLoading,\r\n isFetching: query.isFetching,\r\n error: (query.error as Error | null) ?? null,\r\n refresh: () => query.refetch(),\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useAudio/store.ts","../src/hooks/useForm.tsx","../src/hooks/useAdminState.ts","../src/hooks/usePlayers.ts"],"names":["store"],"mappings":"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","file":"chunk-ABBZHNLP.js","sourcesContent":["import { create } from 'zustand';\r\n\r\n// Import sounds as URLs (TypeScript-safe)\r\nimport clickSoundUrl from './click_sound.mp3';\r\nimport hoverSoundUrl from './hover_sound.mp3';\r\n\r\n// Define a type for the store state and actions\r\ntype AudioPlayerStore = {\r\n play: (sound: string) => void;\r\n stop: (sound: string) => void;\r\n};\r\n\r\n// Create the store using Zustand\r\nexport const useAudio = create<AudioPlayerStore>(() => {\r\n const audioRefs: Record<string, HTMLAudioElement> = {};\r\n\r\n // Predefined sounds\r\n const sounds: Record<string, string> = {\r\n click: clickSoundUrl,\r\n hover: hoverSoundUrl,\r\n };\r\n\r\n // Initialize audio elements for each sound\r\n for (const [key, src] of Object.entries(sounds)) {\r\n audioRefs[key] = new Audio(src);\r\n }\r\n\r\n return {\r\n play: (sound) => {\r\n const audio = audioRefs[sound];\r\n if (!audio) return console.warn(`Sound '${sound}' not found.`);\r\n audio.currentTime = 0;\r\n audio.volume = 0.1;\r\n audio.play();\r\n },\r\n stop: (sound) => {\r\n const audio = audioRefs[sound];\r\n if (!audio) return console.warn(`Sound '${sound}' not found.`);\r\n audio.pause();\r\n audio.currentTime = 0;\r\n },\r\n };\r\n});\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\n\r\nimport React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { createStore, StoreApi, useStore } from \"zustand\";\r\n\r\n/* ======================================================\r\n Type Utilities\r\n====================================================== */\r\n\r\ntype PathValue<T, P extends string> = P extends keyof T\r\n ? T[P]\r\n : P extends `${infer K}.${infer R}`\r\n ? K extends keyof T\r\n ? PathValue<T[K], R>\r\n : never\r\n : never;\r\n\r\ntype Paths<T> = T extends object\r\n ? {\r\n [K in keyof T]: K extends string\r\n ? T[K] extends object\r\n ? T[K] extends any[] | Date | Function\r\n ? K\r\n : K | `${K}.${Paths<T[K]>}`\r\n : K\r\n : never;\r\n }[keyof T]\r\n : never;\r\n\r\ntype LoosePaths<T> = Paths<T> | (string & {});\r\n\r\n/* ======================================================\r\n Utilities\r\n====================================================== */\r\n\r\nfunction getNested(obj: any, path: string): any {\r\n return path.split(\".\").reduce((acc, key) => (acc ? acc[key] : undefined), obj);\r\n}\r\n\r\nfunction setNested(obj: any, path: string, value: any): any {\r\n const keys = path.split(\".\");\r\n const root = Array.isArray(obj) ? [...obj] : { ...obj };\r\n let current = root;\r\n\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n const nextKey = keys[i + 1];\r\n const isIndex = !isNaN(Number(nextKey));\r\n\r\n const existing = current[key];\r\n\r\n current[key] =\r\n existing != null\r\n ? Array.isArray(existing)\r\n ? [...existing]\r\n : { ...existing }\r\n : isIndex\r\n ? []\r\n : {};\r\n\r\n current = current[key];\r\n }\r\n\r\n current[keys[keys.length - 1]] = value;\r\n return root;\r\n}\r\n\r\nfunction deleteNested(obj: any, path: string): any {\r\n const keys = path.split(\".\");\r\n const newObj = { ...obj };\r\n let current = newObj;\r\n\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n if (!current[key]) return obj;\r\n current[key] = { ...current[key] };\r\n current = current[key];\r\n }\r\n\r\n delete current[keys[keys.length - 1]];\r\n return newObj;\r\n}\r\n\r\nfunction isPlainObject(value: any): value is Record<string, any> {\r\n return (\r\n value !== null &&\r\n typeof value === \"object\" &&\r\n !Array.isArray(value) &&\r\n !(value instanceof Date)\r\n );\r\n}\r\n\r\nfunction collectChangedPaths(values: any, initial: any, prefix = \"\"): string[] {\r\n if (Object.is(values, initial)) return [];\r\n\r\n const valuesIsObj = isPlainObject(values);\r\n const initialIsObj = isPlainObject(initial);\r\n const valuesIsArr = Array.isArray(values);\r\n const initialIsArr = Array.isArray(initial);\r\n\r\n if (valuesIsArr || initialIsArr) {\r\n const maxLen = Math.max(values?.length ?? 0, initial?.length ?? 0);\r\n const fields: string[] = [];\r\n for (let i = 0; i < maxLen; i++) {\r\n const nextPrefix = prefix ? `${prefix}.${i}` : `${i}`;\r\n fields.push(...collectChangedPaths(values?.[i], initial?.[i], nextPrefix));\r\n }\r\n return fields;\r\n }\r\n\r\n if (valuesIsObj || initialIsObj) {\r\n const keys = new Set([\r\n ...Object.keys(values ?? {}),\r\n ...Object.keys(initial ?? {}),\r\n ]);\r\n const fields: string[] = [];\r\n for (const key of keys) {\r\n const nextPrefix = prefix ? `${prefix}.${key}` : key;\r\n fields.push(...collectChangedPaths(values?.[key], initial?.[key], nextPrefix));\r\n }\r\n return fields;\r\n }\r\n\r\n return prefix ? [prefix] : [];\r\n}\r\n\r\nfunction computeChangedState<T>(\r\n values: Partial<T>,\r\n initialVals: Partial<T>\r\n): { fields: string[]; partial: Partial<T> } {\r\n const fields = collectChangedPaths(values, initialVals);\r\n let partial: Partial<T> = {};\r\n\r\n for (const path of fields) {\r\n partial = setNested(partial, path, getNested(values, path));\r\n }\r\n\r\n return { fields, partial };\r\n}\r\n\r\nfunction flattenRules(\r\n rules: any,\r\n prefix = \"\"\r\n): Record<string, ValidatorFn> {\r\n const result: Record<string, ValidatorFn> = {};\r\n\r\n for (const key in rules) {\r\n const fullPath = prefix ? `${prefix}.${key}` : key;\r\n const val = rules[key];\r\n\r\n if (typeof val === \"function\") result[fullPath] = val;\r\n else if (typeof val === \"object\")\r\n Object.assign(result, flattenRules(val, fullPath));\r\n }\r\n\r\n return result;\r\n}\r\n\r\nasync function runRule(\r\n rule: ValidatorFn,\r\n value: any,\r\n values: any\r\n): Promise<string | null> {\r\n const result = rule(value, values);\r\n return result instanceof Promise ? await result : result;\r\n}\r\n\r\n/* ======================================================\r\n Types\r\n====================================================== */\r\n\r\nexport type ValidatorFn<T = any> =\r\n | ((value: any, values: Partial<T>) => string | null)\r\n | ((value: any, values: Partial<T>) => Promise<string | null>);\r\n\r\nexport type ValidationRules<T> = {\r\n [K in keyof T]?: T[K] extends object\r\n ? ValidationRules<T[K]>\r\n : ValidatorFn<T>;\r\n};\r\n\r\nexport type FormState<T> = {\r\n values: Partial<T>;\r\n initialValues: Partial<T>;\r\n errors: Record<string, string>;\r\n\r\n partialChanged: Partial<T>;\r\n\r\n getInputProps: (path: string) => {\r\n value: any;\r\n error?: string;\r\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\r\n };\r\n\r\n setValue: (path: string, value: any, options?: { validate?: boolean }) => void;\r\n setInitialValues: (newInitialValues: Partial<T>) => void;\r\n\r\n setError: (path: string, message: string) => void;\r\n clearError: (path: string) => void;\r\n\r\n validate: () => Promise<boolean>;\r\n validateField: (path: string) => Promise<boolean>;\r\n\r\n reset: () => void;\r\n reinitialize: (newValues: Partial<T>) => void;\r\n\r\n back: () => void;\r\n forward: () => void;\r\n canBack: boolean;\r\n canForward: boolean;\r\n\r\n changedFields: string[];\r\n changedCount: number;\r\n resetChangeCount: () => void;\r\n\r\n onSubmit?: (form: FormState<T>) => void;\r\n submit: () => Promise<void>;\r\n};\r\n\r\n/* ======================================================\r\n Store\r\n====================================================== */\r\n\r\nexport function createFormStore<T>(\r\n initialValues: Partial<T>,\r\n validationRules?: ValidationRules<T>,\r\n onSubmit?: (form: FormState<T>) => void\r\n) {\r\n const flatRules = validationRules ? flattenRules(validationRules) : {};\r\n\r\n const history: Partial<T>[] = [];\r\n const future: Partial<T>[] = [];\r\n const changed = new Set<string>();\r\n\r\n return createStore<FormState<T>>((set, get) => ({\r\n initialValues,\r\n values: initialValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n onSubmit,\r\n\r\n submit: async () => {\r\n const state = get();\r\n const isValid = await state.validate();\r\n if (isValid && state.onSubmit) {\r\n state.onSubmit(get());\r\n }\r\n },\r\n\r\n getInputProps: (path: string) => {\r\n return {\r\n value: getNested(get().values, path) ?? \"\",\r\n error: get().errors[path],\r\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\r\n get().setValue(path, e.target.value, { validate: true });\r\n },\r\n };\r\n },\r\n\r\n resetChangeCount: () => {\r\n changed.clear();\r\n set({ changedFields: [], changedCount: 0, partialChanged: {} });\r\n },\r\n\r\n setInitialValues: (newInitialValues) =>\r\n set({ initialValues: newInitialValues }),\r\n\r\n setValue: (path, value, options) => {\r\n const state = get();\r\n const currentValues = state.values;\r\n const newValues = setNested(currentValues, path, value);\r\n\r\n const oldValue = getNested(state.initialValues, path);\r\n const hasChanged = value !== oldValue;\r\n\r\n history.push(currentValues);\r\n future.length = 0;\r\n\r\n let newPartial = state.partialChanged;\r\n\r\n if (hasChanged) {\r\n changed.add(path);\r\n newPartial = setNested(newPartial, path, value);\r\n } else {\r\n changed.delete(path);\r\n newPartial = deleteNested(newPartial, path);\r\n }\r\n\r\n set({\r\n values: newValues,\r\n partialChanged: newPartial,\r\n canBack: history.length > 0,\r\n canForward: false,\r\n changedFields: Array.from(changed),\r\n changedCount: changed.size,\r\n });\r\n\r\n if (!options?.validate) return;\r\n\r\n const rule = flatRules[path];\r\n if (!rule) return;\r\n\r\n Promise.resolve(runRule(rule, value, newValues)).then((error) => {\r\n if (error)\r\n set((s) => ({ errors: setNested(s.errors, path, error) }));\r\n else\r\n set((s) => ({ errors: deleteNested(s.errors, path) }));\r\n });\r\n },\r\n\r\n setError: (path, message) =>\r\n set((s) => ({ errors: setNested(s.errors, path, message) })),\r\n\r\n clearError: (path) =>\r\n set((s) => ({ errors: deleteNested(s.errors, path) })),\r\n\r\n validateField: async (path) => {\r\n const state = get();\r\n const rule = flatRules[path];\r\n if (!rule) return true;\r\n\r\n const value = getNested(state.values, path);\r\n const error = await runRule(rule, value, state.values);\r\n\r\n if (error) {\r\n set((s) => ({ errors: setNested(s.errors, path, error) }));\r\n return false;\r\n }\r\n\r\n set((s) => ({ errors: deleteNested(s.errors, path) }));\r\n return true;\r\n },\r\n\r\n validate: async () => {\r\n const state = get();\r\n let isValid = true;\r\n let newErrors: Record<string, string> = {};\r\n\r\n for (const path in flatRules) {\r\n const rule = flatRules[path];\r\n const value = getNested(state.values, path);\r\n const error = await runRule(rule, value, state.values);\r\n\r\n if (error) {\r\n isValid = false;\r\n newErrors = setNested(newErrors, path, error);\r\n }\r\n }\r\n\r\n set({ errors: newErrors });\r\n return isValid;\r\n },\r\n\r\n reset: () => {\r\n history.length = 0;\r\n future.length = 0;\r\n changed.clear();\r\n\r\n set({\r\n values: get().initialValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n });\r\n },\r\n\r\n reinitialize: (newValues) => {\r\n history.length = 0;\r\n future.length = 0;\r\n changed.clear();\r\n\r\n set({\r\n values: newValues,\r\n initialValues: newValues,\r\n errors: {},\r\n partialChanged: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n });\r\n },\r\n\r\n back: () => {\r\n if (!history.length) return;\r\n\r\n const prev = history.pop()!;\r\n future.push(get().values);\r\n\r\n const { fields, partial } = computeChangedState(prev, get().initialValues);\r\n\r\n set({\r\n values: prev,\r\n partialChanged: partial,\r\n canBack: history.length > 0,\r\n canForward: true,\r\n changedFields: fields,\r\n changedCount: fields.length,\r\n });\r\n },\r\n\r\n forward: () => {\r\n if (!future.length) return;\r\n\r\n const next = future.pop()!;\r\n history.push(get().values);\r\n\r\n const { fields, partial } = computeChangedState(next, get().initialValues);\r\n\r\n set({\r\n values: next,\r\n partialChanged: partial,\r\n canBack: true,\r\n canForward: future.length > 0,\r\n changedFields: fields,\r\n changedCount: fields.length,\r\n });\r\n },\r\n }));\r\n}\r\n\r\n/* ======================================================\r\n Context + Hook\r\n====================================================== */\r\n\r\nconst FormContext = createContext<StoreApi<FormState<any>> | null>(null);\r\n\r\nexport function FormProvider<T>({\r\n initialValues,\r\n validate,\r\n onSubmit,\r\n children,\r\n}: {\r\n initialValues: Partial<T>;\r\n validate?: ValidationRules<T>;\r\n onSubmit?: (form: FormState<T>) => void;\r\n children: React.ReactNode;\r\n}) {\r\n const storeRef = useRef(\r\n createFormStore<T>(initialValues, validate, onSubmit)\r\n );\r\n\r\n return (\r\n <FormContext.Provider value={storeRef.current}>\r\n {children}\r\n </FormContext.Provider>\r\n );\r\n}\r\n\r\nexport function useForm<T>() {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useForm must be used inside <FormProvider>\");\r\n }\r\n const state = useStore(store) as FormState<T>;\r\n\r\n const changedFields = useMemo(() => {\r\n return collectChangedPaths(state.values, state.initialValues);\r\n }, [state.values, state.initialValues]);\r\n\r\n return { ...state, changedFields, changedCount: changedFields.length } as FormState<T>;\r\n}\r\n\r\nexport function useFormField<T, P extends LoosePaths<T> = LoosePaths<T>>(\r\n path: P\r\n): P extends Paths<T> ? PathValue<T, P> | undefined : any {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormField must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => getNested(s.values, path));\r\n}\r\n\r\nexport function useFormFields<T, P extends LoosePaths<T> = LoosePaths<T>>(\r\n ...paths: P[]\r\n): { [K in P]: K extends Paths<T> ? PathValue<T, K> | undefined : any } {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormFields must be used inside <FormProvider>\");\r\n }\r\n \r\n return useStore(store, (s) => {\r\n const result = {} as { [K in P]: K extends Paths<T> ? PathValue<T, K> | undefined : any };\r\n for (const path of paths) {\r\n result[path] = getNested(s.values, path);\r\n }\r\n return result;\r\n });\r\n}\r\n\r\nexport function useFormError(path: string) {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormError must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => s.errors[path]);\r\n}\r\n\r\nexport function useFormErrors(...paths: string[]) {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormErrors must be used inside <FormProvider>\");\r\n }\r\n return useStore(store, (s) => {\r\n const result: Record<string, string | undefined> = {};\r\n for (const path of paths) {\r\n result[path] = s.errors[path];\r\n }\r\n return result;\r\n });\r\n}\r\n\r\nexport function useFormActions<T>() {\r\n const store = useContext(FormContext);\r\n if (!store) {\r\n throw new Error(\"useFormActions must be used inside <FormProvider>\");\r\n }\r\n return store.getState() as Pick<\r\n FormState<T>,\r\n | \"setValue\"\r\n | \"setError\"\r\n | \"clearError\"\r\n | \"validate\"\r\n | \"validateField\"\r\n | \"reset\"\r\n | \"reinitialize\"\r\n | \"back\"\r\n | \"forward\"\r\n | \"canBack\"\r\n | \"canForward\"\r\n | \"submit\"\r\n >;\r\n}","// Drop-in replacement for React's useState that survives unmount/remount.\r\n//\r\n// Why: ConfigPanel returns null when closed, which tears down the entire\r\n// React tree under it. Local component state (active tab, current edit\r\n// target, search text, view-mode toggles, etc.) is lost. Admins who close\r\n// and reopen the panel land back at the default state every time.\r\n//\r\n// useAdminState writes to a module-scope store keyed by an arbitrary\r\n// string. The store survives close/reopen because the NUI iframe stays\r\n// alive across SetNuiFocus toggles — only React unmounts.\r\n//\r\n// Reset scope: keys live for the lifetime of the iframe (= until the\r\n// resource restarts or the player reloads the NUI). That's exactly what\r\n// admins want — \"remember where I was in this session, but don't carry\r\n// over after a restart\".\r\n//\r\n// Usage:\r\n// const [activeTab, setActiveTab] = useAdminState(\"labs:viewMode\", \"list\");\r\n//\r\n// Key collisions: state is scoped per dirk-cfx-react module-scope, which\r\n// is per-iframe — so two different consumer resources can both use the key\r\n// \"viewMode\" without colliding. Within a single consumer, keep keys unique\r\n// (prefix with section name: \"labs:viewMode\", \"shells:editingId\" etc).\r\n\r\nimport { useEffect, useState } from \"react\";\r\n\r\ntype Listener = () => void;\r\n\r\nconst store: Map<string, unknown> = new Map();\r\nconst listeners: Map<string, Set<Listener>> = new Map();\r\n\r\nfunction notify(key: string) {\r\n const ls = listeners.get(key);\r\n if (!ls) return;\r\n ls.forEach((fn) => fn());\r\n}\r\n\r\n/**\r\n * Like `useState` but the value persists in a module-scope store across\r\n * unmount/remount cycles. Identical API otherwise.\r\n *\r\n * @param key Stable string identifying this slot. Must be unique per\r\n * intended slot within the consumer's iframe.\r\n * @param initial Initial value if no value has been stored under `key` yet.\r\n */\r\nexport function useAdminState<T>(key: string, initial: T): [T, (v: T | ((prev: T) => T)) => void] {\r\n // useState here is just for triggering re-renders on cross-component\r\n // updates. The real value lives in the module-scope `store`.\r\n const [, setTick] = useState(0);\r\n\r\n useEffect(() => {\r\n const set = listeners.get(key) ?? new Set<Listener>();\r\n listeners.set(key, set);\r\n const handler = () => setTick((t) => t + 1);\r\n set.add(handler);\r\n return () => {\r\n set.delete(handler);\r\n if (set.size === 0) listeners.delete(key);\r\n };\r\n }, [key]);\r\n\r\n const value = (store.has(key) ? store.get(key) : initial) as T;\r\n\r\n const setValue = (v: T | ((prev: T) => T)) => {\r\n const next = typeof v === \"function\" ? (v as (p: T) => T)(value) : v;\r\n store.set(key, next);\r\n notify(key);\r\n };\r\n\r\n return [value, setValue];\r\n}\r\n\r\n/** Imperative clear — useful for \"reset everything\" admin actions. */\r\nexport function clearAdminState(key?: string) {\r\n if (key) {\r\n store.delete(key);\r\n notify(key);\r\n } else {\r\n const keys = Array.from(store.keys());\r\n store.clear();\r\n keys.forEach(notify);\r\n }\r\n}\r\n","// Fetches normalised player lists from dirk_lib's admin tool subsystem.\r\n//\r\n// Two modes, controlled by `includeOffline`:\r\n// • false (default) → online-only. Fast, cheap, refreshed every ~5s.\r\n// • true → DB-backed search across online + offline players.\r\n// Filtered server-side by `search`. Cap of 50.\r\n//\r\n// Caching: react-query handles dedupe + stale-while-revalidate, keyed on\r\n// (mode, search, limit). Multiple <PlayerSelect>s on screen share one\r\n// fetch. Typing \"j\" → \"jo\" → \"joh\" creates separate cache entries — each\r\n// stays cached for 5 minutes (gcTime) so re-typing reuses instantly.\r\n//\r\n// Admin gating: lives behind the ADMIN_TOOL_QUERY perm check on the\r\n// server. Outside an open admin panel, fetches return empty arrays.\r\n\r\nimport { useQuery, keepPreviousData } from \"@tanstack/react-query\";\r\nimport { fetchNui } from \"../utils/fetchNui\";\r\n\r\nexport type Player = {\r\n /** Server id when online, null when offline. */\r\n id: number | null;\r\n /** Persistent identifier — citizenid (qb/qbx) or license (esx). */\r\n citizenId: string;\r\n /** Steam/Discord display name from GetPlayerName(src). Blank for some\r\n * framework + offline combos (ESX users table doesn't store it). */\r\n name: string;\r\n /** First name + last name from the framework's character data. */\r\n charName: string;\r\n online: boolean;\r\n};\r\n\r\nexport type UsePlayersOptions = {\r\n /** When true, hits the DB and returns offline + online matches.\r\n * When false (default), returns only currently-connected players. */\r\n includeOffline?: boolean;\r\n /** Required when includeOffline=true; ignored otherwise. Server-side\r\n * filtered (LIKE) across char name, citizenId, account name. */\r\n search?: string;\r\n /** Default 50, max 50 (capped server-side). */\r\n limit?: number;\r\n /** Override staleTime — how long the cache stays fresh before background\r\n * refetch. Default 5s for online, 30s for search. */\r\n staleTimeMs?: number;\r\n /** Set to e.g. 10000 to auto-refresh every 10s. Off by default. */\r\n refetchIntervalMs?: number;\r\n};\r\n\r\nexport type UsePlayersResult = {\r\n players: Player[];\r\n isLoading: boolean;\r\n isFetching: boolean;\r\n error: Error | null;\r\n refresh: () => void;\r\n};\r\n\r\nexport function usePlayers(opts: UsePlayersOptions = {}): UsePlayersResult {\r\n const {\r\n includeOffline = false,\r\n search = \"\",\r\n limit = 50,\r\n staleTimeMs,\r\n refetchIntervalMs,\r\n } = opts;\r\n\r\n const query = useQuery<Player[]>({\r\n queryKey: includeOffline\r\n ? [\"dirk:players\", \"search\", search.trim().toLowerCase(), limit]\r\n : [\"dirk:players\", \"online\"],\r\n queryFn: async () => {\r\n const toolId = includeOffline ? \"searchPlayers\" : \"getOnlinePlayers\";\r\n const payload = includeOffline\r\n ? { id: toolId, value: { search: search.trim(), limit } }\r\n : { id: toolId };\r\n const result = await fetchNui<Player[] | null>(\r\n \"ADMIN_TOOL_QUERY\",\r\n payload,\r\n // Browser-dev fallback. Returns a couple of mock players so the\r\n // dev shell isn't blank.\r\n includeOffline\r\n ? [\r\n { id: 1, citizenId: \"ABC12345\", name: \"Dev User\", charName: \"John Doe\", online: true },\r\n { id: null, citizenId: \"DEF67890\", name: \"\", charName: \"Jane Offline\", online: false },\r\n ]\r\n : [\r\n { id: 1, citizenId: \"ABC12345\", name: \"Dev User\", charName: \"John Doe\", online: true },\r\n ],\r\n );\r\n return Array.isArray(result) ? result : [];\r\n },\r\n staleTime: staleTimeMs ?? (includeOffline ? 30_000 : 5_000),\r\n gcTime: 5 * 60_000,\r\n refetchInterval: refetchIntervalMs ?? false,\r\n refetchOnWindowFocus: false,\r\n placeholderData: includeOffline ? keepPreviousData : undefined,\r\n });\r\n\r\n return {\r\n players: query.data ?? [],\r\n isLoading: query.isLoading,\r\n isFetching: query.isFetching,\r\n error: (query.error as Error | null) ?? null,\r\n refresh: () => query.refetch(),\r\n };\r\n}\r\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunk6CNT35D6_cjs = require('./chunk-6CNT35D6.cjs');
|
|
4
|
+
var chunk5XODUPUE_cjs = require('./chunk-5XODUPUE.cjs');
|
|
5
5
|
var chunk4DE2IREA_cjs = require('./chunk-4DE2IREA.cjs');
|
|
6
6
|
var zustand = require('zustand');
|
|
7
7
|
require('@mantine/core/styles.css');
|
|
@@ -353,22 +353,22 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
|
|
|
353
353
|
primaryShade,
|
|
354
354
|
customTheme,
|
|
355
355
|
game
|
|
356
|
-
} =
|
|
357
|
-
|
|
356
|
+
} = chunk5XODUPUE_cjs.useSettings();
|
|
357
|
+
chunk5XODUPUE_cjs.localeStore((s) => s.locales);
|
|
358
358
|
const [scTheme, setScTheme] = React.useState(null);
|
|
359
359
|
React.useLayoutEffect(() => {
|
|
360
|
-
|
|
360
|
+
chunk5XODUPUE_cjs.useSettings.setState({
|
|
361
361
|
overideResourceName
|
|
362
362
|
});
|
|
363
363
|
}, [overideResourceName]);
|
|
364
364
|
React.useEffect(() => {
|
|
365
|
-
|
|
365
|
+
chunk5XODUPUE_cjs.fetchNui("NUI_READY").catch(() => {
|
|
366
366
|
});
|
|
367
367
|
Promise.all([
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
chunk5XODUPUE_cjs.fetchNui("GET_SETTINGS"),
|
|
369
|
+
chunk5XODUPUE_cjs.fetchNui("GET_RESOURCE_VERSION", void 0, { version: "dev" })
|
|
370
370
|
]).then(([data, resourceInfo]) => {
|
|
371
|
-
|
|
371
|
+
chunk5XODUPUE_cjs.useSettings.setState({
|
|
372
372
|
...data,
|
|
373
373
|
resourceVersion: resourceInfo?.version || "dev"
|
|
374
374
|
});
|
|
@@ -376,16 +376,16 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
|
|
|
376
376
|
console.error("Failed to fetch initial settings within dirk-cfx-react:", err);
|
|
377
377
|
});
|
|
378
378
|
}, []);
|
|
379
|
-
|
|
379
|
+
chunk6CNT35D6_cjs.useNuiEvent("UPDATE_DIRK_LIB_SETTINGS", (data) => {
|
|
380
380
|
if (!data || typeof data !== "object") return;
|
|
381
|
-
|
|
381
|
+
chunk5XODUPUE_cjs.useSettings.setState(data);
|
|
382
382
|
});
|
|
383
|
-
|
|
383
|
+
chunk6CNT35D6_cjs.useNuiEvent(
|
|
384
384
|
"UPDATE_SCRIPT_CONFIG",
|
|
385
385
|
(data) => {
|
|
386
386
|
if (!data || !data.config || typeof data.config !== "object") return;
|
|
387
387
|
try {
|
|
388
|
-
const inst =
|
|
388
|
+
const inst = chunk6CNT35D6_cjs.getScriptConfigInstance();
|
|
389
389
|
inst.store.setState((prev) => ({ ...prev, ...data.config }));
|
|
390
390
|
} catch {
|
|
391
391
|
}
|
|
@@ -394,7 +394,7 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
|
|
|
394
394
|
React.useEffect(() => {
|
|
395
395
|
let unsubscribe;
|
|
396
396
|
try {
|
|
397
|
-
const inst =
|
|
397
|
+
const inst = chunk6CNT35D6_cjs.getScriptConfigInstance();
|
|
398
398
|
setScTheme(inst.store.getState()?.theme ?? null);
|
|
399
399
|
const subscribable = inst.store;
|
|
400
400
|
if (typeof subscribable.subscribe === "function") {
|
|
@@ -423,7 +423,7 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
|
|
|
423
423
|
React.useEffect(() => {
|
|
424
424
|
document.body.style.fontFamily = game === "rdr3" ? '"Red Dead", sans-serif' : '"Akrobat Regular", sans-serif';
|
|
425
425
|
}, [game]);
|
|
426
|
-
const content =
|
|
426
|
+
const content = chunk5XODUPUE_cjs.isEnvBrowser() ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
427
427
|
core.BackgroundImage,
|
|
428
428
|
{
|
|
429
429
|
w: "100vw",
|
|
@@ -441,5 +441,5 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
|
|
|
441
441
|
exports.DirkProvider = DirkProvider;
|
|
442
442
|
exports.dirkQueryClient = dirkQueryClient;
|
|
443
443
|
exports.useAdminToolStore = useAdminToolStore;
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
445
|
-
//# sourceMappingURL=chunk-
|
|
444
|
+
//# sourceMappingURL=chunk-EOSHBZQK.cjs.map
|
|
445
|
+
//# sourceMappingURL=chunk-EOSHBZQK.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/AdminTools/adminToolStore.ts","../src/theme.ts","../src/utils/mergeMantineTheme.ts","../src/providers/DirkErrorBoundary.tsx","../src/components/AdminTools/AdminOverlays.tsx","../src/providers/DirkProvider.tsx"],"names":["create","createTheme","theme","alpha","React","__publicField","error","jsx","Box","jsxs","Stack","Title","Text","Code","useEffect","library","fas","far","fab","QueryClient","useSettings","localeStore","useState","useLayoutEffect","fetchNui","useNuiEvent","getScriptConfigInstance","useMemo","isEnvBrowser","BackgroundImage","QueryClientProvider","MantineProvider"],"mappings":"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","file":"chunk-QOQQ3ERZ.cjs","sourcesContent":["// Shared store for \"in-world admin tools\" — flows where the React admin UI\r\n// releases NUI focus, lets the player do something in the game world, and\r\n// resolves a Promise when they confirm or cancel.\r\n//\r\n// Every tool (capture position, place object, etc.) plugs into the same\r\n// store so AdminOverlays can render at most one InstructionPanel at a time\r\n// and the tool's start() returns a typed Promise. Lua side fires the\r\n// matching SendNUIMessage('<TOOL_ID>_RESULT' / '<TOOL_ID>_CANCELLED'),\r\n// which AdminOverlays routes back to whichever tool is active.\r\nimport { create } from \"zustand\";\r\n\r\nexport type ActiveTool<Result = unknown> = {\r\n /** Tool id matching the SendNUIMessage event names from Lua. */\r\n id: string;\r\n /** Title shown in the InstructionPanel header. */\r\n title: string;\r\n /** Optional sub-text under the title. */\r\n hint?: string;\r\n /** Key bindings shown as labelled key caps. */\r\n keys?: { key: string; action: string }[];\r\n /** Resolve handler for the in-flight Promise. */\r\n resolve: (v: Result | null) => void;\r\n};\r\n\r\ntype AdminToolStore = {\r\n active: ActiveTool | null;\r\n /**\r\n * Begin a tool flow. Returns a Promise resolving to the tool's typed\r\n * result (or null on cancel / replacement). Replaces any currently-active\r\n * tool by cancelling it first — only one in-world prompt at a time.\r\n */\r\n begin: <Result>(spec: Omit<ActiveTool<Result>, \"resolve\">) => Promise<Result | null>;\r\n /** Settle the currently-active tool with a value (called by SendNUIMessage handler). */\r\n resolveActive: (value: unknown) => void;\r\n /** Cancel the currently-active tool (called by SendNUIMessage handler or programmatic cancel). */\r\n cancelActive: () => void;\r\n};\r\n\r\nexport const useAdminToolStore = create<AdminToolStore>((set, get) => ({\r\n active: null,\r\n begin: <Result>(spec: Omit<ActiveTool<Result>, \"resolve\">) =>\r\n new Promise<Result | null>((resolve) => {\r\n const prev = get().active;\r\n if (prev) prev.resolve(null); // bump the previous tool out\r\n set({ active: { ...spec, resolve: resolve as ActiveTool[\"resolve\"] } });\r\n }),\r\n resolveActive: (value) => {\r\n const cur = get().active;\r\n if (!cur) return;\r\n cur.resolve(value);\r\n set({ active: null });\r\n },\r\n cancelActive: () => {\r\n const cur = get().active;\r\n if (!cur) return;\r\n cur.resolve(null);\r\n set({ active: null });\r\n },\r\n}));\r\n","import { alpha, ColorInput, createTheme, MantineTheme, MultiSelect, NumberInput, TextInput } from \"@mantine/core\";\r\n\r\nexport const label = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase', \r\n};\r\nexport const error = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n fontFamily: 'Akrobat Regular',\r\n};\r\nexport const description = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n};\r\n\r\nexport const genericInputStyles = {\r\n styles: {\r\n label: label,\r\n error: error,\r\n description: description,\r\n \r\n input:{\r\n background: 'rgba(255,255,255,0.04)',\r\n border: '0.1vh solid rgba(255,255,255,0.08)',\r\n color: 'rgba(255,255,255,0.85)',\r\n minHeight: '4vh',\r\n },\r\n },\r\n};\r\n\r\nconst theme = createTheme({\r\n primaryColor: \"dirk\",\r\n primaryShade: 9,\r\n defaultRadius: \"xs\",\r\n fontFamily: \"Akrobat Regular, sans-serif\",\r\n\r\n radius:{\r\n xxs: '0.3vh',\r\n xs: '0.5vh',\r\n sm: '0.75vh',\r\n md: '1vh',\r\n lg: '1.5vh',\r\n xl: '2vh',\r\n xxl: '3vh',\r\n },\r\n\r\n fontSizes: {\r\n xxs: '1.2vh',\r\n xs: '1.5vh',\r\n sm: '1.8vh',\r\n md: '2.2vh',\r\n lg: '2.8vh',\r\n xl: '3.3vh',\r\n xxl: '3.8vh',\r\n },\r\n\r\n lineHeights: {\r\n xxs: '1.4vh',\r\n xs: '1.8vh',\r\n sm: '2.2vh',\r\n md: '2.8vh',\r\n lg: '3.3vh',\r\n xl: '3.8vh',\r\n },\r\n\r\n spacing:{\r\n xxs: '0.5vh',\r\n xs: '0.75vh',\r\n sm: '1.5vh',\r\n md: '2vh',\r\n lg: '3vh',\r\n xl: '4vh',\r\n xxl: '5vh',\r\n },\r\n\r\n components:{\r\n Progress:{\r\n styles:{\r\n label: {\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase', \r\n },\r\n\r\n root:{\r\n backgroundColor: 'rgba(77, 77, 77, 0.4)',\r\n },\r\n \r\n }\r\n },\r\n \r\n \r\n Input: genericInputStyles,\r\n TextInput: genericInputStyles,\r\n NumberInput: genericInputStyles,\r\n Select: genericInputStyles,\r\n MultiSelect: genericInputStyles,\r\n Textarea: genericInputStyles,\r\n ColorInput: genericInputStyles,\r\n DateInput: genericInputStyles,\r\n\r\n // Mantine's <Button> defaults to rem-based heights (xs ≈ 1.875rem)\r\n // which doesn't match this theme's vh-based input min-heights, so\r\n // `<Button size=\"xs\">` rendered next to `<TextInput size=\"xs\">` ends\r\n // up visibly shorter. Pin the button heights to the same vh values\r\n // the inputs use so xs-everything lines up out of the box.\r\n Button: {\r\n styles: {\r\n label: {\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase',\r\n },\r\n root: {\r\n // Mantine maps these to --button-height per size; setting them\r\n // directly here keeps native Button sizing logic intact.\r\n },\r\n },\r\n vars: (_theme: MantineTheme, props: { size?: string }) => {\r\n const heights: Record<string, string> = {\r\n xs: '4vh',\r\n sm: '4.5vh',\r\n md: '5vh',\r\n lg: '5.5vh',\r\n xl: '6vh',\r\n };\r\n const h = heights[props.size ?? 'sm'] ?? '4.5vh';\r\n return {\r\n root: {\r\n '--button-height': h,\r\n },\r\n };\r\n },\r\n },\r\n\r\n Pill: {\r\n styles: (theme: MantineTheme) => ({\r\n root: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n backgroundColor: 'rgba(76, 76, 76, 0.3)',\r\n height: 'fit-content',\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.05em',\r\n fontFamily: 'Akrobat Bold',\r\n fontSize: '1.25vh',\r\n borderRadius: theme.defaultRadius,\r\n paddingBottom: '0.5vh',\r\n paddingTop: '0.5vh',\r\n }\r\n })\r\n },\r\n\r\n // Mantine's <Tooltip> defaults to a white card with black text — looks\r\n // jarring against every dirk consumer's dark configurator. Every script\r\n // used to hand-paste this dark style block per Tooltip; centralised here\r\n // so consumers get the right look automatically and never need to think\r\n // about it again.\r\n Tooltip: {\r\n styles: (theme: MantineTheme) => ({\r\n tooltip: {\r\n background: alpha(theme.colors.dark[7], 0.95),\r\n border: '0.1vh solid rgba(255,255,255,0.1)',\r\n color: 'rgba(255,255,255,0.75)',\r\n fontFamily: 'Akrobat Bold',\r\n fontSize: '1.3vh',\r\n lineHeight: 1.3,\r\n padding: '0.6vh 0.8vh',\r\n letterSpacing: '0.03em',\r\n },\r\n }),\r\n },\r\n\r\n },\r\n\r\n colors: {\r\n\r\n dirk:[\r\n \"#ffffff\",\r\n \"#f3fce9\",\r\n \"#dbf5bd\",\r\n \"#c3ee91\",\r\n \"#ace765\",\r\n \"#94e039\",\r\n \"#7ac61f\",\r\n \"#5f9a18\",\r\n \"#29420a\",\r\n \"#446e11\",\r\n ],\r\n },\r\n});\r\n\r\n\r\nexport default theme;","import type { MantineColorsTuple, MantineThemeOverride } from \"@mantine/core\";\r\n\r\nconst isValidColorScale = (v: unknown): v is MantineColorsTuple =>\r\n Array.isArray(v) && v.length === 10 && v.every((shade) => typeof shade === \"string\");\r\n\r\nexport function mergeMantineThemeSafe(\r\n base: MantineThemeOverride,\r\n custom?: MantineColorsTuple,\r\n override?: MantineThemeOverride\r\n): MantineThemeOverride {\r\n const colors = { ...base.colors };\r\n\r\n if (custom && isValidColorScale(custom)) {\r\n colors[\"custom\"] = custom;\r\n } else if (!colors[\"custom\"]) {\r\n // Always register a \"custom\" entry so primaryColor === \"custom\" never\r\n // points at undefined and silently falls back to Mantine's default\r\n // (which surfaces as \"the configured palette didn't apply\").\r\n const fallback = (base.colors && (base.colors as any).dirk) as MantineColorsTuple | undefined;\r\n if (fallback && isValidColorScale(fallback)) {\r\n colors[\"custom\"] = fallback;\r\n }\r\n }\r\n\r\n return {\r\n ...base,\r\n ...override,\r\n colors: {\r\n ...colors,\r\n ...(override?.colors ?? {}),\r\n },\r\n };\r\n}\r\n","\"use client\";\r\n\r\nimport React from \"react\";\r\nimport { Box, Code, Stack, Text, Title } from \"@mantine/core\";\r\n\r\ntype State = {\r\n error: Error | null;\r\n stack?: string;\r\n};\r\n\r\nexport class DirkErrorBoundary extends React.Component<\r\n { children: React.ReactNode },\r\n State\r\n> {\r\n state: State = { error: null, stack: undefined };\r\n\r\n static getDerivedStateFromError(error: Error) {\r\n // We can't get component stack here, so just store the error\r\n return { error };\r\n }\r\n\r\n componentDidCatch(error: Error, info: React.ErrorInfo) {\r\n console.group(\"🔥 Dirk UI Crash\");\r\n console.error(\"Error:\", error);\r\n console.error(\"Component Stack:\", info.componentStack);\r\n console.groupEnd();\r\n\r\n // Only log here, don't call setState — the error state is already captured\r\n }\r\n\r\n render() {\r\n if (!this.state.error) return this.props.children;\r\n\r\n return (\r\n <Box\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n width: \"100vw\",\r\n height: \"100vh\",\r\n background: \"rgba(10, 10, 12, 0.92)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n padding: \"2rem\",\r\n zIndex: 999999,\r\n }}\r\n >\r\n <Box\r\n maw={900}\r\n w=\"100%\"\r\n p=\"lg\"\r\n style={{\r\n background: \"rgba(20,20,24,0.75)\",\r\n border: \"1px solid rgba(255,255,255,0.08)\",\r\n borderRadius: \"10px\",\r\n boxShadow: \"0 10px 40px rgba(0,0,0,0.6)\",\r\n }}\r\n >\r\n <Stack gap=\"sm\">\r\n <Title order={2} c=\"red.5\">\r\n Dirk UI Crash\r\n </Title>\r\n\r\n <Text c=\"dimmed\" size=\"sm\">\r\n The interface encountered a fatal error and stopped rendering.\r\n </Text>\r\n\r\n <Code block style={{ maxHeight: 150, overflow: \"auto\" }}>\r\n {this.state.error?.message}\r\n </Code>\r\n\r\n {/* Optional: show component stack if you pass it from props */}\r\n <Text size=\"xs\" c=\"dimmed\">\r\n Check console for full stack trace\r\n </Text>\r\n </Stack>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n}","// Internal. Auto-mounted by DirkProvider. Consumers never reference this\r\n// directly — it just exists in the tree so any admin-tool flow has its\r\n// NUI-message routing and admin-UI-hide effect wired up automatically.\r\n//\r\n// What it does (and DOESN'T do anymore):\r\n// • Subscribes to <toolId>_RESULT and <toolId>_CANCELLED NUI messages\r\n// from any admin tool and routes them into useAdminToolStore so\r\n// `await pickDoor()` etc. resolve.\r\n// • While a tool is active, applies a body attribute so the consumer\r\n// admin UI hides itself (so the player sees the world).\r\n//\r\n// What used to live here but doesn't anymore:\r\n// • Rendering the bottom-right InstructionPanel — that's now ALWAYS\r\n// driven by dirk_lib's own NUI via `lib.showInstructions`, so the\r\n// panel renders in one place regardless of where the tool was\r\n// triggered from. Avoids dual code paths and per-resource iframe\r\n// compositing differences that were eating immediate-mode draws.\r\n\r\nimport { useEffect } from \"react\";\r\nimport { useAdminToolStore } from \"./adminToolStore\";\r\n\r\n// Single global listener registration so multiple DirkProviders (if a\r\n// consumer ever stacks them) don't multiply-route the same message into\r\n// the store. Guarded by a module-level flag.\r\nlet listenerInstalled = false;\r\n\r\nfunction installNuiListener() {\r\n if (listenerInstalled || typeof window === \"undefined\") return;\r\n listenerInstalled = true;\r\n window.addEventListener(\"message\", (e) => {\r\n const msg = e?.data;\r\n if (!msg || typeof msg !== \"object\" || typeof msg.action !== \"string\") return;\r\n const action: string = msg.action;\r\n const cur = useAdminToolStore.getState().active;\r\n if (!cur) return;\r\n if (action === `${cur.id}_RESULT`) {\r\n useAdminToolStore.getState().resolveActive(msg.data ?? null);\r\n } else if (action === `${cur.id}_CANCELLED`) {\r\n useAdminToolStore.getState().cancelActive();\r\n }\r\n });\r\n}\r\n\r\n// CSS injected once that hides every body child except things tagged\r\n// with data-dirk-instruction-overlay. Used to make the consumer's\r\n// admin panel disappear so the player sees the world while picking.\r\nconst BODY_HIDE_STYLE_ID = \"dirk-instruction-panel-style\";\r\nconst BODY_HIDE_ATTR = \"data-dirk-instruction-active\";\r\nconst OVERLAY_ATTR = \"data-dirk-instruction-overlay\";\r\n\r\nfunction ensureBodyHideStyle() {\r\n if (typeof document === \"undefined\") return;\r\n if (document.getElementById(BODY_HIDE_STYLE_ID)) return;\r\n const el = document.createElement(\"style\");\r\n el.id = BODY_HIDE_STYLE_ID;\r\n el.textContent = `\r\n body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {\r\n visibility: hidden !important;\r\n opacity: 0 !important;\r\n pointer-events: none !important;\r\n }\r\n `;\r\n document.head.appendChild(el);\r\n}\r\n\r\nexport function AdminOverlays() {\r\n const active = useAdminToolStore((s) => s.active);\r\n\r\n useEffect(() => {\r\n installNuiListener();\r\n }, []);\r\n\r\n // Body-hide effect — toggles the attribute while a tool is in flight\r\n // so the consumer admin UI vanishes and the player sees the world.\r\n useEffect(() => {\r\n if (!active || typeof document === \"undefined\") return;\r\n ensureBodyHideStyle();\r\n document.body.setAttribute(BODY_HIDE_ATTR, \"\");\r\n return () => {\r\n document.body.removeAttribute(BODY_HIDE_ATTR);\r\n };\r\n }, [active]);\r\n\r\n // Nothing visual to render — dirk_lib's NUI handles the bottom-right\r\n // InstructionPanel via lib.showInstructions.\r\n return null;\r\n}\r\n","\"use client\";\r\n\r\nimport \"@mantine/core/styles.css\";\r\nimport \"@mantine/notifications/styles.css\";\r\nimport './styles/fonts.css';\r\nimport './styles/notify.css';\r\nimport './styles/scrollBar.css';\r\nimport './styles/tornEdge.css';\r\n\r\n\r\nimport { library } from \"@fortawesome/fontawesome-svg-core\";\r\nimport { fab } from \"@fortawesome/free-brands-svg-icons\";\r\nimport { far } from \"@fortawesome/free-regular-svg-icons\";\r\nimport { fas } from \"@fortawesome/free-solid-svg-icons\";\r\nlibrary.add(fas, far, fab);\r\n\r\nimport theme from \"@/theme\";\r\nimport { BackgroundImage, MantineColorShade, MantineColorsTuple, MantineProvider } from \"@mantine/core\";\r\nimport { useEffect, useLayoutEffect, useMemo, useState } from \"react\";\r\n\r\nimport { useNuiEvent } from \"@/hooks/useNuiEvent\";\r\nimport { fetchNui, isEnvBrowser } from \"@/utils\";\r\nimport { localeStore } from \"@/utils/locales\";\r\nimport { mergeMantineThemeSafe } from \"@/utils/mergeMantineTheme\";\r\nimport { SettingsState, useSettings } from \"@/utils/useSettings\";\r\nimport { getScriptConfigInstance } from \"@/hooks/useScriptConfig\";\r\nimport { DirkErrorBoundary } from \"./DirkErrorBoundary\";\r\nimport { AdminOverlays } from \"@/components/AdminTools/AdminOverlays\";\r\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\r\n\r\n// Shared QueryClient for every consumer of <DirkProvider>. Hooks in this\r\n// library (usePlayers, ConfigPanel's history pagination, MissingItemsBanner\r\n// audit, etc.) all consume this so consumers don't need to wire their own\r\n// QueryClientProvider. Defaults match what ConfigPanel previously used in\r\n// its private QueryClient so the move doesn't change refetch cadence.\r\n//\r\n// Consumers that already wrap in their own outer QueryClientProvider (e.g.\r\n// dirk_fishing's App.tsx) still work — react-query picks the nearest\r\n// provider in the tree, and \"nearest\" here means this inner one. Anything\r\n// inside DirkProvider uses this client; anything outside uses the consumer's\r\n// outer one (if present).\r\nexport const dirkQueryClient = new QueryClient({\r\n defaultOptions: { queries: { staleTime: 30_000, gcTime: 5 * 60_000 } },\r\n});\r\n\r\nexport type DirkProviderProps = {\r\n children: React.ReactNode;\r\n overideResourceName?: string;\r\n themeOverride?: any;\r\n};\r\n\r\n// Shape DirkProvider looks for at `scriptConfig.theme` to detect a per-resource\r\n// theme override. Consumers opt in by adding a `theme` block to their schema.json\r\n// with these four fields — DirkProvider auto-detects and applies it. Resources\r\n// with no `theme` block (or `useOverride: false`) follow the global dirk_lib\r\n// theme as before. No bridge component, no extra wiring required from consumers.\r\ntype ScriptConfigThemeOverride = {\r\n useOverride?: boolean;\r\n primaryColor?: string;\r\n primaryShade?: number;\r\n customTheme?: MantineColorsTuple | string[];\r\n};\r\n\r\nexport function DirkProvider({ children, overideResourceName, themeOverride }: DirkProviderProps) {\r\n const {\r\n primaryColor,\r\n primaryShade,\r\n customTheme,\r\n game,\r\n } = useSettings();\r\n\r\n // Subscribe to locale changes so the entire tree re-renders when an admin\r\n // updates `language` via dirk_lib's scriptConfig — components calling\r\n // `locale(\"Foo\")` directly (no hook) get fresh strings on the next render.\r\n const _locales = localeStore((s) => s.locales);\r\n void _locales;\r\n\r\n // Per-resource theme override snapshot, read from `scriptConfig.theme` when\r\n // the consumer has a scriptConfig registered. Updated by the subscription\r\n // below; survives Mantine recomputes via useMemo deps.\r\n const [scTheme, setScTheme] = useState<ScriptConfigThemeOverride | null>(null);\r\n\r\n useLayoutEffect(() => {\r\n useSettings.setState({\r\n overideResourceName,\r\n });\r\n }, [overideResourceName]);\r\n\r\n useEffect(() => {\r\n fetchNui('NUI_READY').catch(() => {});\r\n Promise.all([\r\n fetchNui<Partial<SettingsState>>('GET_SETTINGS'),\r\n fetchNui<{ version?: string }>('GET_RESOURCE_VERSION', undefined, { version: 'dev' }),\r\n ]).then(([data, resourceInfo]) => {\r\n useSettings.setState({\r\n ...data,\r\n resourceVersion: resourceInfo?.version || 'dev',\r\n });\r\n }).catch((err) => {\r\n console.error(\"Failed to fetch initial settings within dirk-cfx-react:\", err);\r\n });\r\n }, []);\r\n\r\n // Live updates: dirk_lib broadcasts UPDATE_DIRK_LIB_SETTINGS whenever an admin\r\n // changes appearance/localization via the scriptConfig UI. Merge the partial\r\n // patch into the settings store so theme / currency / branding update live\r\n // without a resource restart. Per-resource theme override is handled below\r\n // via the scriptConfig subscription — pushes here can flow through unchanged.\r\n useNuiEvent<Partial<SettingsState>>('UPDATE_DIRK_LIB_SETTINGS', (data) => {\r\n if (!data || typeof data !== 'object') return;\r\n useSettings.setState(data);\r\n });\r\n\r\n // ── Per-resource theme auto-detect ────────────────────────────────────────\r\n // If the consumer has called createScriptConfig() (i.e. has a registered\r\n // scriptConfig instance) AND their schema has a `theme` block with\r\n // `useOverride: true`, DirkProvider uses those theme values instead of\r\n // dirk_lib's global appearance. Consumers don't have to write any glue\r\n // code — they just add the `theme` block to schema.json and a Theme tab\r\n // (via ThemeOverrideSection) to their configurator. Everything else is\r\n // handled here.\r\n //\r\n // We also forward UPDATE_SCRIPT_CONFIG pushes into the store so this works\r\n // whether or not the consumer remembers to call useScriptConfigHooks().\r\n useNuiEvent<{ config?: Record<string, unknown>; clientVersion?: number }>(\r\n 'UPDATE_SCRIPT_CONFIG',\r\n (data) => {\r\n if (!data || !data.config || typeof data.config !== 'object') return;\r\n try {\r\n const inst = getScriptConfigInstance<Record<string, unknown>>();\r\n inst.store.setState((prev) => ({ ...prev, ...(data.config as Record<string, unknown>) }));\r\n } catch {\r\n // No scriptConfig instance registered — consumer doesn't use scriptConfig, ignore.\r\n }\r\n }\r\n );\r\n\r\n useEffect(() => {\r\n let unsubscribe: (() => void) | undefined;\r\n try {\r\n const inst = getScriptConfigInstance<{ theme?: ScriptConfigThemeOverride }>();\r\n\r\n // Seed from whatever's already in the store (covers cases where the\r\n // store was hydrated before DirkProvider mounted, e.g. by an upstream\r\n // ConfigPanel open).\r\n setScTheme((inst.store.getState() as { theme?: ScriptConfigThemeOverride })?.theme ?? null);\r\n\r\n // Subscribe to store mutations so live edits / hydration completion\r\n // re-trigger the override application.\r\n const subscribable = inst.store as unknown as {\r\n subscribe?: (listener: (s: { theme?: ScriptConfigThemeOverride }) => void) => () => void;\r\n };\r\n if (typeof subscribable.subscribe === 'function') {\r\n unsubscribe = subscribable.subscribe((s) => {\r\n setScTheme(s?.theme ?? null);\r\n });\r\n }\r\n\r\n // Option B: NO proactive GET_FULL_SCRIPT_CONFIG round-trip here. The\r\n // client-visible config (which includes the `theme` block — it is NOT\r\n // server-only) is pushed by dirk_lib via UPDATE_SCRIPT_CONFIG and lands\r\n // in the store. The store `subscribe` above updates `scTheme` the moment\r\n // that push arrives, and the seed `setScTheme(...)` from the current\r\n // store state covers the case where the push beat this mount. This drops\r\n // the per-mount full-config fetch entirely — reopen/reload = store hit,\r\n // 0 bytes. The cold-start NUI-readiness race is handled on the Lua side\r\n // (the push is (re)sent once the NUI signals NUI_READY).\r\n } catch {\r\n // No scriptConfig instance — consumer doesn't use scriptConfig at all.\r\n // Fall through silently; global theme behaviour is unchanged.\r\n }\r\n return () => { unsubscribe?.(); };\r\n }, []);\r\n\r\n const overrideActive = scTheme?.useOverride === true;\r\n const effectivePrimaryColor = overrideActive ? (scTheme!.primaryColor ?? primaryColor) : primaryColor;\r\n const effectivePrimaryShade = overrideActive ? (scTheme!.primaryShade ?? primaryShade) : primaryShade;\r\n const effectiveCustomTheme = overrideActive\r\n ? ((scTheme!.customTheme as MantineColorsTuple | undefined) ?? customTheme)\r\n : customTheme;\r\n\r\n const mergedTheme = useMemo(\r\n () => mergeMantineThemeSafe(\r\n { ...theme, primaryColor: effectivePrimaryColor, primaryShade: effectivePrimaryShade as MantineColorShade },\r\n effectiveCustomTheme,\r\n themeOverride\r\n ),\r\n [effectivePrimaryColor, effectivePrimaryShade, effectiveCustomTheme, themeOverride]\r\n );\r\n\r\n useEffect(() => {\r\n document.body.style.fontFamily =\r\n game === \"rdr3\"\r\n ? '\"Red Dead\", sans-serif'\r\n : '\"Akrobat Regular\", sans-serif';\r\n }, [game]);\r\n\r\n const content = isEnvBrowser() ? (\r\n <BackgroundImage\r\n w=\"100vw\"\r\n h=\"100vh\"\r\n src={game === \"fivem\"\r\n ? \"https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg\"\r\n : \"https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg\"}\r\n >\r\n {children}\r\n </BackgroundImage>\r\n ) : (\r\n children\r\n );\r\n\r\n return (\r\n <QueryClientProvider client={dirkQueryClient}>\r\n <MantineProvider theme={mergedTheme} defaultColorScheme=\"dark\">\r\n <DirkErrorBoundary>\r\n {content}\r\n {/* Auto-mounted. Renders nothing unless an admin tool is active.\r\n Consumers never have to wire this up themselves — every admin\r\n tool flow that uses useAdminToolStore.begin() gets the\r\n bottom-right InstructionPanel + NUI message routing for free. */}\r\n <AdminOverlays />\r\n </DirkErrorBoundary>\r\n </MantineProvider>\r\n </QueryClientProvider>\r\n );\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/AdminTools/adminToolStore.ts","../src/theme.ts","../src/utils/mergeMantineTheme.ts","../src/providers/DirkErrorBoundary.tsx","../src/components/AdminTools/AdminOverlays.tsx","../src/providers/DirkProvider.tsx"],"names":["create","createTheme","theme","alpha","React","__publicField","error","jsx","Box","jsxs","Stack","Title","Text","Code","useEffect","library","fas","far","fab","QueryClient","useSettings","localeStore","useState","useLayoutEffect","fetchNui","useNuiEvent","getScriptConfigInstance","useMemo","isEnvBrowser","BackgroundImage","QueryClientProvider","MantineProvider"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsCO,IAAM,iBAAA,GAAoBA,cAAA,CAAuB,CAAC,GAAA,EAAK,GAAA,MAAS;AAAA,EACrE,MAAA,EAAQ,IAAA;AAAA,EACR,OAAO,CAAS,IAAA,KACd,IAAI,OAAA,CAAuB,CAAC,OAAA,KAAY;AACtC,IAAA,MAAM,IAAA,GAAO,KAAI,CAAE,MAAA;AACnB,IAAA,IAAI,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA;AAC3B,IAAA,GAAA,CAAI,EAAE,MAAA,EAAQ,EAAE,GAAG,IAAA,EAAM,OAAA,IAA6C,CAAA;AAAA,EACxE,CAAC,CAAA;AAAA,EACH,aAAA,EAAe,CAAC,KAAA,KAAU;AACxB,IAAA,MAAM,GAAA,GAAM,KAAI,CAAE,MAAA;AAClB,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,GAAA,CAAI,QAAQ,KAAK,CAAA;AACjB,IAAA,GAAA,CAAI,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EACtB,CAAA;AAAA,EACA,cAAc,MAAM;AAClB,IAAA,MAAM,GAAA,GAAM,KAAI,CAAE,MAAA;AAClB,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,GAAA,CAAI,QAAQ,IAAI,CAAA;AAChB,IAAA,GAAA,CAAI,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EACtB;AACF,CAAA,CAAE;ACxDK,IAAM,KAAA,GAAQ;AAAA,EACnB,QAAA,EAAU,6BAAA;AAAA,EACV,UAAA,EAAY,cAAA;AAAA,EACZ,aAAA,EAAe,QAAA;AAAA,EACf,aAAA,EAAe;AACjB,CAAA;AACO,IAAM,KAAA,GAAQ;AAAA,EACnB,QAAA,EAAU,6BAAA;AAAA,EACV,UAAA,EAAY;AACd,CAAA;AACO,IAAM,WAAA,GAAc;AAAA,EACzB,QAAA,EAAU;AACZ,CAAA;AAEO,IAAM,kBAAA,GAAqB;AAAA,EAChC,MAAA,EAAQ;AAAA,IACN,KAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAA;AAAA,IAEA,KAAA,EAAM;AAAA,MACJ,UAAA,EAAY,wBAAA;AAAA,MACZ,MAAA,EAAQ,oCAAA;AAAA,MACR,KAAA,EAAO,wBAAA;AAAA,MACP,SAAA,EAAW;AAAA;AACb;AAEJ,CAAA;AAEA,IAAM,QAAQC,gBAAA,CAAY;AAAA,EACxB,YAAA,EAAc,MAAA;AAAA,EACd,YAAA,EAAc,CAAA;AAAA,EACd,aAAA,EAAe,IAAA;AAAA,EACf,UAAA,EAAY,6BAAA;AAAA,EAEZ,MAAA,EAAO;AAAA,IACL,GAAA,EAAK,OAAA;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,QAAA;AAAA,IACJ,EAAA,EAAI,KAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,KAAA;AAAA,IACJ,GAAA,EAAK;AAAA,GACP;AAAA,EAEA,SAAA,EAAW;AAAA,IACT,GAAA,EAAK,OAAA;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,GAAA,EAAK;AAAA,GACP;AAAA,EAEA,WAAA,EAAa;AAAA,IACX,GAAA,EAAK,OAAA;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI;AAAA,GACN;AAAA,EAEA,OAAA,EAAQ;AAAA,IACN,GAAA,EAAK,OAAA;AAAA,IACL,EAAA,EAAI,QAAA;AAAA,IACJ,EAAA,EAAI,OAAA;AAAA,IACJ,EAAA,EAAI,KAAA;AAAA,IACJ,EAAA,EAAI,KAAA;AAAA,IACJ,EAAA,EAAI,KAAA;AAAA,IACJ,GAAA,EAAK;AAAA,GACP;AAAA,EAEA,UAAA,EAAW;AAAA,IACT,QAAA,EAAS;AAAA,MACP,MAAA,EAAO;AAAA,QACL,KAAA,EAAO;AAAA,UACL,UAAA,EAAY,cAAA;AAAA,UACZ,aAAA,EAAe,QAAA;AAAA,UACf,aAAA,EAAe;AAAA,SACjB;AAAA,QAEA,IAAA,EAAK;AAAA,UACH,eAAA,EAAiB;AAAA;AACnB;AAEF,KACF;AAAA,IAGA,KAAA,EAAQ,kBAAA;AAAA,IACR,SAAA,EAAW,kBAAA;AAAA,IACX,WAAA,EAAa,kBAAA;AAAA,IACb,MAAA,EAAQ,kBAAA;AAAA,IACR,WAAA,EAAc,kBAAA;AAAA,IACd,QAAA,EAAU,kBAAA;AAAA,IACV,UAAA,EAAY,kBAAA;AAAA,IACZ,SAAA,EAAW,kBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOX,MAAA,EAAQ;AAAA,MACN,MAAA,EAAQ;AAAA,QACN,KAAA,EAAO;AAAA,UACL,UAAA,EAAY,cAAA;AAAA,UACZ,aAAA,EAAe,QAAA;AAAA,UACf,aAAA,EAAe;AAAA,SACjB;AAAA,QACA,IAAA,EAAM;AAAA;AAAA;AAAA;AAGN,OACF;AAAA,MACA,IAAA,EAAM,CAAC,MAAA,EAAsB,KAAA,KAA6B;AACxD,QAAA,MAAM,OAAA,GAAkC;AAAA,UACtC,EAAA,EAAI,KAAA;AAAA,UACJ,EAAA,EAAI,OAAA;AAAA,UACJ,EAAA,EAAI,KAAA;AAAA,UACJ,EAAA,EAAI,OAAA;AAAA,UACJ,EAAA,EAAI;AAAA,SACN;AACA,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,KAAA,CAAM,IAAA,IAAQ,IAAI,CAAA,IAAK,OAAA;AACzC,QAAA,OAAO;AAAA,UACL,IAAA,EAAM;AAAA,YACJ,iBAAA,EAAmB;AAAA;AACrB,SACF;AAAA,MACF;AAAA,KACF;AAAA,IAEA,IAAA,EAAM;AAAA,MACJ,MAAA,EAAQ,CAACC,MAAAA,MAAyB;AAAA,QAChC,IAAA,EAAM;AAAA,UACJ,OAAA,EAAS,aAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,eAAA;AAAA,UAChB,eAAA,EAAiB,uBAAA;AAAA,UACjB,MAAA,EAAQ,aAAA;AAAA,UACR,aAAA,EAAe,WAAA;AAAA,UACf,aAAA,EAAe,QAAA;AAAA,UACf,UAAA,EAAY,cAAA;AAAA,UACZ,QAAA,EAAU,QAAA;AAAA,UACV,cAAcA,MAAAA,CAAM,aAAA;AAAA,UACpB,aAAA,EAAe,OAAA;AAAA,UACf,UAAA,EAAY;AAAA;AACd,OACF;AAAA,KACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,OAAA,EAAS;AAAA,MACP,MAAA,EAAQ,CAACA,MAAAA,MAAyB;AAAA,QAChC,OAAA,EAAS;AAAA,UACP,YAAYC,UAAA,CAAMD,MAAAA,CAAM,OAAO,IAAA,CAAK,CAAC,GAAG,IAAI,CAAA;AAAA,UAC5C,MAAA,EAAQ,mCAAA;AAAA,UACR,KAAA,EAAO,wBAAA;AAAA,UACP,UAAA,EAAY,cAAA;AAAA,UACZ,QAAA,EAAU,OAAA;AAAA,UACV,UAAA,EAAY,GAAA;AAAA,UACZ,OAAA,EAAS,aAAA;AAAA,UACT,aAAA,EAAe;AAAA;AACjB,OACF;AAAA;AACF,GAEF;AAAA,EAEA,MAAA,EAAQ;AAAA,IAEN,IAAA,EAAK;AAAA,MACH,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA;AACF;AAEJ,CAAC,CAAA;AAGD,IAAO,aAAA,GAAQ,KAAA;;;ACjMf,IAAM,oBAAoB,CAAC,CAAA,KACzB,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,MAAA,KAAW,EAAA,IAAM,EAAE,KAAA,CAAM,CAAC,KAAA,KAAU,OAAO,UAAU,QAAQ,CAAA;AAE9E,SAAS,qBAAA,CACd,IAAA,EACA,MAAA,EACA,QAAA,EACsB;AACtB,EAAA,MAAM,MAAA,GAAS,EAAE,GAAG,IAAA,CAAK,MAAA,EAAO;AAEhC,EAAA,IAAI,MAAA,IAAU,iBAAA,CAAkB,MAAM,CAAA,EAAG;AACvC,IAAA,MAAA,CAAO,QAAQ,CAAA,GAAI,MAAA;AAAA,EACrB,CAAA,MAAA,IAAW,CAAC,MAAA,CAAO,QAAQ,CAAA,EAAG;AAI5B,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,MAAA,IAAW,IAAA,CAAK,MAAA,CAAe,IAAA;AACtD,IAAA,IAAI,QAAA,IAAY,iBAAA,CAAkB,QAAQ,CAAA,EAAG;AAC3C,MAAA,MAAA,CAAO,QAAQ,CAAA,GAAI,QAAA;AAAA,IACrB;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,GAAG,QAAA;AAAA,IACH,MAAA,EAAQ;AAAA,MACN,GAAG,MAAA;AAAA,MACH,GAAI,QAAA,EAAU,MAAA,IAAU;AAAC;AAC3B,GACF;AACF;ACtBO,IAAM,iBAAA,GAAN,cAAgCE,sBAAA,CAAM,SAAA,CAG3C;AAAA,EAHK,WAAA,GAAA;AAAA,IAAA,KAAA,CAAA,GAAA,SAAA,CAAA;AAIL,IAAAC,+BAAA,CAAA,IAAA,EAAA,OAAA,EAAe,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO,MAAA,EAAU,CAAA;AAAA,EAAA;AAAA,EAE/C,OAAO,yBAAyBC,MAAAA,EAAc;AAE5C,IAAA,OAAO,EAAE,OAAAA,MAAAA,EAAM;AAAA,EACjB;AAAA,EAEA,iBAAA,CAAkBA,QAAc,IAAA,EAAuB;AACrD,IAAA,OAAA,CAAQ,MAAM,yBAAkB,CAAA;AAChC,IAAA,OAAA,CAAQ,KAAA,CAAM,UAAUA,MAAK,CAAA;AAC7B,IAAA,OAAA,CAAQ,KAAA,CAAM,kBAAA,EAAoB,IAAA,CAAK,cAAc,CAAA;AACrD,IAAA,OAAA,CAAQ,QAAA,EAAS;AAAA,EAGnB;AAAA,EAEA,MAAA,GAAS;AACP,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,CAAM,KAAA,EAAO,OAAO,KAAK,KAAA,CAAM,QAAA;AAEzC,IAAA,uBACEC,cAAA;AAAA,MAACC,QAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO;AAAA,UACL,QAAA,EAAU,OAAA;AAAA,UACV,KAAA,EAAO,CAAA;AAAA,UACP,KAAA,EAAO,OAAA;AAAA,UACP,MAAA,EAAQ,OAAA;AAAA,UACR,UAAA,EAAY,wBAAA;AAAA,UACZ,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,QAAA;AAAA,UAChB,OAAA,EAAS,MAAA;AAAA,UACT,MAAA,EAAQ;AAAA,SACV;AAAA,QAEA,QAAA,kBAAAD,cAAA;AAAA,UAACC,QAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,GAAA;AAAA,YACL,CAAA,EAAE,MAAA;AAAA,YACF,CAAA,EAAE,IAAA;AAAA,YACF,KAAA,EAAO;AAAA,cACL,UAAA,EAAY,qBAAA;AAAA,cACZ,MAAA,EAAQ,kCAAA;AAAA,cACR,YAAA,EAAc,MAAA;AAAA,cACd,SAAA,EAAW;AAAA,aACb;AAAA,YAEA,QAAA,kBAAAC,eAAA,CAACC,UAAA,EAAA,EAAM,GAAA,EAAI,IAAA,EACT,QAAA,EAAA;AAAA,8BAAAH,cAAA,CAACI,UAAA,EAAA,EAAM,KAAA,EAAO,CAAA,EAAG,CAAA,EAAE,SAAQ,QAAA,EAAA,eAAA,EAE3B,CAAA;AAAA,6CAECC,SAAA,EAAA,EAAK,CAAA,EAAE,QAAA,EAAS,IAAA,EAAK,MAAK,QAAA,EAAA,gEAAA,EAE3B,CAAA;AAAA,8BAEAL,cAAA,CAACM,SAAA,EAAA,EAAK,KAAA,EAAK,IAAA,EAAC,OAAO,EAAE,SAAA,EAAW,GAAA,EAAK,QAAA,EAAU,MAAA,EAAO,EACnD,QAAA,EAAA,IAAA,CAAK,KAAA,CAAM,OAAO,OAAA,EACrB,CAAA;AAAA,6CAGCD,SAAA,EAAA,EAAK,IAAA,EAAK,IAAA,EAAK,CAAA,EAAE,UAAS,QAAA,EAAA,oCAAA,EAE3B;AAAA,aAAA,EACF;AAAA;AAAA;AACF;AAAA,KACF;AAAA,EAEJ;AACF,CAAA;ACzDA,IAAI,iBAAA,GAAoB,KAAA;AAExB,SAAS,kBAAA,GAAqB;AAC5B,EAAA,IAAI,iBAAA,IAAqB,OAAO,MAAA,KAAW,WAAA,EAAa;AACxD,EAAA,iBAAA,GAAoB,IAAA;AACpB,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAM;AACxC,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA;AACf,IAAA,IAAI,CAAC,OAAO,OAAO,GAAA,KAAQ,YAAY,OAAO,GAAA,CAAI,WAAW,QAAA,EAAU;AACvE,IAAA,MAAM,SAAiB,GAAA,CAAI,MAAA;AAC3B,IAAA,MAAM,GAAA,GAAM,iBAAA,CAAkB,QAAA,EAAS,CAAE,MAAA;AACzC,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,IAAI,MAAA,KAAW,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,OAAA,CAAA,EAAW;AACjC,MAAA,iBAAA,CAAkB,QAAA,EAAS,CAAE,aAAA,CAAc,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,IAC7D,CAAA,MAAA,IAAW,MAAA,KAAW,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,UAAA,CAAA,EAAc;AAC3C,MAAA,iBAAA,CAAkB,QAAA,GAAW,YAAA,EAAa;AAAA,IAC5C;AAAA,EACF,CAAC,CAAA;AACH;AAKA,IAAM,kBAAA,GAAqB,8BAAA;AAC3B,IAAM,cAAA,GAAiB,8BAAA;AACvB,IAAM,YAAA,GAAe,+BAAA;AAErB,SAAS,mBAAA,GAAsB;AAC7B,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,kBAAkB,CAAA,EAAG;AACjD,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AACzC,EAAA,EAAA,CAAG,EAAA,GAAK,kBAAA;AACR,EAAA,EAAA,CAAG,WAAA,GAAc;AAAA,SAAA,EACR,cAAc,cAAc,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAMjD,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,EAAE,CAAA;AAC9B;AAEO,SAAS,aAAA,GAAgB;AAC9B,EAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,CAAC,CAAA,KAAM,EAAE,MAAM,CAAA;AAEhD,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,kBAAA,EAAmB;AAAA,EACrB,CAAA,EAAG,EAAE,CAAA;AAIL,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,QAAA,KAAa,WAAA,EAAa;AAChD,IAAA,mBAAA,EAAoB;AACpB,IAAA,QAAA,CAAS,IAAA,CAAK,YAAA,CAAa,cAAA,EAAgB,EAAE,CAAA;AAC7C,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,IAAA,CAAK,gBAAgB,cAAc,CAAA;AAAA,IAC9C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAIX,EAAA,OAAO,IAAA;AACT;ACxEAC,0BAAA,CAAQ,GAAA,CAAIC,qBAAA,EAAKC,uBAAA,EAAKC,sBAAG,CAAA;AA2BlB,IAAM,eAAA,GAAkB,IAAIC,sBAAA,CAAY;AAAA,EAC7C,cAAA,EAAgB,EAAE,OAAA,EAAS,EAAE,WAAW,GAAA,EAAQ,MAAA,EAAQ,CAAA,GAAI,GAAA,EAAO;AACrE,CAAC;AAoBM,SAAS,YAAA,CAAa,EAAE,QAAA,EAAU,mBAAA,EAAqB,eAAc,EAAsB;AAChG,EAAA,MAAM;AAAA,IACJ,YAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,MACEC,6BAAA,EAAY;AAKhB,EAAiBC,6BAAA,CAAY,CAAC,CAAA,KAAM,EAAE,OAAO;AAM7C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,eAA2C,IAAI,CAAA;AAE7E,EAAAC,qBAAA,CAAgB,MAAM;AACpB,IAAAH,6BAAA,CAAY,QAAA,CAAS;AAAA,MACnB;AAAA,KACD,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAAN,gBAAU,MAAM;AACd,IAAAU,0BAAA,CAAS,WAAW,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AACpC,IAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,MACVA,2BAAiC,cAAc,CAAA;AAAA,MAC/CA,2BAA+B,sBAAA,EAAwB,MAAA,EAAW,EAAE,OAAA,EAAS,OAAO;AAAA,KACrF,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,IAAA,EAAM,YAAY,CAAA,KAAM;AAChC,MAAAJ,6BAAA,CAAY,QAAA,CAAS;AAAA,QACnB,GAAG,IAAA;AAAA,QACH,eAAA,EAAiB,cAAc,OAAA,IAAW;AAAA,OAC3C,CAAA;AAAA,IACH,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAChB,MAAA,OAAA,CAAQ,KAAA,CAAM,2DAA2D,GAAG,CAAA;AAAA,IAC9E,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,EAAE,CAAA;AAOL,EAAAK,6BAAA,CAAoC,0BAAA,EAA4B,CAAC,IAAA,KAAS;AACxE,IAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACvC,IAAAL,6BAAA,CAAY,SAAS,IAAI,CAAA;AAAA,EAC3B,CAAC,CAAA;AAaD,EAAAK,6BAAA;AAAA,IACE,sBAAA;AAAA,IACA,CAAC,IAAA,KAAS;AACR,MAAA,IAAI,CAAC,QAAQ,CAAC,IAAA,CAAK,UAAU,OAAO,IAAA,CAAK,WAAW,QAAA,EAAU;AAC9D,MAAA,IAAI;AACF,QAAA,MAAM,OAAOC,yCAAA,EAAiD;AAC9D,QAAA,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,CAAC,IAAA,MAAU,EAAE,GAAG,IAAA,EAAM,GAAI,IAAA,CAAK,MAAA,EAAmC,CAAE,CAAA;AAAA,MAC1F,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,GACF;AAEA,EAAAZ,gBAAU,MAAM;AACd,IAAA,IAAI,WAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,OAAOY,yCAAA,EAA+D;AAK5E,MAAA,UAAA,CAAY,IAAA,CAAK,KAAA,CAAM,QAAA,EAAS,EAA6C,SAAS,IAAI,CAAA;AAI1F,MAAA,MAAM,eAAe,IAAA,CAAK,KAAA;AAG1B,MAAA,IAAI,OAAO,YAAA,CAAa,SAAA,KAAc,UAAA,EAAY;AAChD,QAAA,WAAA,GAAc,YAAA,CAAa,SAAA,CAAU,CAAC,CAAA,KAAM;AAC1C,UAAA,UAAA,CAAW,CAAA,EAAG,SAAS,IAAI,CAAA;AAAA,QAC7B,CAAC,CAAA;AAAA,MACH;AAAA,IAWF,CAAA,CAAA,MAAQ;AAAA,IAGR;AACA,IAAA,OAAO,MAAM;AAAE,MAAA,WAAA,IAAc;AAAA,IAAG,CAAA;AAAA,EAClC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,cAAA,GAAiB,SAAS,WAAA,KAAgB,IAAA;AAChD,EAAA,MAAM,qBAAA,GAAwB,cAAA,GAAkB,OAAA,CAAS,YAAA,IAAgB,YAAA,GAAgB,YAAA;AACzF,EAAA,MAAM,qBAAA,GAAwB,cAAA,GAAkB,OAAA,CAAS,YAAA,IAAgB,YAAA,GAAgB,YAAA;AACzF,EAAA,MAAM,oBAAA,GAAuB,cAAA,GACvB,OAAA,CAAS,WAAA,IAAkD,WAAA,GAC7D,WAAA;AAEJ,EAAA,MAAM,WAAA,GAAcC,aAAA;AAAA,IAClB,MAAM,qBAAA;AAAA,MACJ,EAAE,GAAG,aAAA,EAAO,YAAA,EAAc,qBAAA,EAAuB,cAAc,qBAAA,EAA2C;AAAA,MAC1G,oBAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA,CAAC,qBAAA,EAAuB,qBAAA,EAAuB,oBAAA,EAAsB,aAAa;AAAA,GACpF;AAEA,EAAAb,gBAAU,MAAM;AACd,IAAA,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,UAAA,GAClB,IAAA,KAAS,SACL,wBAAA,GACA,+BAAA;AAAA,EACR,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,OAAA,GAAUc,8BAAA,EAAa,mBAC3BrB,cAAAA;AAAA,IAACsB,oBAAA;AAAA,IAAA;AAAA,MACC,CAAA,EAAE,OAAA;AAAA,MACF,CAAA,EAAE,OAAA;AAAA,MACF,GAAA,EAAK,IAAA,KAAS,OAAA,GACV,sDAAA,GACA,iJAAA;AAAA,MAEH;AAAA;AAAA,GACH,GAEA,QAAA;AAGF,EAAA,uBACItB,cAAAA,CAACuB,8BAAA,EAAA,EAAoB,MAAA,EAAQ,iBAC3B,QAAA,kBAAAvB,cAAAA,CAACwB,oBAAA,EAAA,EAAgB,KAAA,EAAO,WAAA,EAAa,kBAAA,EAAmB,MAAA,EACtD,QAAA,kBAAAtB,gBAAC,iBAAA,EAAA,EACI,QAAA,EAAA;AAAA,IAAA,OAAA;AAAA,oBAKDF,eAAC,aAAA,EAAA,EAAc;AAAA,GAAA,EACnB,GACF,CAAA,EACF,CAAA;AAEN","file":"chunk-EOSHBZQK.cjs","sourcesContent":["// Shared store for \"in-world admin tools\" — flows where the React admin UI\r\n// releases NUI focus, lets the player do something in the game world, and\r\n// resolves a Promise when they confirm or cancel.\r\n//\r\n// Every tool (capture position, place object, etc.) plugs into the same\r\n// store so AdminOverlays can render at most one InstructionPanel at a time\r\n// and the tool's start() returns a typed Promise. Lua side fires the\r\n// matching SendNUIMessage('<TOOL_ID>_RESULT' / '<TOOL_ID>_CANCELLED'),\r\n// which AdminOverlays routes back to whichever tool is active.\r\nimport { create } from \"zustand\";\r\n\r\nexport type ActiveTool<Result = unknown> = {\r\n /** Tool id matching the SendNUIMessage event names from Lua. */\r\n id: string;\r\n /** Title shown in the InstructionPanel header. */\r\n title: string;\r\n /** Optional sub-text under the title. */\r\n hint?: string;\r\n /** Key bindings shown as labelled key caps. */\r\n keys?: { key: string; action: string }[];\r\n /** Resolve handler for the in-flight Promise. */\r\n resolve: (v: Result | null) => void;\r\n};\r\n\r\ntype AdminToolStore = {\r\n active: ActiveTool | null;\r\n /**\r\n * Begin a tool flow. Returns a Promise resolving to the tool's typed\r\n * result (or null on cancel / replacement). Replaces any currently-active\r\n * tool by cancelling it first — only one in-world prompt at a time.\r\n */\r\n begin: <Result>(spec: Omit<ActiveTool<Result>, \"resolve\">) => Promise<Result | null>;\r\n /** Settle the currently-active tool with a value (called by SendNUIMessage handler). */\r\n resolveActive: (value: unknown) => void;\r\n /** Cancel the currently-active tool (called by SendNUIMessage handler or programmatic cancel). */\r\n cancelActive: () => void;\r\n};\r\n\r\nexport const useAdminToolStore = create<AdminToolStore>((set, get) => ({\r\n active: null,\r\n begin: <Result>(spec: Omit<ActiveTool<Result>, \"resolve\">) =>\r\n new Promise<Result | null>((resolve) => {\r\n const prev = get().active;\r\n if (prev) prev.resolve(null); // bump the previous tool out\r\n set({ active: { ...spec, resolve: resolve as ActiveTool[\"resolve\"] } });\r\n }),\r\n resolveActive: (value) => {\r\n const cur = get().active;\r\n if (!cur) return;\r\n cur.resolve(value);\r\n set({ active: null });\r\n },\r\n cancelActive: () => {\r\n const cur = get().active;\r\n if (!cur) return;\r\n cur.resolve(null);\r\n set({ active: null });\r\n },\r\n}));\r\n","import { alpha, ColorInput, createTheme, MantineTheme, MultiSelect, NumberInput, TextInput } from \"@mantine/core\";\r\n\r\nexport const label = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase', \r\n};\r\nexport const error = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n fontFamily: 'Akrobat Regular',\r\n};\r\nexport const description = {\r\n fontSize: 'var(--mantine-font-size-xs)',\r\n};\r\n\r\nexport const genericInputStyles = {\r\n styles: {\r\n label: label,\r\n error: error,\r\n description: description,\r\n \r\n input:{\r\n background: 'rgba(255,255,255,0.04)',\r\n border: '0.1vh solid rgba(255,255,255,0.08)',\r\n color: 'rgba(255,255,255,0.85)',\r\n minHeight: '4vh',\r\n },\r\n },\r\n};\r\n\r\nconst theme = createTheme({\r\n primaryColor: \"dirk\",\r\n primaryShade: 9,\r\n defaultRadius: \"xs\",\r\n fontFamily: \"Akrobat Regular, sans-serif\",\r\n\r\n radius:{\r\n xxs: '0.3vh',\r\n xs: '0.5vh',\r\n sm: '0.75vh',\r\n md: '1vh',\r\n lg: '1.5vh',\r\n xl: '2vh',\r\n xxl: '3vh',\r\n },\r\n\r\n fontSizes: {\r\n xxs: '1.2vh',\r\n xs: '1.5vh',\r\n sm: '1.8vh',\r\n md: '2.2vh',\r\n lg: '2.8vh',\r\n xl: '3.3vh',\r\n xxl: '3.8vh',\r\n },\r\n\r\n lineHeights: {\r\n xxs: '1.4vh',\r\n xs: '1.8vh',\r\n sm: '2.2vh',\r\n md: '2.8vh',\r\n lg: '3.3vh',\r\n xl: '3.8vh',\r\n },\r\n\r\n spacing:{\r\n xxs: '0.5vh',\r\n xs: '0.75vh',\r\n sm: '1.5vh',\r\n md: '2vh',\r\n lg: '3vh',\r\n xl: '4vh',\r\n xxl: '5vh',\r\n },\r\n\r\n components:{\r\n Progress:{\r\n styles:{\r\n label: {\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase', \r\n },\r\n\r\n root:{\r\n backgroundColor: 'rgba(77, 77, 77, 0.4)',\r\n },\r\n \r\n }\r\n },\r\n \r\n \r\n Input: genericInputStyles,\r\n TextInput: genericInputStyles,\r\n NumberInput: genericInputStyles,\r\n Select: genericInputStyles,\r\n MultiSelect: genericInputStyles,\r\n Textarea: genericInputStyles,\r\n ColorInput: genericInputStyles,\r\n DateInput: genericInputStyles,\r\n\r\n // Mantine's <Button> defaults to rem-based heights (xs ≈ 1.875rem)\r\n // which doesn't match this theme's vh-based input min-heights, so\r\n // `<Button size=\"xs\">` rendered next to `<TextInput size=\"xs\">` ends\r\n // up visibly shorter. Pin the button heights to the same vh values\r\n // the inputs use so xs-everything lines up out of the box.\r\n Button: {\r\n styles: {\r\n label: {\r\n fontFamily: 'Akrobat Bold',\r\n letterSpacing: '0.05em',\r\n textTransform: 'uppercase',\r\n },\r\n root: {\r\n // Mantine maps these to --button-height per size; setting them\r\n // directly here keeps native Button sizing logic intact.\r\n },\r\n },\r\n vars: (_theme: MantineTheme, props: { size?: string }) => {\r\n const heights: Record<string, string> = {\r\n xs: '4vh',\r\n sm: '4.5vh',\r\n md: '5vh',\r\n lg: '5.5vh',\r\n xl: '6vh',\r\n };\r\n const h = heights[props.size ?? 'sm'] ?? '4.5vh';\r\n return {\r\n root: {\r\n '--button-height': h,\r\n },\r\n };\r\n },\r\n },\r\n\r\n Pill: {\r\n styles: (theme: MantineTheme) => ({\r\n root: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n backgroundColor: 'rgba(76, 76, 76, 0.3)',\r\n height: 'fit-content',\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.05em',\r\n fontFamily: 'Akrobat Bold',\r\n fontSize: '1.25vh',\r\n borderRadius: theme.defaultRadius,\r\n paddingBottom: '0.5vh',\r\n paddingTop: '0.5vh',\r\n }\r\n })\r\n },\r\n\r\n // Mantine's <Tooltip> defaults to a white card with black text — looks\r\n // jarring against every dirk consumer's dark configurator. Every script\r\n // used to hand-paste this dark style block per Tooltip; centralised here\r\n // so consumers get the right look automatically and never need to think\r\n // about it again.\r\n Tooltip: {\r\n styles: (theme: MantineTheme) => ({\r\n tooltip: {\r\n background: alpha(theme.colors.dark[7], 0.95),\r\n border: '0.1vh solid rgba(255,255,255,0.1)',\r\n color: 'rgba(255,255,255,0.75)',\r\n fontFamily: 'Akrobat Bold',\r\n fontSize: '1.3vh',\r\n lineHeight: 1.3,\r\n padding: '0.6vh 0.8vh',\r\n letterSpacing: '0.03em',\r\n },\r\n }),\r\n },\r\n\r\n },\r\n\r\n colors: {\r\n\r\n dirk:[\r\n \"#ffffff\",\r\n \"#f3fce9\",\r\n \"#dbf5bd\",\r\n \"#c3ee91\",\r\n \"#ace765\",\r\n \"#94e039\",\r\n \"#7ac61f\",\r\n \"#5f9a18\",\r\n \"#29420a\",\r\n \"#446e11\",\r\n ],\r\n },\r\n});\r\n\r\n\r\nexport default theme;","import type { MantineColorsTuple, MantineThemeOverride } from \"@mantine/core\";\r\n\r\nconst isValidColorScale = (v: unknown): v is MantineColorsTuple =>\r\n Array.isArray(v) && v.length === 10 && v.every((shade) => typeof shade === \"string\");\r\n\r\nexport function mergeMantineThemeSafe(\r\n base: MantineThemeOverride,\r\n custom?: MantineColorsTuple,\r\n override?: MantineThemeOverride\r\n): MantineThemeOverride {\r\n const colors = { ...base.colors };\r\n\r\n if (custom && isValidColorScale(custom)) {\r\n colors[\"custom\"] = custom;\r\n } else if (!colors[\"custom\"]) {\r\n // Always register a \"custom\" entry so primaryColor === \"custom\" never\r\n // points at undefined and silently falls back to Mantine's default\r\n // (which surfaces as \"the configured palette didn't apply\").\r\n const fallback = (base.colors && (base.colors as any).dirk) as MantineColorsTuple | undefined;\r\n if (fallback && isValidColorScale(fallback)) {\r\n colors[\"custom\"] = fallback;\r\n }\r\n }\r\n\r\n return {\r\n ...base,\r\n ...override,\r\n colors: {\r\n ...colors,\r\n ...(override?.colors ?? {}),\r\n },\r\n };\r\n}\r\n","\"use client\";\r\n\r\nimport React from \"react\";\r\nimport { Box, Code, Stack, Text, Title } from \"@mantine/core\";\r\n\r\ntype State = {\r\n error: Error | null;\r\n stack?: string;\r\n};\r\n\r\nexport class DirkErrorBoundary extends React.Component<\r\n { children: React.ReactNode },\r\n State\r\n> {\r\n state: State = { error: null, stack: undefined };\r\n\r\n static getDerivedStateFromError(error: Error) {\r\n // We can't get component stack here, so just store the error\r\n return { error };\r\n }\r\n\r\n componentDidCatch(error: Error, info: React.ErrorInfo) {\r\n console.group(\"🔥 Dirk UI Crash\");\r\n console.error(\"Error:\", error);\r\n console.error(\"Component Stack:\", info.componentStack);\r\n console.groupEnd();\r\n\r\n // Only log here, don't call setState — the error state is already captured\r\n }\r\n\r\n render() {\r\n if (!this.state.error) return this.props.children;\r\n\r\n return (\r\n <Box\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n width: \"100vw\",\r\n height: \"100vh\",\r\n background: \"rgba(10, 10, 12, 0.92)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n padding: \"2rem\",\r\n zIndex: 999999,\r\n }}\r\n >\r\n <Box\r\n maw={900}\r\n w=\"100%\"\r\n p=\"lg\"\r\n style={{\r\n background: \"rgba(20,20,24,0.75)\",\r\n border: \"1px solid rgba(255,255,255,0.08)\",\r\n borderRadius: \"10px\",\r\n boxShadow: \"0 10px 40px rgba(0,0,0,0.6)\",\r\n }}\r\n >\r\n <Stack gap=\"sm\">\r\n <Title order={2} c=\"red.5\">\r\n Dirk UI Crash\r\n </Title>\r\n\r\n <Text c=\"dimmed\" size=\"sm\">\r\n The interface encountered a fatal error and stopped rendering.\r\n </Text>\r\n\r\n <Code block style={{ maxHeight: 150, overflow: \"auto\" }}>\r\n {this.state.error?.message}\r\n </Code>\r\n\r\n {/* Optional: show component stack if you pass it from props */}\r\n <Text size=\"xs\" c=\"dimmed\">\r\n Check console for full stack trace\r\n </Text>\r\n </Stack>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n}","// Internal. Auto-mounted by DirkProvider. Consumers never reference this\r\n// directly — it just exists in the tree so any admin-tool flow has its\r\n// NUI-message routing and admin-UI-hide effect wired up automatically.\r\n//\r\n// What it does (and DOESN'T do anymore):\r\n// • Subscribes to <toolId>_RESULT and <toolId>_CANCELLED NUI messages\r\n// from any admin tool and routes them into useAdminToolStore so\r\n// `await pickDoor()` etc. resolve.\r\n// • While a tool is active, applies a body attribute so the consumer\r\n// admin UI hides itself (so the player sees the world).\r\n//\r\n// What used to live here but doesn't anymore:\r\n// • Rendering the bottom-right InstructionPanel — that's now ALWAYS\r\n// driven by dirk_lib's own NUI via `lib.showInstructions`, so the\r\n// panel renders in one place regardless of where the tool was\r\n// triggered from. Avoids dual code paths and per-resource iframe\r\n// compositing differences that were eating immediate-mode draws.\r\n\r\nimport { useEffect } from \"react\";\r\nimport { useAdminToolStore } from \"./adminToolStore\";\r\n\r\n// Single global listener registration so multiple DirkProviders (if a\r\n// consumer ever stacks them) don't multiply-route the same message into\r\n// the store. Guarded by a module-level flag.\r\nlet listenerInstalled = false;\r\n\r\nfunction installNuiListener() {\r\n if (listenerInstalled || typeof window === \"undefined\") return;\r\n listenerInstalled = true;\r\n window.addEventListener(\"message\", (e) => {\r\n const msg = e?.data;\r\n if (!msg || typeof msg !== \"object\" || typeof msg.action !== \"string\") return;\r\n const action: string = msg.action;\r\n const cur = useAdminToolStore.getState().active;\r\n if (!cur) return;\r\n if (action === `${cur.id}_RESULT`) {\r\n useAdminToolStore.getState().resolveActive(msg.data ?? null);\r\n } else if (action === `${cur.id}_CANCELLED`) {\r\n useAdminToolStore.getState().cancelActive();\r\n }\r\n });\r\n}\r\n\r\n// CSS injected once that hides every body child except things tagged\r\n// with data-dirk-instruction-overlay. Used to make the consumer's\r\n// admin panel disappear so the player sees the world while picking.\r\nconst BODY_HIDE_STYLE_ID = \"dirk-instruction-panel-style\";\r\nconst BODY_HIDE_ATTR = \"data-dirk-instruction-active\";\r\nconst OVERLAY_ATTR = \"data-dirk-instruction-overlay\";\r\n\r\nfunction ensureBodyHideStyle() {\r\n if (typeof document === \"undefined\") return;\r\n if (document.getElementById(BODY_HIDE_STYLE_ID)) return;\r\n const el = document.createElement(\"style\");\r\n el.id = BODY_HIDE_STYLE_ID;\r\n el.textContent = `\r\n body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {\r\n visibility: hidden !important;\r\n opacity: 0 !important;\r\n pointer-events: none !important;\r\n }\r\n `;\r\n document.head.appendChild(el);\r\n}\r\n\r\nexport function AdminOverlays() {\r\n const active = useAdminToolStore((s) => s.active);\r\n\r\n useEffect(() => {\r\n installNuiListener();\r\n }, []);\r\n\r\n // Body-hide effect — toggles the attribute while a tool is in flight\r\n // so the consumer admin UI vanishes and the player sees the world.\r\n useEffect(() => {\r\n if (!active || typeof document === \"undefined\") return;\r\n ensureBodyHideStyle();\r\n document.body.setAttribute(BODY_HIDE_ATTR, \"\");\r\n return () => {\r\n document.body.removeAttribute(BODY_HIDE_ATTR);\r\n };\r\n }, [active]);\r\n\r\n // Nothing visual to render — dirk_lib's NUI handles the bottom-right\r\n // InstructionPanel via lib.showInstructions.\r\n return null;\r\n}\r\n","\"use client\";\r\n\r\nimport \"@mantine/core/styles.css\";\r\nimport \"@mantine/notifications/styles.css\";\r\nimport './styles/fonts.css';\r\nimport './styles/notify.css';\r\nimport './styles/scrollBar.css';\r\nimport './styles/tornEdge.css';\r\n\r\n\r\nimport { library } from \"@fortawesome/fontawesome-svg-core\";\r\nimport { fab } from \"@fortawesome/free-brands-svg-icons\";\r\nimport { far } from \"@fortawesome/free-regular-svg-icons\";\r\nimport { fas } from \"@fortawesome/free-solid-svg-icons\";\r\nlibrary.add(fas, far, fab);\r\n\r\nimport theme from \"@/theme\";\r\nimport { BackgroundImage, MantineColorShade, MantineColorsTuple, MantineProvider } from \"@mantine/core\";\r\nimport { useEffect, useLayoutEffect, useMemo, useState } from \"react\";\r\n\r\nimport { useNuiEvent } from \"@/hooks/useNuiEvent\";\r\nimport { fetchNui, isEnvBrowser } from \"@/utils\";\r\nimport { localeStore } from \"@/utils/locales\";\r\nimport { mergeMantineThemeSafe } from \"@/utils/mergeMantineTheme\";\r\nimport { SettingsState, useSettings } from \"@/utils/useSettings\";\r\nimport { getScriptConfigInstance } from \"@/hooks/useScriptConfig\";\r\nimport { DirkErrorBoundary } from \"./DirkErrorBoundary\";\r\nimport { AdminOverlays } from \"@/components/AdminTools/AdminOverlays\";\r\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\r\n\r\n// Shared QueryClient for every consumer of <DirkProvider>. Hooks in this\r\n// library (usePlayers, ConfigPanel's history pagination, MissingItemsBanner\r\n// audit, etc.) all consume this so consumers don't need to wire their own\r\n// QueryClientProvider. Defaults match what ConfigPanel previously used in\r\n// its private QueryClient so the move doesn't change refetch cadence.\r\n//\r\n// Consumers that already wrap in their own outer QueryClientProvider (e.g.\r\n// dirk_fishing's App.tsx) still work — react-query picks the nearest\r\n// provider in the tree, and \"nearest\" here means this inner one. Anything\r\n// inside DirkProvider uses this client; anything outside uses the consumer's\r\n// outer one (if present).\r\nexport const dirkQueryClient = new QueryClient({\r\n defaultOptions: { queries: { staleTime: 30_000, gcTime: 5 * 60_000 } },\r\n});\r\n\r\nexport type DirkProviderProps = {\r\n children: React.ReactNode;\r\n overideResourceName?: string;\r\n themeOverride?: any;\r\n};\r\n\r\n// Shape DirkProvider looks for at `scriptConfig.theme` to detect a per-resource\r\n// theme override. Consumers opt in by adding a `theme` block to their schema.json\r\n// with these four fields — DirkProvider auto-detects and applies it. Resources\r\n// with no `theme` block (or `useOverride: false`) follow the global dirk_lib\r\n// theme as before. No bridge component, no extra wiring required from consumers.\r\ntype ScriptConfigThemeOverride = {\r\n useOverride?: boolean;\r\n primaryColor?: string;\r\n primaryShade?: number;\r\n customTheme?: MantineColorsTuple | string[];\r\n};\r\n\r\nexport function DirkProvider({ children, overideResourceName, themeOverride }: DirkProviderProps) {\r\n const {\r\n primaryColor,\r\n primaryShade,\r\n customTheme,\r\n game,\r\n } = useSettings();\r\n\r\n // Subscribe to locale changes so the entire tree re-renders when an admin\r\n // updates `language` via dirk_lib's scriptConfig — components calling\r\n // `locale(\"Foo\")` directly (no hook) get fresh strings on the next render.\r\n const _locales = localeStore((s) => s.locales);\r\n void _locales;\r\n\r\n // Per-resource theme override snapshot, read from `scriptConfig.theme` when\r\n // the consumer has a scriptConfig registered. Updated by the subscription\r\n // below; survives Mantine recomputes via useMemo deps.\r\n const [scTheme, setScTheme] = useState<ScriptConfigThemeOverride | null>(null);\r\n\r\n useLayoutEffect(() => {\r\n useSettings.setState({\r\n overideResourceName,\r\n });\r\n }, [overideResourceName]);\r\n\r\n useEffect(() => {\r\n fetchNui('NUI_READY').catch(() => {});\r\n Promise.all([\r\n fetchNui<Partial<SettingsState>>('GET_SETTINGS'),\r\n fetchNui<{ version?: string }>('GET_RESOURCE_VERSION', undefined, { version: 'dev' }),\r\n ]).then(([data, resourceInfo]) => {\r\n useSettings.setState({\r\n ...data,\r\n resourceVersion: resourceInfo?.version || 'dev',\r\n });\r\n }).catch((err) => {\r\n console.error(\"Failed to fetch initial settings within dirk-cfx-react:\", err);\r\n });\r\n }, []);\r\n\r\n // Live updates: dirk_lib broadcasts UPDATE_DIRK_LIB_SETTINGS whenever an admin\r\n // changes appearance/localization via the scriptConfig UI. Merge the partial\r\n // patch into the settings store so theme / currency / branding update live\r\n // without a resource restart. Per-resource theme override is handled below\r\n // via the scriptConfig subscription — pushes here can flow through unchanged.\r\n useNuiEvent<Partial<SettingsState>>('UPDATE_DIRK_LIB_SETTINGS', (data) => {\r\n if (!data || typeof data !== 'object') return;\r\n useSettings.setState(data);\r\n });\r\n\r\n // ── Per-resource theme auto-detect ────────────────────────────────────────\r\n // If the consumer has called createScriptConfig() (i.e. has a registered\r\n // scriptConfig instance) AND their schema has a `theme` block with\r\n // `useOverride: true`, DirkProvider uses those theme values instead of\r\n // dirk_lib's global appearance. Consumers don't have to write any glue\r\n // code — they just add the `theme` block to schema.json and a Theme tab\r\n // (via ThemeOverrideSection) to their configurator. Everything else is\r\n // handled here.\r\n //\r\n // We also forward UPDATE_SCRIPT_CONFIG pushes into the store so this works\r\n // whether or not the consumer remembers to call useScriptConfigHooks().\r\n useNuiEvent<{ config?: Record<string, unknown>; clientVersion?: number }>(\r\n 'UPDATE_SCRIPT_CONFIG',\r\n (data) => {\r\n if (!data || !data.config || typeof data.config !== 'object') return;\r\n try {\r\n const inst = getScriptConfigInstance<Record<string, unknown>>();\r\n inst.store.setState((prev) => ({ ...prev, ...(data.config as Record<string, unknown>) }));\r\n } catch {\r\n // No scriptConfig instance registered — consumer doesn't use scriptConfig, ignore.\r\n }\r\n }\r\n );\r\n\r\n useEffect(() => {\r\n let unsubscribe: (() => void) | undefined;\r\n try {\r\n const inst = getScriptConfigInstance<{ theme?: ScriptConfigThemeOverride }>();\r\n\r\n // Seed from whatever's already in the store (covers cases where the\r\n // store was hydrated before DirkProvider mounted, e.g. by an upstream\r\n // ConfigPanel open).\r\n setScTheme((inst.store.getState() as { theme?: ScriptConfigThemeOverride })?.theme ?? null);\r\n\r\n // Subscribe to store mutations so live edits / hydration completion\r\n // re-trigger the override application.\r\n const subscribable = inst.store as unknown as {\r\n subscribe?: (listener: (s: { theme?: ScriptConfigThemeOverride }) => void) => () => void;\r\n };\r\n if (typeof subscribable.subscribe === 'function') {\r\n unsubscribe = subscribable.subscribe((s) => {\r\n setScTheme(s?.theme ?? null);\r\n });\r\n }\r\n\r\n // Option B: NO proactive GET_FULL_SCRIPT_CONFIG round-trip here. The\r\n // client-visible config (which includes the `theme` block — it is NOT\r\n // server-only) is pushed by dirk_lib via UPDATE_SCRIPT_CONFIG and lands\r\n // in the store. The store `subscribe` above updates `scTheme` the moment\r\n // that push arrives, and the seed `setScTheme(...)` from the current\r\n // store state covers the case where the push beat this mount. This drops\r\n // the per-mount full-config fetch entirely — reopen/reload = store hit,\r\n // 0 bytes. The cold-start NUI-readiness race is handled on the Lua side\r\n // (the push is (re)sent once the NUI signals NUI_READY).\r\n } catch {\r\n // No scriptConfig instance — consumer doesn't use scriptConfig at all.\r\n // Fall through silently; global theme behaviour is unchanged.\r\n }\r\n return () => { unsubscribe?.(); };\r\n }, []);\r\n\r\n const overrideActive = scTheme?.useOverride === true;\r\n const effectivePrimaryColor = overrideActive ? (scTheme!.primaryColor ?? primaryColor) : primaryColor;\r\n const effectivePrimaryShade = overrideActive ? (scTheme!.primaryShade ?? primaryShade) : primaryShade;\r\n const effectiveCustomTheme = overrideActive\r\n ? ((scTheme!.customTheme as MantineColorsTuple | undefined) ?? customTheme)\r\n : customTheme;\r\n\r\n const mergedTheme = useMemo(\r\n () => mergeMantineThemeSafe(\r\n { ...theme, primaryColor: effectivePrimaryColor, primaryShade: effectivePrimaryShade as MantineColorShade },\r\n effectiveCustomTheme,\r\n themeOverride\r\n ),\r\n [effectivePrimaryColor, effectivePrimaryShade, effectiveCustomTheme, themeOverride]\r\n );\r\n\r\n useEffect(() => {\r\n document.body.style.fontFamily =\r\n game === \"rdr3\"\r\n ? '\"Red Dead\", sans-serif'\r\n : '\"Akrobat Regular\", sans-serif';\r\n }, [game]);\r\n\r\n const content = isEnvBrowser() ? (\r\n <BackgroundImage\r\n w=\"100vw\"\r\n h=\"100vh\"\r\n src={game === \"fivem\"\r\n ? \"https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg\"\r\n : \"https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg\"}\r\n >\r\n {children}\r\n </BackgroundImage>\r\n ) : (\r\n children\r\n );\r\n\r\n return (\r\n <QueryClientProvider client={dirkQueryClient}>\r\n <MantineProvider theme={mergedTheme} defaultColorScheme=\"dark\">\r\n <DirkErrorBoundary>\r\n {content}\r\n {/* Auto-mounted. Renders nothing unless an admin tool is active.\r\n Consumers never have to wire this up themselves — every admin\r\n tool flow that uses useAdminToolStore.begin() gets the\r\n bottom-right InstructionPanel + NUI message routing for free. */}\r\n <AdminOverlays />\r\n </DirkErrorBoundary>\r\n </MantineProvider>\r\n </QueryClientProvider>\r\n );\r\n}\r\n"]}
|