baselode 0.1.22 → 0.1.24

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.
@@ -1 +1 @@
1
- {"version":3,"file":"baselode.js","sources":["../src/data/keying.js","../src/data/assayFieldSets.js","../src/data/assayLoader.js","../src/data/csvRowUtils.js","../src/data/traceGridConfig.js","../src/data/assayDataLoader.js","../src/data/desurvey.js","../src/data/desurveyMethods.js","../src/data/drillholeLoader.js","../src/data/datasetLoader.js","../src/data/structuralLoader.js","../src/data/unifiedLoader.js","../src/data/intercepts.js","../src/data/geophysicsLoader.js","../src/data/lasLoader.js","../src/viz/structuralViz.js","../src/viz/tracePlotState.js","../src/viz/TracePlot.jsx","../src/viz/useDrillholeTraceGrid.jsx","../src/viz/view2d.js","../src/viz/view3dPayload.js","../src/viz/Baselode3DControls.jsx","../src/viz/BlockModelWidget.jsx","../src/viz/corePhotoViz.js","../src/viz/CorePhotoTable.jsx","../src/viz/CorePhotoViewer.jsx","../src/grade_blocks/gradeBlockLoader.js"],"sourcesContent":["/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { _COLUMN_LOOKUP, DEFAULT_COLUMN_MAP, HOLE_ID } from './datamodel.js';\n\n/**\n * Normalize a field name to lowercase with underscores\n * @param {string} name - The field name to normalize\n * @returns {string} - The normalized field name\n */\nexport function normalizeFieldName(name) {\n return (name || '').toString().trim().toLowerCase().replace(/\\s+/g, '_');\n}\n\n/**\n * Standardize column names in a row object using the baselode data model\n * @param {Object} row - The row object with arbitrary column names\n * @param {Object} [columnMap] - Optional column map to use (defaults to DEFAULT_COLUMN_MAP)\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Object} - The row object with standardized column names\n */\nexport function standardizeColumns(row, columnMap = null, sourceColumnMap = null) {\n const lookup = { ..._COLUMN_LOOKUP };\n \n // Add user-provided column mappings\n if (sourceColumnMap) {\n for (const [rawName, expectedName] of Object.entries(sourceColumnMap)) {\n if (rawName != null && expectedName != null) {\n const normalizedKey = normalizeFieldName(rawName);\n const normalizedValue = normalizeFieldName(expectedName);\n lookup[normalizedKey] = normalizedValue;\n }\n }\n }\n\n const renamed = {};\n for (const [col, value] of Object.entries(row)) {\n const key = normalizeFieldName(col);\n const mapped = lookup[key] || key;\n renamed[mapped] = value;\n }\n\n return renamed;\n}\n\n/**\n * Standardize an array of rows (e.g., from CSV parsing)\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map to use\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeRowArray(rows, columnMap = null, sourceColumnMap = null) {\n return rows.map(row => standardizeColumns(row, columnMap, sourceColumnMap));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport const ASSAY_NON_VALUE_FIELDS = new Set([\n 'hole_id',\n 'holeid',\n 'id',\n 'holeId',\n 'project_code',\n 'project',\n 'latitude',\n 'longitude',\n 'lat',\n 'lng',\n 'elevation',\n 'dip',\n 'azimuth',\n 'holetype',\n 'shape',\n 'anumber',\n 'collarid',\n 'companyholeid',\n 'company_hole_id',\n 'samp_from',\n 'samp_to',\n 'sample_from',\n 'sample_to',\n 'from',\n 'to',\n 'depth_from',\n 'depth_to',\n 'fromdepth',\n 'todepth',\n 'comment',\n 'z'\n]);","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, PROJECT_ID } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to use standardized column names\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Extract hole ID from a normalized row\n * @private\n * @param {Object} row - Normalized row object\n * @returns {{holeId: string}} Object containing hole ID\n */\nfunction extractIdFields(row) {\n const holeId = row[HOLE_ID];\n return { holeId };\n}\n\n/**\n * Extract and validate assay interval data from a row\n * @private\n * @param {Object} row - Normalized row object\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object|null} Interval object or null if invalid\n */\nfunction extractInterval(row, sourceColumnMap = null) {\n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n if (!holeId) return null;\n\n const project = row[PROJECT_ID] || row.project || row.project_code;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return null;\n\n return {\n holeId,\n project,\n from,\n to,\n ...row\n };\n}\n\n/**\n * Convert array of intervals to a hole object with points\n * @private\n * @param {string} holeId - Hole identifier\n * @param {Array<Object>} intervals - Array of interval objects\n * @returns {{id: string, project: string, points: Array<Object>}} Hole object\n */\nfunction intervalsToHole(holeId, intervals) {\n const sorted = intervals.sort((a, b) => a.from - b.from);\n const points = [];\n sorted.forEach((iv) => {\n const { from, to, project, ...rest } = iv;\n const pointData = {\n z: from,\n from,\n to,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n ...rest\n };\n points.push(pointData);\n points.push({ ...pointData, z: to });\n });\n return { id: holeId, project: sorted[0]?.project, points };\n}\n\n/**\n * Parse assay CSV to extract unique hole IDs (quick pass, no interval data)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<string>>} Array of unique hole IDs\n */\nexport function parseAssayHoleIds(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const holeIds = new Set();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const hid = row[HOLE_ID];\n if (hid !== undefined && `${hid}`.trim() !== '') {\n holeIds.add(`${hid}`.trim());\n }\n },\n complete: () => resolve(Array.from(holeIds)),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIds', error))\n });\n });\n}\n\n/**\n * Check if a row has at least one non-null assay value\n * @private\n * @param {Object} row - Normalized row object\n * @returns {boolean} True if row contains assay data\n */\nfunction hasAssayValue(row) {\n return Object.entries(row || {}).some(([k, v]) => {\n if (ASSAY_NON_VALUE_FIELDS.has(k)) return false;\n if (v === undefined || v === null) return false;\n if (typeof v === 'string' && v.trim() === '') return false;\n return true;\n });\n}\n\n/**\n * Parse assay CSV to extract hole IDs that have at least one assay value (quick pass)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<{holeId: string}>>} Array of objects with hole IDs that have assay data\n */\nexport function parseAssayHoleIdsWithAssays(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const byHole = new Map();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n if (!hasAssayValue(row)) return;\n const ids = extractIdFields(row);\n const hid = ids.holeId;\n if (hid !== undefined && `${hid}`.trim() !== '') {\n const key = `${hid}`.trim();\n if (!byHole.has(key)) {\n byHole.set(key, {\n holeId: key\n });\n }\n }\n },\n complete: () => resolve(Array.from(byHole.values())),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIdsWithAssays', error))\n });\n });\n}\n\n/**\n * Parse assay CSV for a single hole's intervals\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to extract\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object|null>} Hole object with intervals or null if not found\n */\nexport function parseAssayHole(file, holeId, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const wanted = `${holeId}`.trim();\n if (!wanted) {\n reject(withDataErrorContext('parseAssayHole', new Error('Missing hole id')));\n return;\n }\n const intervals = [];\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (`${interval.holeId}`.trim() !== wanted) return;\n intervals.push(interval);\n },\n complete: () => {\n if (!intervals.length) {\n resolve(null);\n return;\n }\n const hole = intervalsToHole(wanted, intervals);\n resolve(hole);\n },\n error: (error) => reject(withDataErrorContext('parseAssayHole', error))\n });\n });\n}\n\n/**\n * Parse complete assay CSV file with all holes and intervals (eager load)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<{holes: Array<Object>}>} Object containing array of all holes with intervals\n */\nexport function parseAssaysCSV(file, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow) => {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (!byHole.has(interval.holeId)) byHole.set(interval.holeId, []);\n byHole.get(interval.holeId).push(interval);\n });\n\n const holes = Array.from(byHole.entries()).map(([hid, intervals]) => intervalsToHole(hid, intervals));\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseAssaysCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { normalizeFieldName, standardizeColumns } from './keying.js';\n\n/**\n * Normalize a CSV row by converting all keys to lowercase with underscores\n * @deprecated Use standardizeColumns from keying.js instead for full data model support\n */\nexport function normalizeCsvRow(row = {}) {\n const normalized = {};\n Object.entries(row || {}).forEach(([key, value]) => {\n if (!key) return;\n normalized[normalizeFieldName(key)] = value;\n });\n return normalized;\n}\n\n/**\n * Pick the first present value from a list of keys in a normalized row\n * @param {Object} normalized - The normalized row object\n * @param {Array<string>} keys - Array of keys to check\n * @param {*} fallback - Fallback value if none found\n * @returns {*} - The first present value or fallback\n */\nexport function pickFirstPresent(normalized = {}, keys = [], fallback) {\n for (const key of keys) {\n const value = normalized[key];\n if (value !== undefined && value !== null && `${value}`.trim() !== '') {\n return value;\n }\n }\n return fallback;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default number of trace plots in a grid */\nexport const DEFAULT_TRACE_PLOT_COUNT = 4;\n\n/**\n * Reorder hole IDs to place a focused hole first\n * @param {Array<string>} ids - Array of hole IDs\n * @param {string} focusId - Hole ID to place first\n * @returns {Array<string>} Reordered array with focusId first if found\n */\nexport function reorderHoleIds(ids = [], focusId = '') {\n if (!ids.length) return [];\n if (!focusId) return ids;\n const matchIdx = ids.findIndex((id) => id === focusId);\n if (matchIdx === -1) return ids;\n const selected = ids[matchIdx];\n const rest = ids.filter((_, idx) => idx !== matchIdx);\n return [selected, ...rest];\n}\n\n/**\n * Determine appropriate chart type for a property.\n * Comment columns always use 'comment'; categorical → 'categorical'; numeric → requested or default.\n *\n * @param {Object} options - Configuration options\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Requested chart type\n * @param {Array<string>} options.categoricalProps - List of categorical property names\n * @param {Array<string>} options.commentProps - List of comment property names\n * @param {string} options.numericDefaultChartType - Default chart type for numeric properties\n * @returns {string} Coerced chart type ('comment', 'categorical', 'line', 'markers+line', etc.)\n */\nexport function coerceChartTypeForProperty({\n property = '',\n chartType = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n if (!property) return chartType || numericDefaultChartType;\n if (commentProps.includes(property)) return 'comment';\n if (categoricalProps.includes(property)) return 'categorical';\n if (property === 'dip') return 'tadpole';\n if (!chartType || chartType === 'categorical' || chartType === 'comment' || chartType === 'tadpole') return numericDefaultChartType;\n return chartType;\n}\n\n/**\n * Build trace plot configuration objects for a grid of hole IDs\n * @param {Object} options - Configuration options\n * @param {Array<string>} options.holeIds - Array of hole IDs\n * @param {string} options.focusedHoleId - Hole ID to focus (place first)\n * @param {number} options.plotCount - Number of plots in grid\n * @param {string} options.defaultProp - Default property to display\n * @param {Array<string>} options.categoricalProps - List of categorical properties\n * @param {Array<string>} options.commentProps - List of comment properties\n * @param {string} options.numericDefaultChartType - Default chart type for numeric props\n * @returns {Array<{holeId: string, property: string, chartType: string}>} Array of trace configurations\n */\nexport function buildTraceConfigsForHoleIds({\n holeIds = [],\n focusedHoleId = '',\n plotCount = DEFAULT_TRACE_PLOT_COUNT,\n defaultProp = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n const ordered = reorderHoleIds(holeIds, focusedHoleId);\n return Array.from({ length: plotCount }).map((_, idx) => {\n const holeId = ordered[idx] || holeIds[idx] || '';\n const chartType = coerceChartTypeForProperty({\n property: defaultProp,\n chartType: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType\n });\n return {\n holeId,\n property: defaultProp,\n chartType\n };\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { parseAssayHole, parseAssayHoleIdsWithAssays, parseAssaysCSV } from './assayLoader.js';\nimport { buildTraceConfigsForHoleIds, reorderHoleIds } from './traceGridConfig.js';\nimport { classifyColumns } from './columnMeta.js';\n\nexport { reorderHoleIds };\n\n/**\n * Derive property names from hole data using column display-type classification.\n *\n * @param {Array<Object>} holes - Array of hole objects with points containing assay data\n * @returns {{\n * numericProps: string[],\n * categoricalProps: string[],\n * commentProps: string[],\n * columnMeta: Object,\n * defaultProp: string\n * }} Property classification\n */\nexport function deriveAssayProps(holes = []) {\n const points = holes.flatMap((h) => h.points || []);\n const { numericCols, categoricalCols, commentCols, byType } = classifyColumns(points);\n\n const defaultProp = numericCols[0] || categoricalCols[0] || '';\n\n return {\n numericProps: numericCols,\n categoricalProps: categoricalCols,\n commentProps: commentCols,\n columnMeta: byType,\n defaultProp,\n };\n}\n\n/**\n * Load metadata (hole IDs) from an assay CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Array<{holeId: string}>>} Array of hole IDs with assay data\n */\nexport async function loadAssayMetadata(file, config = null) {\n const holeIds = await parseAssayHoleIdsWithAssays(file);\n return holeIds;\n}\n\n/**\n * Load assay intervals for a specific hole from CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to load\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Object|null>} Hole object with assay intervals or null if not found\n */\nexport async function loadAssayHole(file, holeId, config = null) {\n const hole = await parseAssayHole(file, holeId);\n return hole;\n}\n\n/**\n * Build complete assay state from hole data including property analysis and trace configs\n * @param {Array<Object>} holes - Array of hole objects with assay data\n * @param {string} focusedHoleId - Hole ID to focus on (will be first in trace configs)\n * @returns {Object|null} Assay state object with holes, properties, and trace configs\n */\nexport function buildAssayState(holes = [], focusedHoleId = '') {\n if (!holes.length) return null;\n const { numericProps, categoricalProps, commentProps, columnMeta, defaultProp } = deriveAssayProps(holes);\n const holeIds = holes.map((h) => h.id || h.holeId).filter(Boolean);\n const traceConfigs = buildTraceConfigsForHoleIds({\n holeIds,\n focusedHoleId,\n plotCount: 4,\n defaultProp,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'line'\n });\n return {\n holes,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n defaultProp,\n traceConfigs\n };\n}\n\n/**\n * Load and parse complete assay CSV file into state object\n * @param {File|Blob} file - Assay CSV file\n * @param {string} focusedHoleId - Hole ID to focus on\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object>} Complete assay state\n * @throws {Error} If no valid assay intervals found\n */\nexport async function loadAssayFile(file, focusedHoleId = '', sourceColumnMap = null) {\n const { holes } = await parseAssaysCSV(file, sourceColumnMap);\n const state = buildAssayState(holes, focusedHoleId);\n if (!state) throw new Error('No valid assay intervals found.');\n return state;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, LATITUDE, LONGITUDE, AZIMUTH, DIP, DEPTH, PROJECT_ID } from './datamodel.js';\n\n/**\n * Parse survey CSV file containing downhole survey measurements\n * Expected columns: hole_id, depth, azimuth, dip\n * @param {File|Blob} file - Survey CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<Object>>} Array of normalized survey rows\n */\nexport function parseSurveyCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = results.data\n .map((row) => normalizeRow(row, sourceColumnMap))\n .filter((row) => row[HOLE_ID] && Number.isFinite(row[DEPTH]) && Number.isFinite(row[DIP]) && Number.isFinite(row[AZIMUTH]));\n resolve(rows);\n },\n error: (err) => reject(withDataErrorContext('parseSurveyCSV', err))\n });\n });\n}\n\n/**\n * Normalize a survey row to standardized field names\n * @private\n * @param {Object} row - Raw survey row\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object} Normalized row with standardized field names\n */\nfunction normalizeRow(row, sourceColumnMap = null) {\n const norm = standardizeColumns(row, null, sourceColumnMap);\n\n const holeId = norm[HOLE_ID];\n const project = norm[PROJECT_ID] || norm.project || norm.project_code;\n const lat = toNumber(norm[LATITUDE]);\n const lng = toNumber(norm[LONGITUDE]);\n const surveyDepth = toNumber(norm[DEPTH]);\n const dip = toNumber(norm[DIP]);\n const azimuth = toNumber(norm[AZIMUTH]);\n const maxDepth = toNumber(norm.maxdepth);\n\n return {\n raw: norm,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n [LATITUDE]: lat,\n [LONGITUDE]: lng,\n [DEPTH]: surveyDepth,\n [DIP]: dip,\n [AZIMUTH]: azimuth,\n maxdepth: maxDepth,\n // Legacy field names for backwards compatibility\n project_code: project,\n latitude: lat,\n longitude: lng,\n surveydepth: surveyDepth\n };\n}\n\n/**\n * Convert value to number, returning undefined if not finite\n * @private\n * @param {*} v - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nconst toNumber = (v) => {\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/**\n * Desurvey drillhole traces using minimum curvature method\n * Converts survey measurements (azimuth, dip, depth) to 3D coordinates (x, y, z)\n * @param {Array<Object>} collars - Array of collar objects with location data\n * @param {Array<Object>} surveys - Array of survey measurement objects\n * @returns {Array<{id: string, project: string, points: Array<{x: number, y: number, z: number, md: number, azimuth: number, dip: number, lat: number, lng: number}>, collar: Object}>} Array of desurveyed holes with 3D points\n */\nexport function desurveyTraces(collars, surveys) {\n const collarByKey = new Map();\n collars.forEach((c) => {\n const holeId = (c[HOLE_ID] || c.holeId || c.id || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!collarByKey.has(key)) {\n collarByKey.set(key, c);\n }\n });\n\n const refLat = collars[0]?.lat ?? collars[0]?.[LATITUDE] ?? 0;\n const refLng = collars[0]?.lng ?? collars[0]?.[LONGITUDE] ?? 0;\n const refMetersPerDegLat = 111132;\n const refMetersPerDegLon = 111320 * Math.cos((refLat * Math.PI) / 180);\n\n const grouped = new Map();\n surveys.forEach((s) => {\n const holeId = (s[HOLE_ID] || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(s);\n });\n\n const holes = [];\n grouped.forEach((stations, key) => {\n const collar = collarByKey.get(key);\n if (!collar) return;\n const sorted = stations\n .filter((s) => Number.isFinite(s[DEPTH] ?? s.surveydepth))\n .sort((a, b) => (a[DEPTH] ?? a.surveydepth) - (b[DEPTH] ?? b.surveydepth));\n if (!sorted.length) return;\n\n const lat0 = collar.lat ?? collar[LATITUDE];\n const lng0 = collar.lng ?? collar[LONGITUDE];\n const metersPerDegLat = 111132;\n const metersPerDegLon = 111320 * Math.cos((lat0 * Math.PI) / 180);\n const baseX = (lng0 - refLng) * refMetersPerDegLon;\n const baseY = (lat0 - refLat) * refMetersPerDegLat;\n\n const points = [];\n let accX = 0;\n let accY = 0;\n let accZ = 0;\n\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const prev = sorted[i - 1];\n const currDepth = curr[DEPTH] ?? curr.surveydepth;\n const currAzimuth = curr[AZIMUTH] ?? curr.azimuth;\n const currDip = curr[DIP] ?? curr.dip;\n \n if (!prev) {\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: 0,\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n continue;\n }\n\n const prevDepth = prev[DEPTH] ?? prev.surveydepth;\n const prevAzimuth = prev[AZIMUTH] ?? prev.azimuth;\n const prevDip = prev[DIP] ?? prev.dip;\n \n const deltaMD = currDepth - prevDepth;\n if (deltaMD <= 0) continue;\n\n const inc1 = toInclination(prevDip);\n const inc2 = toInclination(currDip);\n const az1 = degToRad(prevAzimuth);\n const az2 = degToRad(currAzimuth);\n\n const beta = Math.acos(\n Math.sin(inc1) * Math.sin(inc2) * Math.cos(az1 - az2) +\n Math.cos(inc1) * Math.cos(inc2)\n );\n\n const rf = beta > 1e-6 ? (2 / beta) * Math.tan(beta / 2) : 1;\n\n const dx = 0.5 * deltaMD * (Math.sin(inc1) * Math.cos(az1) + Math.sin(inc2) * Math.cos(az2)) * rf;\n const dy = 0.5 * deltaMD * (Math.sin(inc1) * Math.sin(az1) + Math.sin(inc2) * Math.sin(az2)) * rf;\n const dzDown = 0.5 * deltaMD * (Math.cos(inc1) + Math.cos(inc2)) * rf; // down is positive\n\n accX += dx;\n accY += dy;\n accZ += dzDown;\n\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: -accZ, // render with z up; depth down\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n }\n\n // convert local meters back to approx lat/lon for map overlay if needed\n const pointsWithGeo = points.map((p) => ({\n ...p,\n lat: lat0 + (p.y / metersPerDegLat),\n lng: lng0 + (p.x / metersPerDegLon)\n }));\n\n holes.push({\n id: collar[HOLE_ID] || collar.holeId || key,\n project: collar[PROJECT_ID] || collar.project_id || collar.project || '',\n points: pointsWithGeo,\n collar\n });\n });\n\n return holes;\n}\n\n/**\n * Convert degrees to radians\n * @private\n * @param {number} d - Degrees\n * @returns {number} Radians\n */\nconst degToRad = (d) => (d * Math.PI) / 180;\n\n/**\n * Convert dip (negative downward from horizontal) to inclination from vertical\n * @private\n * @param {number} dipDeg - Dip in degrees (negative = downward)\n * @returns {number} Inclination in radians from vertical (0 = vertical down)\n */\nconst toInclination = (dipDeg) => {\n const val = Number(dipDeg);\n const incDeg = 90 + (Number.isFinite(val) ? val : 0); // dip -60 => inc 30\n const clamped = Math.min(180, Math.max(0, incDeg));\n return degToRad(clamped);\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { withDataErrorContext } from './dataErrorUtils.js';\n/**\n * Convert value to number with optional fallback\n * @private\n */function toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Normalize hole ID value to trimmed string\n * @private\n */\nfunction normalizeHoleIdValue(value) {\n if (value === undefined || value === null) return '';\n return `${value}`.trim();\n}\n\n/**\n * Canonicalize hole ID column across rows with varying column names\n * @private\n */\nfunction canonicalizeHoleIdRows(rows = [], holeIdCol = null) {\n const preferred = holeIdCol || 'hole_id';\n const candidates = [preferred, 'hole_id', 'holeId', 'id'];\n const resolved = candidates.find((col) => rows.some((row) => normalizeHoleIdValue(row?.[col])));\n if (!resolved) {\n throw withDataErrorContext('canonicalizeHoleIdRows', new Error(`hole id column '${preferred}' not found`));\n }\n return {\n aliasCol: resolved,\n rows: rows.map((row) => ({\n ...row,\n hole_id: normalizeHoleIdValue(row?.[resolved])\n }))\n };\n}\n\n/**\n * Convert degrees to radians\n * @private\n */\nfunction degToRad(angle) {\n return (Number(angle) * Math.PI) / 180;\n}\n\n/**\n * Calculate direction cosines from azimuth and dip\n * @private\n */\nfunction directionCosines(azimuth, dip) {\n const azRad = degToRad(azimuth);\n const dipRad = degToRad(dip);\n const ca = Math.cos(dipRad) * Math.sin(azRad);\n const cb = Math.cos(dipRad) * Math.cos(azRad);\n const cc = Math.sin(dipRad) * -1;\n return { ca, cb, cc };\n}\n\n/**\n * Calculate displacement vector for a survey segment using specified method\n * @private\n */\nfunction segmentDisplacement(deltaMd, az0, dip0, az1, dip1, method = 'minimum_curvature') {\n const dc0 = directionCosines(az0, dip0);\n const dc1 = directionCosines(az1, dip1);\n\n if (method === 'tangential') {\n return {\n dx: deltaMd * dc0.ca,\n dy: deltaMd * dc0.cb,\n dz: deltaMd * dc0.cc,\n azimuth: az0,\n dip: dip0\n };\n }\n\n if (method === 'balanced_tangential') {\n const azAvg = 0.5 * (az0 + az1);\n const dipAvg = 0.5 * (dip0 + dip1);\n const dcAvg = directionCosines(azAvg, dipAvg);\n return {\n dx: deltaMd * dcAvg.ca,\n dy: deltaMd * dcAvg.cb,\n dz: deltaMd * dcAvg.cc,\n azimuth: azAvg,\n dip: dipAvg\n };\n }\n\n const dot = (dc0.ca * dc1.ca) + (dc0.cb * dc1.cb) + (dc0.cc * dc1.cc);\n const dogleg = Math.acos(Math.max(-1, Math.min(1, dot)));\n const rf = dogleg > 1e-6 ? (2 * Math.tan(dogleg / 2)) / dogleg : 1;\n\n return {\n dx: 0.5 * deltaMd * (dc0.ca + dc1.ca) * rf,\n dy: 0.5 * deltaMd * (dc0.cb + dc1.cb) * rf,\n dz: 0.5 * deltaMd * (dc0.cc + dc1.cc) * rf,\n azimuth: az1,\n dip: dip1\n };\n}\n\nfunction desurvey(rowsCollars = [], rowsSurveys = [], options = {}) {\n const {\n step = 1,\n holeIdCol = null,\n method = 'minimum_curvature'\n } = options;\n\n const safeStep = Number.isFinite(Number(step)) && Number(step) > 0 ? Number(step) : 1;\n\n const collarsCanonical = canonicalizeHoleIdRows(rowsCollars, holeIdCol);\n const surveysCanonical = canonicalizeHoleIdRows(rowsSurveys, holeIdCol || collarsCanonical.aliasCol);\n\n if (!collarsCanonical.rows.length || !surveysCanonical.rows.length) return [];\n\n const collarsByHole = new Map();\n collarsCanonical.rows.forEach((row) => {\n if (!row.hole_id || collarsByHole.has(row.hole_id)) return;\n collarsByHole.set(row.hole_id, row);\n });\n\n const surveysByHole = new Map();\n surveysCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!surveysByHole.has(row.hole_id)) surveysByHole.set(row.hole_id, []);\n surveysByHole.get(row.hole_id).push(row);\n });\n\n const out = [];\n surveysByHole.forEach((stations, holeId) => {\n const collar = collarsByHole.get(holeId);\n if (!collar) return;\n\n const sorted = [...stations]\n .map((row) => ({\n ...row,\n from: toNumber(row.from),\n azimuth: toNumber(row.azimuth),\n dip: toNumber(row.dip)\n }))\n .filter((row) => Number.isFinite(row.from) && Number.isFinite(row.azimuth) && Number.isFinite(row.dip))\n .sort((a, b) => a.from - b.from);\n\n if (!sorted.length) return;\n\n let x = toNumber(collar.x, 0);\n let y = toNumber(collar.y, 0);\n let z = toNumber(collar.z, 0);\n let mdCursor = sorted[0].from;\n const azPrev = sorted[0].azimuth;\n const dipPrev = sorted[0].dip;\n\n const firstRecord = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: azPrev,\n dip: dipPrev\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n firstRecord[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(firstRecord);\n\n for (let idx = 0; idx < sorted.length - 1; idx += 1) {\n const s0 = sorted[idx];\n const s1 = sorted[idx + 1];\n const md0 = s0.from;\n const md1 = s1.from;\n const deltaMd = md1 - md0;\n if (deltaMd <= 0) continue;\n\n const segmentSteps = Math.max(1, Math.ceil(deltaMd / safeStep));\n const mdIncrement = deltaMd / segmentSteps;\n\n for (let stepIdx = 0; stepIdx < segmentSteps; stepIdx += 1) {\n mdCursor += mdIncrement;\n const weight = (mdCursor - md0) / deltaMd;\n const azInterp = s0.azimuth + weight * (s1.azimuth - s0.azimuth);\n const dipInterp = s0.dip + weight * (s1.dip - s0.dip);\n\n const disp = segmentDisplacement(mdIncrement, s0.azimuth, s0.dip, s1.azimuth, s1.dip, method);\n x += disp.dx;\n y += disp.dy;\n z += disp.dz;\n\n const record = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: method === 'minimum_curvature' ? azInterp : disp.azimuth,\n dip: method === 'minimum_curvature' ? dipInterp : disp.dip\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n record[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(record);\n }\n }\n });\n\n return out;\n}\n\n/**\n * Desurvey drillholes using minimum curvature method\n * @param {Array<Object>} collars - Collar data with hole_id, lat, lng\n * @param {Array<Object>} surveys - Survey data with hole_id, depth, azimuth, dip\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points with x, y, z, md coordinates\n */\nexport function minimumCurvatureDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'minimum_curvature' });\n}\n\n/**\n * Desurvey drillholes using tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function tangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'tangential' });\n}\n\n/**\n * Desurvey drillholes using balanced tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function balancedTangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'balanced_tangential' });\n}\n\n/**\n * Build desurveyed traces using minimum curvature method (alias for minimumCurvatureDesurvey)\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function buildTraces(collars, surveys, options = {}) {\n return minimumCurvatureDesurvey(collars, surveys, options);\n}\n\n/**\n * Find nearest trace point by measured depth\n * @private\n */\nfunction nearestByMeasuredDepth(traceRows, midMd) {\n if (!traceRows.length || !Number.isFinite(midMd)) return null;\n let best = null;\n let bestDist = Infinity;\n for (let i = 0; i < traceRows.length; i += 1) {\n const row = traceRows[i];\n const md = toNumber(row.md);\n if (!Number.isFinite(md)) continue;\n const dist = Math.abs(md - midMd);\n if (dist < bestDist) {\n bestDist = dist;\n best = row;\n }\n }\n return best;\n}\n\n/**\n * Attach 3D position data from desurveyed traces to assay intervals\n * Finds nearest trace point by mid-depth of each assay interval\n * @param {Array<Object>} assays - Assay data with hole_id, from, to\n * @param {Array<Object>} traces - Desurveyed trace data with hole_id, md, x, y, z\n * @param {Object} options - Options\n * @param {string} options.holeIdCol - Hole ID column name (default: 'hole_id')\n * @returns {Array<Object>} Assay rows enriched with x, y, z, md, azimuth, dip from nearest trace point\n */\nexport function attachAssayPositions(assays = [], traces = [], options = {}) {\n const holeIdCol = options.holeIdCol || 'hole_id';\n const assaysCanonical = canonicalizeHoleIdRows(assays, holeIdCol);\n const tracesCanonical = canonicalizeHoleIdRows(traces, holeIdCol);\n\n if (!assaysCanonical.rows.length || !tracesCanonical.rows.length) return [...assaysCanonical.rows];\n\n const tracesByHole = new Map();\n tracesCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!tracesByHole.has(row.hole_id)) tracesByHole.set(row.hole_id, []);\n tracesByHole.get(row.hole_id).push(row);\n });\n tracesByHole.forEach((rows, holeId) => {\n tracesByHole.set(holeId, [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0)));\n });\n\n return assaysCanonical.rows.map((assay) => {\n const from = toNumber(assay.from);\n const to = toNumber(assay.to);\n const midMd = Number.isFinite(from) && Number.isFinite(to) ? 0.5 * (from + to) : undefined;\n if (!assay.hole_id || !Number.isFinite(midMd)) return { ...assay };\n\n const nearest = nearestByMeasuredDepth(tracesByHole.get(assay.hole_id) || [], midMd);\n if (!nearest) return { ...assay };\n\n const merged = { ...assay };\n ['md', 'x', 'y', 'z', 'azimuth', 'dip'].forEach((key) => {\n if (nearest[key] === undefined) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = nearest[key];\n } else {\n merged[key] = nearest[key];\n }\n });\n return merged;\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, EASTING, NORTHING, ELEVATION } from './datamodel.js';\n\n/**\n * Parse drillholes CSV with desurveyed trace points\n * Expect CSV columns: hole_id, x (easting), y (northing), z (elevation), order (optional) plus any attributes per point\n * @param {File|Blob|string} file - CSV file or data\n * @param {Object} [sourceColumnMap] - Optional user-provided column mappings\n * @returns {Promise<{holes: Array}>} - Parsed drillhole data\n */\nexport function parseDrillholesCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow, idx) => {\n const row = standardizeColumns(rawRow, null, sourceColumnMap);\n \n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n const x = row[EASTING] ?? row.x;\n const y = row[NORTHING] ?? row.y;\n const z = row[ELEVATION] ?? row.z;\n const order = row.order ?? idx;\n\n if (!holeId || x === null || x === undefined || y === null || y === undefined || z === null || z === undefined) return;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({\n ...row,\n holeId,\n order,\n x: Number(x) ?? 0,\n y: Number(y) ?? 0,\n z: Number(z) ?? 0\n });\n });\n\n const holes = Array.from(byHole.entries()).map(([holeId, pts]) => ({\n id: holeId,\n points: pts\n .sort((a, b) => a.order - b.order)\n .map((p) => ({\n ...p,\n x: Number(p.x) || 0,\n y: Number(p.y) || 0,\n z: Number(p.z) || 0\n }))\n }));\n\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseDrillholesCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { standardizeColumns as standardizeRow } from './keying.js';\nimport {\n HOLE_ID,\n LATITUDE,\n LONGITUDE,\n ELEVATION,\n AZIMUTH,\n DIP,\n FROM,\n TO,\n MID,\n PROJECT_ID,\n EASTING,\n NORTHING,\n CRS,\n DEPTH,\n GEOLOGY_CODE,\n GEOLOGY_DESCRIPTION,\n BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY,\n BASELODE_DATA_MODEL_DRILL_GEOLOGY\n} from './datamodel.js';\n\n// Re-export for backwards compatibility\nexport { DEFAULT_COLUMN_MAP } from './datamodel.js';\n\n/**\n * Convert source to array\n * @private\n */\nfunction toArray(source) {\n if (!source) return [];\n if (Array.isArray(source)) return [...source];\n return [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Sort rows by multiple columns\n * @private\n */\nfunction sortByColumns(rows = [], columns = []) {\n const out = [...rows];\n out.sort((a, b) => {\n for (let i = 0; i < columns.length; i += 1) {\n const col = columns[i];\n const av = a?.[col];\n const bv = b?.[col];\n if (av === bv) continue;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return `${av}`.localeCompare(`${bv}`);\n }\n return 0;\n });\n return out;\n}\n\nfunction validateNoOverlappingIntervals(rows = [], label = 'Intervals') {\n if (!rows.length) return;\n const ordered = sortByColumns(rows, [HOLE_ID, FROM, TO]);\n const prevToByHole = new Map();\n\n ordered.forEach((row) => {\n const holeId = `${row?.[HOLE_ID] ?? ''}`.trim();\n const fromValue = Number(row?.[FROM]);\n const toValue = Number(row?.[TO]);\n if (!holeId || !Number.isFinite(fromValue) || !Number.isFinite(toValue)) return;\n\n const prevTo = prevToByHole.get(holeId);\n if (Number.isFinite(prevTo) && fromValue < prevTo) {\n throw withDataErrorContext(\n 'validateNoOverlappingIntervals',\n new Error(`${label} intervals overlap for hole '${holeId}': from=${fromValue} is less than previous to=${prevTo}`)\n );\n }\n prevToByHole.set(holeId, toValue);\n });\n}\n\n/**\n * Parse CSV data using PapaParse\n * @private\n */\nfunction parseCsv(source, papaParseConfig = {}) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n ...papaParseConfig,\n complete: (results) => resolve(Array.isArray(results?.data) ? results.data : []),\n error: (error) => reject(withDataErrorContext('loadTable(csv)', error))\n });\n });\n}\n\n/**\n * Standardize column names in an array of rows using the baselode data model\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map (unused, for backwards compatibility)\n * @param {Object} [sourceColumnMap] - Optional source column map for user-provided mappings\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeColumns(rows = [], columnMap = null, sourceColumnMap = null) {\n return rows.map((row) => standardizeRow(row, columnMap, sourceColumnMap));\n}\n\nexport async function loadTable(source, options = {}) {\n const {\n kind = 'csv',\n columnMap = null,\n sourceColumnMap = null,\n papaParseConfig = {}\n } = options;\n\n let rows;\n if (Array.isArray(source)) {\n rows = toArray(source);\n } else if (kind === 'csv') {\n rows = await parseCsv(source, papaParseConfig);\n } else if (kind === 'parquet' || kind === 'sql') {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind in JS runtime: ${kind}`));\n } else {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind: ${kind}`));\n }\n\n return standardizeColumns(rows, columnMap, sourceColumnMap);\n}\n\n/**\n * Load and validate drillhole collar data\n * Requires hole_id and coordinates (either lat/lng or easting/northing)\n * @param {File|Blob|Array<Object>|string} source - Collar data source\n * @param {Object} options - Loading options\n * @param {string} options.crs - Coordinate reference system (optional)\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated collar rows\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadCollars(source, options = {}) {\n const {\n crs = null,\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Check for hole_id\n const hasHoleId = standardized.some(row => HOLE_ID in row);\n if (!hasHoleId) {\n throw withDataErrorContext('loadCollars', new Error(`Collar table missing column: ${HOLE_ID}`));\n }\n\n // Check for required coordinate columns\n const hasXY = standardized.some(row => EASTING in row && NORTHING in row);\n const hasLatLon = standardized.some(row => LATITUDE in row && LONGITUDE in row);\n\n if (!hasXY && !hasLatLon) {\n throw withDataErrorContext('loadCollars', new Error('Collar table missing coordinate columns (need easting/northing or latitude/longitude)'));\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert coordinates to numbers\n if (LATITUDE in result) result[LATITUDE] = toNumber(result[LATITUDE]);\n if (LONGITUDE in result) result[LONGITUDE] = toNumber(result[LONGITUDE]);\n if (ELEVATION in result) result[ELEVATION] = toNumber(result[ELEVATION]);\n if (EASTING in result) result[EASTING] = toNumber(result[EASTING]);\n if (NORTHING in result) result[NORTHING] = toNumber(result[NORTHING]);\n\n // If datasource_hole_id is missing, copy from hole_id\n if (!('datasource_hole_id' in result) && HOLE_ID in result) {\n result.datasource_hole_id = result[HOLE_ID];\n }\n\n return result;\n });\n\n // Validate required fields\n const required = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (hasLatLon && (!Number.isFinite(row[LATITUDE]) || !Number.isFinite(row[LONGITUDE]))) {\n return false;\n }\n if (hasXY && !hasLatLon && (!Number.isFinite(row[EASTING]) || !Number.isFinite(row[NORTHING]))) {\n return false;\n }\n return true;\n });\n\n if (!required) {\n throw withDataErrorContext('loadCollars', new Error('Collar table has missing required values'));\n }\n\n return normalized;\n}\n\n/**\n * Load and validate drillhole survey data\n * Requires hole_id, depth, azimuth, and dip columns\n * @param {File|Blob|Array<Object>|string} source - Survey data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated survey rows, sorted by hole_id and depth\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadSurveys(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, DEPTH, AZIMUTH, DIP];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadSurveys', new Error(`Survey table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (DEPTH in result) result[DEPTH] = toNumber(result[DEPTH]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n if (AZIMUTH in result) result[AZIMUTH] = toNumber(result[AZIMUTH]);\n if (DIP in result) result[DIP] = toNumber(result[DIP]);\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[DEPTH])) return false;\n if (!Number.isFinite(row[AZIMUTH])) return false;\n if (!Number.isFinite(row[DIP])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadSurveys', new Error('Survey table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, DEPTH]);\n}\n\n/**\n * Load and validate assay/interval data\n * Requires hole_id, from, and to columns. Calculates mid depth automatically.\n * @param {File|Blob|Array<Object>|string} source - Assay data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated assay rows, sorted by hole_id, from, to\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadAssays(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadAssays', new Error(`Assay table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n \n // Calculate midpoint\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadAssays', new Error('Assay table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Load and validate geology interval data for categorical strip-log plotting.\n * Requires hole_id, from, to and at least one geology categorical field.\n * @param {File|Blob|Array<Object>|string} source - Geology data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @returns {Promise<Array<Object>>} Array of validated geology rows, sorted by hole_id, from, to\n */\nexport async function loadGeology(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n\n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some((row) => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n // Expand equal bounds to a 1mm interval (mirrors Python loader behaviour)\n if (result[TO] === result[FROM]) {\n result[FROM] = Math.round(result[FROM] * 1000) / 1000;\n result[TO] = result[FROM] + 0.001;\n }\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n\n const hasCode = result[GEOLOGY_CODE] !== undefined && result[GEOLOGY_CODE] !== null && `${result[GEOLOGY_CODE]}`.trim() !== '';\n const hasDescription = result[GEOLOGY_DESCRIPTION] !== undefined && result[GEOLOGY_DESCRIPTION] !== null && `${result[GEOLOGY_DESCRIPTION]}`.trim() !== '';\n if (!hasCode && hasDescription) {\n result[GEOLOGY_CODE] = result[GEOLOGY_DESCRIPTION];\n }\n if (hasCode && !hasDescription) {\n result[GEOLOGY_DESCRIPTION] = result[GEOLOGY_CODE];\n }\n\n return result;\n });\n\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadGeology', new Error('Geology table has missing or invalid interval values'));\n }\n\n const hasCategory = normalized.some((row) => {\n const code = row[GEOLOGY_CODE];\n const description = row[GEOLOGY_DESCRIPTION];\n return (code !== undefined && code !== null && `${code}`.trim() !== '') ||\n (description !== undefined && description !== null && `${description}`.trim() !== '');\n });\n\n if (!hasCategory) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing categorical columns: ${GEOLOGY_CODE} or ${GEOLOGY_DESCRIPTION}`));\n }\n\n validateNoOverlappingIntervals(normalized, 'Geology');\n\n if (!keepAll) {\n const keep = new Set(Object.keys(BASELODE_DATA_MODEL_DRILL_GEOLOGY));\n return sortByColumns(\n normalized.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => keep.has(k)))),\n [HOLE_ID, FROM, TO]\n );\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Join assay data to desurveyed trace data by matching key columns\n * Adds trace fields to assay rows, suffixing with '_trace' if field already exists\n * @param {Array<Object>} assays - Array of assay rows\n * @param {Array<Object>} traces - Array of trace/desurvey rows\n * @param {Object} options - Join options\n * @param {Array<string>} options.onCols - Columns to join on (default: [hole_id])\n * @returns {Array<Object>} Assay rows enriched with trace data\n */\nexport function joinAssaysToTraces(assays = [], traces = [], options = {}) {\n const onCols = Array.isArray(options.onCols) && options.onCols.length ? options.onCols : [HOLE_ID];\n if (!traces.length) return [...assays];\n\n const keyOf = (row) => onCols.map((col) => `${row?.[col] ?? ''}`).join('|');\n const tracesByKey = new Map();\n traces.forEach((trace) => {\n tracesByKey.set(keyOf(trace), trace);\n });\n\n return assays.map((assay) => {\n const trace = tracesByKey.get(keyOf(assay));\n if (!trace) return { ...assay };\n const merged = { ...assay };\n Object.entries(trace).forEach(([key, value]) => {\n if (onCols.includes(key)) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = value;\n } else {\n merged[key] = value;\n }\n });\n return merged;\n });\n}\n\n/**\n * Filter rows by project ID\n * @param {Array<Object>} rows - Array of rows with project_id field\n * @param {string|null} projectId - Project ID to filter by (null returns all rows)\n * @returns {Array<Object>} Filtered rows\n */\nexport function filterByProject(rows = [], projectId = null) {\n if (projectId === null || projectId === undefined) return [...rows];\n if (!rows.length) return [];\n \n // Check if rows have project_id column\n const hasProjectId = rows.some(row => PROJECT_ID in row);\n if (!hasProjectId) return [...rows];\n \n return rows.filter((row) => row?.[PROJECT_ID] === projectId);\n}\n\n/**\n * Coerce specified columns to numeric (finite number or undefined)\n * @param {Array<Object>} rows - Array of rows\n * @param {Array<string>} columns - Column names to coerce to numeric\n * @returns {Array<Object>} Rows with coerced numeric columns\n */\nexport function coerceNumeric(rows = [], columns = []) {\n return rows.map((row) => {\n const next = { ...row };\n columns.forEach((column) => {\n if (!(column in next)) return;\n const n = toNumber(next[column]);\n next[column] = n;\n });\n return next;\n });\n}\n\n/**\n * Assemble a complete drillhole dataset from component tables\n * @param {Object} components - Dataset components\n * @param {Array<Object>} components.collars - Collar data\n * @param {Array<Object>} components.surveys - Survey data\n * @param {Array<Object>} components.assays - Assay data\n * @param {Array<Object>} components.geology - Geology interval data\n * @param {Array<Object>} components.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, geology: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n geology = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\n geology: toArray(geology),\n structures: toArray(structures),\n metadata: metadata || {}\n };\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, DEPTH, DIP, AZIMUTH } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to standardized column names.\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Determine if a set of rows represents point or interval data.\n * @private\n * @param {Array<Object>} rows - Normalized rows\n * @returns {'point'|'interval'|null}\n */\nfunction detectSchema(rows) {\n if (!rows.length) return null;\n const first = rows[0];\n const hasInterval = FROM in first && TO in first;\n const hasPoint = DEPTH in first && !hasInterval;\n if (hasInterval) return 'interval';\n if (hasPoint) return 'point';\n return null;\n}\n\n/**\n * Coerce a value to a finite number or null.\n * @private\n */\nfunction toNumber(v) {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Normalize a single structural point row.\n * @private\n */\nfunction extractStructuralPoint(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const depth = toNumber(row[DEPTH]);\n if (depth === null) return null;\n\n return {\n [HOLE_ID]: holeId,\n [DEPTH]: depth,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Normalize a single structural interval row.\n * @private\n */\nfunction extractStructuralInterval(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const from = toNumber(row[FROM]);\n const to = toNumber(row[TO]);\n if (from === null || to === null || to <= from) return null;\n\n const mid = 0.5 * (from + to);\n return {\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n mid,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n classification: row.classification != null ? `${row.classification}` : null,\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Validate an array of structural point rows.\n * Returns an object with valid rows and error details.\n *\n * @param {Array<Object>} rows - Normalized structural point rows\n * @returns {{ valid: Array<Object>, errors: Array<{row: Object, message: string}> }}\n */\nexport function validateStructuralPoints(rows) {\n const valid = [];\n const errors = [];\n\n for (const row of rows) {\n const messages = [];\n const dip = toNumber(row[DIP]);\n const az = toNumber(row[AZIMUTH]);\n\n if (dip !== null && (dip < 0 || dip > 90)) {\n messages.push(`dip ${dip} out of range [0, 90]`);\n }\n if (az !== null && (az < 0 || az >= 360)) {\n messages.push(`azimuth ${az} out of range [0, 360)`);\n }\n if (messages.length) {\n errors.push({ row, message: messages.join('; ') });\n } else {\n valid.push(row);\n }\n }\n\n return { valid, errors };\n}\n\n/**\n * Parse a structural points CSV (point schema: hole_id, depth, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural point objects\n */\nexport function parseStructuralPointsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const opts = {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const point = extractStructuralPoint(row);\n if (point) rows.push(point);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralPointsCSV', error)),\n };\n\n if (typeof source === 'string' && !source.startsWith('data:') && source.includes('\\n')) {\n Papa.parse(source, opts);\n } else {\n Papa.parse(source, opts);\n }\n });\n}\n\n/**\n * Parse a structural intervals CSV (interval schema: hole_id, from, to, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural interval objects\n */\nexport function parseStructuralIntervalsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractStructuralInterval(row);\n if (interval) rows.push(interval);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralIntervalsCSV', error)),\n });\n });\n}\n\n/**\n * Group a flat array of structural rows into hole objects keyed by hole_id.\n *\n * The resulting hole objects use the same shape as assay holes\n * ({ holeId, points }) so they can be merged into useDrillholeTraceGrid.\n *\n * @param {Array<Object>} rows - Flat array of structural rows (already normalized)\n * @param {string} [holeIdCol='hole_id'] - Column name containing the hole identifier\n * @returns {Array<{ holeId: string, points: Array<Object> }>}\n */\nexport function groupRowsByHole(rows, holeIdCol = HOLE_ID) {\n const byId = new Map();\n for (const row of rows) {\n const id = row[holeIdCol] != null ? String(row[holeIdCol]).trim() : '';\n if (!id) continue;\n if (!byId.has(id)) byId.set(id, { holeId: id, points: [] });\n byId.get(id).points.push(row);\n }\n return Array.from(byId.values());\n}\n\n/**\n * Parse a structural CSV, auto-detecting point vs interval schema.\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<{ schema: 'point'|'interval', rows: Array<Object> }>}\n */\nexport function parseStructuralCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = results.data.map(r => normalizeRow(r, sourceColumnMap));\n const schema = detectSchema(normalized);\n\n if (!schema) {\n reject(withDataErrorContext('parseStructuralCSV',\n new Error(\"Structural CSV requires either 'depth' (point) or 'from'/'to' (interval) columns\")));\n return;\n }\n\n const rows = [];\n for (const row of normalized) {\n const parsed = schema === 'interval'\n ? extractStructuralInterval(row)\n : extractStructuralPoint(row);\n if (parsed) rows.push(parsed);\n }\n resolve({ schema, rows });\n },\n error: (error) => reject(withDataErrorContext('parseStructuralCSV', error)),\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, FROM, TO, MID, DEPTH, DIP, AZIMUTH } from './datamodel.js';\nimport { parseStructuralCSV, groupRowsByHole } from './structuralLoader.js';\n\n\n/**\n * Parse assay CSV text into an array of hole objects.\n *\n * Each interval row gets a pre-computed `mid` field (= (from + to) / 2) stored\n * under both the `mid` and `depth` keys so it can serve as the unified y-axis\n * depth value when rendered alongside structural point data.\n *\n * @param {string} csvText - Assay CSV content as a text string\n * @returns {Promise<Array<{holeId: string, points: Array<Object>}>>}\n */\nexport function parseAssayCsvTextToHoles(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // Strip structural-domain columns that some assay exports (e.g. GSWA) include\n // as per-interval hole-orientation values — these are borehole survey attributes,\n // not structural plane measurements, and must not appear as selectable properties\n // alongside true structural data.\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rowWithoutStructural } = row;\n const point = {\n ...rowWithoutStructural,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid, // unified depth field for y-axis rendering\n _source: 'assay',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n const holes = Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n }));\n resolve(holes);\n },\n });\n });\n}\n\n/**\n * Parse geology CSV text into an array of hole objects, keyed by hole_id.\n *\n * @param {string} csvText - Geology CSV content as a text string\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport function parseGeologyCsvText(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = (row[HOLE_ID] ?? '').toString().trim();\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rest } = row;\n const point = {\n ...rest,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid,\n _source: 'geology',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n resolve({\n holes: Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n })),\n });\n },\n });\n });\n}\n\n/**\n * Build a unified drillhole dataset by merging assay intervals and structural\n * point/interval measurements from their respective CSV text sources.\n *\n * The result is a flat, holeId-keyed collection of hole objects. Each hole's\n * `points` array contains rows from both sources:\n * - Assay rows carry all geochemical columns; `depth` is set to the interval\n * midpoint ((from + to) / 2) pre-computed at load time.\n * - Structural rows carry dip/azimuth/description columns; `depth` comes\n * directly from the structural CSV (point schema) or the interval midpoint\n * (interval schema).\n * - Rows from each source are tagged with `_source: 'assay'|'structural'`.\n *\n * The returned holes can be passed directly to `useDrillholeTraceGrid` as\n * `extraHoles` (without a `sourceFile`) since all data is eager-loaded.\n *\n * @param {Object} options\n * @param {string} [options.assayCsv] - Assay CSV text\n * @param {string} [options.structuralCsv] - Structural CSV text\n * @param {string} [options.geologyCsv] - Geology CSV text\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv, geologyCsv } = {}) {\n const [assayHoles, structuralHoles, geologyHoles] = await Promise.all([\n assayCsv ? parseAssayCsvTextToHoles(assayCsv) : Promise.resolve([]),\n structuralCsv\n ? parseStructuralCSV(structuralCsv).then(({ rows }) =>\n groupRowsByHole(rows.map((r) => ({ ...r, _source: 'structural' })))\n )\n : Promise.resolve([]),\n geologyCsv ? parseGeologyCsvText(geologyCsv).then(({ holes }) => holes) : Promise.resolve([]),\n ]);\n\n // Merge holes from all sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of [...structuralHoles, ...geologyHoles]) {\n const id = sh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...existing.points, ...(sh.points || [])] });\n } else {\n byId.set(id, sh);\n }\n }\n\n return { holes: Array.from(byId.values()) };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Calculate significant intercepts from assay interval data.\n *\n * A significant intercept is a contiguous downhole run of assay intervals where\n * every interval meets or exceeds {@link minGrade}, and the total run length is\n * at least {@link minLength}.\n *\n * @param {Array<Object>} intervals - Assay interval rows, each containing at\n * minimum the hole ID, from-depth, to-depth, and assay field columns.\n * @param {string} assayField - Name of the assay value property on each row\n * (e.g. `\"CU_PCT\"` or `\"AU_PPM\"`).\n * @param {number} minGrade - Minimum grade threshold. Intervals below this\n * value are excluded.\n * @param {number} minLength - Minimum contiguous downhole length required to\n * report an intercept.\n * @param {Object} [options={}]\n * @param {string} [options.fromCol=\"from\"] - Property name for the from-depth.\n * @param {string} [options.toCol=\"to\"] - Property name for the to-depth.\n * @param {string} [options.holeCol=\"hole_id\"] - Property name for the hole ID.\n * @returns {Array<Object>} One object per significant intercept with properties:\n * `hole_id`, `assay_field`, `from`, `to`, `length`, `avg_grade`,\n * `n_samples`, `label`.\n */\nexport function significantIntercepts(intervals, assayField, minGrade, minLength, options = {}) {\n const fromCol = options.fromCol || 'from';\n const toCol = options.toCol || 'to';\n const holeCol = options.holeCol || 'hole_id';\n\n if (!intervals || !intervals.length) return [];\n\n // Group by hole ID\n const byHole = {};\n for (const row of intervals) {\n const holeId = row[holeCol];\n if (holeId == null) continue;\n if (!byHole[holeId]) byHole[holeId] = [];\n byHole[holeId].push(row);\n }\n\n const results = [];\n\n for (const [holeId, holeIntervals] of Object.entries(byHole)) {\n // Sort by from depth\n const sorted = [...holeIntervals].sort((a, b) => Number(a[fromCol]) - Number(b[fromCol]));\n\n // Filter to above-threshold intervals with finite grades\n const qualifying = sorted.filter((row) => {\n const grade = Number(row[assayField]);\n return Number.isFinite(grade) && grade >= minGrade;\n });\n\n if (!qualifying.length) continue;\n\n // Group qualifying intervals into contiguous runs\n const runs = [];\n let currentRun = [];\n let prevTo = null;\n\n for (const row of qualifying) {\n const f = Number(row[fromCol]);\n const t = Number(row[toCol]);\n\n if (prevTo === null || Math.abs(f - prevTo) > 1e-6) {\n if (currentRun.length) runs.push(currentRun);\n currentRun = [row];\n } else {\n currentRun.push(row);\n }\n prevTo = t;\n }\n if (currentRun.length) runs.push(currentRun);\n\n for (const run of runs) {\n const totalFrom = Number(run[0][fromCol]);\n const totalTo = Number(run[run.length - 1][toCol]);\n const totalLength = totalTo - totalFrom;\n\n if (totalLength < minLength) continue;\n\n let weightedSum = 0;\n let totalWeight = 0;\n for (const row of run) {\n const grade = Number(row[assayField]);\n const len = Number(row[toCol]) - Number(row[fromCol]);\n weightedSum += grade * len;\n totalWeight += len;\n }\n const avgGrade = weightedSum / totalWeight;\n const nSamples = run.length;\n const label = `${totalLength.toFixed(1)} m @ ${avgGrade.toFixed(2)} ${assayField}`;\n\n results.push({\n [holeCol]: holeId,\n assay_field: assayField,\n [fromCol]: totalFrom,\n [toCol]: totalTo,\n length: totalLength,\n avg_grade: avgGrade,\n n_samples: nSamples,\n label,\n });\n }\n }\n\n return results;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, DEPTH, FROM, TO, MID, GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Column name variants for depth in geophysics point logs.\n * These are tried (after normalisation) when the standard 'depth' column is absent.\n * @private\n */\nconst DEPTH_ALIASES = ['depth', 'md', 'measured_depth', 'dept', 'z'];\n\n/**\n * Resolve the depth value from a standardised geophysics row.\n *\n * Supports both point-based (single depth column) and interval-based (from/to)\n * schemas. For interval rows the midpoint is returned. The standard DEPTH\n * column is tried first; common aliases are checked as a fallback.\n *\n * @private\n * @param {object} row - Standardised row object\n * @returns {number|null}\n */\nfunction resolveDepth(row) {\n // Point-based: explicit depth column\n if (row[DEPTH] !== undefined) {\n const v = Number(row[DEPTH]);\n if (Number.isFinite(v)) return v;\n }\n for (const alias of DEPTH_ALIASES) {\n if (row[alias] !== undefined) {\n const v = Number(row[alias]);\n if (Number.isFinite(v)) return v;\n }\n }\n // Interval-based: compute midpoint from from/to\n const f = Number(row[FROM]);\n const t = Number(row[TO]);\n if (Number.isFinite(f) && Number.isFinite(t) && t >= f) return (f + t) / 2;\n return null;\n}\n\n/**\n * Discover the numeric value columns in a geophysics file.\n *\n * All columns that are not hole_id or a recognised depth alias are considered\n * candidate value columns. Only columns whose values parse as finite numbers\n * for the majority (>50 %) of rows are retained, so comment or flag columns\n * are dropped automatically.\n *\n * @private\n * @param {object[]} rows - Standardised rows\n * @returns {string[]} Ordered list of numeric value column names\n */\nfunction detectValueColumns(rows) {\n if (rows.length === 0) return [];\n const skipSet = new Set([HOLE_ID, DEPTH, FROM, TO, MID, ...DEPTH_ALIASES]);\n const candidates = Object.keys(rows[0]).filter((c) => !skipSet.has(c));\n\n return candidates.filter((col) => {\n let numericCount = 0;\n for (const row of rows) {\n const v = Number(row[col]);\n if (Number.isFinite(v)) numericCount++;\n }\n return numericCount / rows.length > 0.5;\n });\n}\n\n/**\n * Parse a geophysics point-log CSV into per-hole arrays of depths and values.\n *\n * Unlike assay intervals (from/to), geophysics logs contain one row per sample\n * depth — typically at a fixed spacing (e.g. 0.1 m for gamma probes). This\n * loader handles the higher sample density gracefully; there is no upper limit\n * on the number of rows.\n *\n * ### Column detection\n * - **hole_id** — standard variants recognised by `standardizeColumns`.\n * - **depth** — standard `depth` plus common aliases: `md`, `measured_depth`,\n * `dept`, `z`.\n * - **value columns** — every other numeric column is treated as a geophysics\n * channel (gamma, resistivity, SP, etc.). Column names are preserved as-is\n * after normalisation so callers can identify individual channels.\n *\n * ### Return value\n * Returns an array of **hole objects**, one per unique hole_id:\n * ```js\n * [\n * {\n * holeId: 'GSWA001',\n * channels: {\n * gamma: { depths: [0.0, 0.1, 0.2, ...], values: [45, 47, 52, ...] },\n * sp: { depths: [0.0, 0.1, 0.2, ...], values: [-12, -11, -9, ...] },\n * }\n * },\n * ...\n * ]\n * ```\n * The `channels` object keys match the normalised CSV column names. Each\n * channel only includes rows where both depth and value are finite numbers.\n *\n * @param {string} csvText - Raw CSV text\n * @param {object} [columnMap] - Optional `{ rawName: standardName }` overrides\n * @returns {Array<{holeId: string, channels: object}>}\n */\nexport function parseGeophysicsCSV(csvText, columnMap = null) {\n const result = Papa.parse(csvText, {\n header: true,\n skipEmptyLines: true,\n dynamicTyping: false,\n });\n\n const rawRows = result.data || [];\n if (rawRows.length === 0) return [];\n\n const rows = rawRows.map((r) => standardizeColumns(r, null, columnMap));\n\n // Discover value columns from the full dataset\n const valueCols = detectValueColumns(rows);\n\n // Group rows by hole\n const byHole = new Map();\n for (const row of rows) {\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n\n const depth = resolveDepth(row);\n if (depth === null || depth < 0) continue;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({ depth, row });\n }\n\n const holes = [];\n for (const [holeId, samples] of byHole) {\n // Sort by depth (ascending)\n samples.sort((a, b) => a.depth - b.depth);\n\n const channels = {};\n for (const col of valueCols) {\n const depths = [];\n const values = [];\n for (const { depth, row } of samples) {\n const v = Number(row[col]);\n if (Number.isFinite(v) && v !== GEOPHYSICS_NULL_SENTINEL) {\n depths.push(depth);\n values.push(v);\n }\n }\n if (depths.length >= 2) {\n channels[col] = { depths, values };\n }\n }\n\n if (Object.keys(channels).length > 0) {\n holes.push({ holeId, channels });\n }\n }\n\n return holes;\n}\n\n/**\n * Convert the output of `parseGeophysicsCSV` into the strip log format\n * expected by `Baselode3DScene.setStripLogs`.\n *\n * A single channel from each hole becomes one strip log entry. If a hole has\n * multiple channels, pass `channel` to select which one (defaults to the first\n * channel found).\n *\n * @param {Array<{holeId: string, channels: object}>} geophysicsHoles\n * Output of `parseGeophysicsCSV`.\n * @param {string} [channel]\n * Channel name to use. When omitted, the first available channel per hole\n * is used.\n * @param {object} [options]\n * Strip log display options forwarded to each entry's `options` field.\n * See `normalizeStripLogOptions` for available keys.\n * @returns {Array<{holeId: string, depths: number[], values: number[], options: object}>}\n */\nexport function geophysicsToStripLogs(geophysicsHoles, channel = null, options = {}) {\n const stripLogs = [];\n for (const { holeId, channels } of geophysicsHoles) {\n const key = channel && channels[channel] ? channel : Object.keys(channels)[0];\n if (!key) continue;\n const { depths, values } = channels[key];\n stripLogs.push({ holeId, depths, values, options });\n }\n return stripLogs;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Parse a single LAS header mnemonic line.\n *\n * LAS header lines have the format:\n * MNEM.UNIT value : description\n *\n * @private\n * @param {string} line\n * @returns {{ mnem: string, unit: string, value: string, description: string } | null}\n */\nfunction parseMnemonicLine(line) {\n const dotIdx = line.indexOf('.');\n if (dotIdx < 0) return null;\n\n const mnem = line.slice(0, dotIdx).trim().toUpperCase();\n const afterDot = line.slice(dotIdx + 1);\n\n // Unit is the first non-whitespace token after the dot\n const unitMatch = afterDot.match(/^(\\S*)\\s*(.*)/s);\n const unit = unitMatch ? unitMatch[1] : '';\n const valueAndDesc = unitMatch ? unitMatch[2] : afterDot;\n\n // Description follows the last colon\n const lastColon = valueAndDesc.lastIndexOf(':');\n const value = lastColon >= 0 ? valueAndDesc.slice(0, lastColon).trim() : valueAndDesc.trim();\n const description = lastColon >= 0 ? valueAndDesc.slice(lastColon + 1).trim() : '';\n\n return { mnem, unit, value, description };\n}\n\n/**\n * Split a LAS text into named sections.\n *\n * @private\n * @param {string} lasText\n * @returns {Array<{ type: string, lines: string[] }>}\n */\nfunction splitSections(lasText) {\n const sections = [];\n let current = null;\n\n for (const raw of lasText.split(/\\r?\\n/)) {\n const line = raw.trimEnd();\n if (line.startsWith('~')) {\n // Section type is the first letter after ~, uppercased\n const type = line.slice(1, 2).toUpperCase();\n current = { type, lines: [] };\n sections.push(current);\n } else if (current) {\n current.lines.push(line);\n }\n }\n\n return sections;\n}\n\n/**\n * Parse a LAS header section into a map of mnemonic → parsed entry.\n *\n * @private\n * @param {string[] | undefined} lines\n * @returns {Record<string, { mnem: string, unit: string, value: string, description: string }>}\n */\nfunction parseHeaderSection(lines = []) {\n const map = {};\n for (const line of lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) map[parsed.mnem] = parsed;\n }\n return map;\n}\n\n/**\n * Parse a LAS 1.2 or 2.0 file into the same per-hole channel format returned\n * by {@link parseGeophysicsCSV}.\n *\n * LAS (Log ASCII Standard) is the industry-standard format for wireline log\n * data. Measurements are point-based — one row per sample depth — so the\n * returned channel objects use a ``depth`` axis rather than from/to intervals.\n *\n * ### LAS → baselode mapping\n *\n * | LAS section | Field | Baselode field |\n * |-------------|--------------|------------------|\n * | `~WELL` | WELL/UWI/API | `holeId` |\n * | `~WELL` | NULL | null sentinel |\n * | `~CURVE` | first curve | depth axis |\n * | `~CURVE` | other curves | channel names |\n * | `~A` | data rows | depths + values |\n *\n * ### Return value\n *\n * Returns an array with a single element (LAS files describe one hole):\n * ```js\n * [\n * {\n * holeId: 'MKC435',\n * units: { gamma: 'GAPI', density: 'G/CC' },\n * channels: {\n * gamma: { depths: [0.1, 0.2, ...], values: [45, 52, ...] },\n * density: { depths: [1.1, 1.2, ...], values: [2.3, 2.4, ...] },\n * }\n * }\n * ]\n * ```\n *\n * The `units` object contains the LAS unit string for each channel (lowercased\n * mnemonic key). Channels with fewer than 2 valid samples are omitted.\n *\n * ### Limitations\n *\n * `WRAP YES` (multi-line data rows) is not supported; a console warning is\n * emitted and parsing continues on a best-effort basis.\n *\n * @param {string} lasText - Raw LAS file text\n * @param {object} [options]\n * @param {string} [options.holeId] - Override the hole identifier\n * @param {number} [options.nullSentinel] - Override the null sentinel value\n * @returns {Array<{ holeId: string, units: object, channels: object }>}\n */\nexport function parseLasFile(lasText, options = {}) {\n const sections = splitSections(lasText);\n\n const versionSection = sections.find((s) => s.type === 'V');\n const wellSection = sections.find((s) => s.type === 'W');\n const curveSection = sections.find((s) => s.type === 'C');\n const dataSection = sections.find((s) => s.type === 'A');\n\n if (!dataSection) return [];\n\n const version = parseHeaderSection(versionSection?.lines);\n const well = parseHeaderSection(wellSection?.lines);\n const curveEntries = [];\n for (const line of (curveSection?.lines ?? [])) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) curveEntries.push(parsed);\n }\n\n // Warn on unsupported WRAP YES\n const wrap = version.WRAP?.value?.trim().toUpperCase() ?? 'NO';\n if (wrap === 'YES') {\n console.warn(\n 'parseLasFile: WRAP YES is not supported. Data rows may be read incorrectly.'\n );\n }\n\n // Resolve hole_id\n const holeId =\n options.holeId ??\n (well.WELL?.value?.trim() || null) ??\n (well.UWI?.value?.trim() || null) ??\n (well.API?.value?.trim() || null) ??\n 'unknown';\n\n // Resolve null sentinel (LAS file value takes precedence over default)\n let nullSentinel = GEOPHYSICS_NULL_SENTINEL;\n if (well.NULL?.value) {\n const v = Number(well.NULL.value);\n if (Number.isFinite(v)) nullSentinel = v;\n }\n if (options.nullSentinel !== undefined) {\n nullSentinel = options.nullSentinel;\n }\n\n if (curveEntries.length === 0) return [];\n\n // First curve is always depth\n const valueCurves = curveEntries.slice(1); // skip depth curve\n\n // Collect units for each value channel\n const units = {};\n for (const { mnem, unit } of valueCurves) {\n units[mnem.toLowerCase()] = unit;\n }\n\n // Accumulate channel data\n const channelData = {};\n for (const { mnem } of valueCurves) {\n channelData[mnem.toLowerCase()] = { depths: [], values: [] };\n }\n\n for (const line of dataSection.lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const tokens = trimmed.split(/\\s+/);\n if (tokens.length < 2) continue;\n\n const depth = Number(tokens[0]);\n if (!Number.isFinite(depth) || depth === nullSentinel) continue;\n\n for (let i = 0; i < valueCurves.length; i++) {\n const key = valueCurves[i].mnem.toLowerCase();\n const v = Number(tokens[i + 1]);\n if (Number.isFinite(v) && v !== nullSentinel) {\n channelData[key].depths.push(depth);\n channelData[key].values.push(v);\n }\n }\n }\n\n // Drop channels with fewer than 2 valid samples\n const channels = {};\n for (const [key, data] of Object.entries(channelData)) {\n if (data.depths.length >= 2) channels[key] = data;\n }\n\n if (Object.keys(channels).length === 0) return [];\n\n return [{ holeId, units, channels }];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Plotly visualization builders for structural measurements.\n *\n * Provides:\n * - buildTadpoleConfig: 1D strip log tadpole plot (dip head + azimuth tail)\n * - buildStructuralStripConfig: categorical interval strip log\n * - buildStrikeDipSymbol: 2D map strike/dip symbol geometry\n */\n\nimport { AZIMUTH, DEPTH, DIP, FROM, TO } from '../data/datamodel.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 36 };\nconst STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\nconst STRIPLOG_AXIS_TITLE_FONT_SIZE = 11;\nconst STRIPLOG_XAXIS_TITLE_STANDOFF = 6;\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.xaxis && layout.xaxis.title) || {}),\n font: {\n ...(((layout.xaxis && layout.xaxis.title && layout.xaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n standoff: (layout.xaxis && layout.xaxis.title && layout.xaxis.title.standoff)\n ?? STRIPLOG_XAXIS_TITLE_STANDOFF,\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.yaxis && layout.yaxis.title) || {}),\n font: {\n ...(((layout.yaxis && layout.yaxis.title && layout.yaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n };\n}\n\n/**\n * Build a Plotly tadpole log config for structural point measurements.\n *\n * Each measurement renders as a circle (head) at its depth with a tail\n * pointing toward the dip direction. Tail length is proportional to dip magnitude.\n *\n * @param {Array<Object>} points - Structural point rows\n * @param {Object} opts\n * @param {number} [opts.tailScale=0.3] - Controls tail length relative to dip magnitude\n * @param {string|null} [opts.colorBy=null] - Column name to color heads by (e.g. 'defect')\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.depthCol='depth'] - Column for measured depth\n * @param {string} [opts.dipCol='dip'] - Column for dip angle\n * @param {string} [opts.azCol='azimuth'] - Column for dip direction\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildTadpoleConfig(points, {\n tailScale = 5,\n colorBy = null,\n palette = DEFAULT_PALETTE,\n depthCol = DEPTH,\n dipCol = DIP,\n azCol = AZIMUTH,\n template = undefined,\n} = {}) {\n const valid = points.filter(p =>\n p[depthCol] != null && p[dipCol] != null && p[azCol] != null\n );\n\n if (!valid.length) {\n return { data: [], layout: {} };\n }\n\n // Build color map for categories\n const colorMap = {};\n if (colorBy) {\n const categories = [...new Set(valid.map(p => p[colorBy]).filter(v => v != null))].sort();\n categories.forEach((cat, i) => { colorMap[cat] = palette[i % palette.length]; });\n }\n\n // Group by category for legend traces\n const byCat = new Map();\n const shapes = [];\n\n for (const p of valid) {\n const depth = Number(p[depthCol]);\n const dip = Number(p[dipCol]);\n const az = Number(p[azCol]);\n const cat = colorBy ? (p[colorBy] ?? '_default') : '_default';\n const color = colorBy ? (colorMap[cat] ?? '#0f172a') : '#0f172a';\n\n if (!byCat.has(cat)) {\n byCat.set(cat, { xs: [], ys: [], dips: [], azs: [], color });\n }\n const group = byCat.get(cat);\n // Head positioned at x=dip (degrees)\n group.xs.push(dip);\n group.ys.push(depth);\n group.dips.push(dip);\n group.azs.push(az);\n\n // Tail: starts at (dip, depth), direction encodes azimuth.\n // Length scales with dip magnitude (in degree units on the x-axis).\n const azRad = (az * Math.PI) / 180;\n const length = tailScale * (Math.abs(dip) / 90);\n const dx = Math.sin(azRad) * length; // x-component (degrees)\n const dy = Math.cos(azRad) * length; // y-component (degrees, visual only)\n\n shapes.push({\n type: 'line',\n x0: dip, y0: depth,\n x1: dip + dx, y1: depth + dy,\n line: { color, width: 2 },\n });\n }\n\n const data = [];\n const showLegend = colorBy && byCat.size > 1;\n\n for (const [cat, group] of byCat.entries()) {\n data.push({\n type: 'scatter',\n x: group.xs,\n y: group.ys,\n mode: 'markers',\n name: cat !== '_default' ? String(cat) : undefined,\n marker: { size: 8, color: group.color },\n showlegend: showLegend && cat !== '_default',\n customdata: group.dips.map((d, i) => [d, group.azs[i]]),\n hovertemplate: 'Depth: %{y}<br>Dip: %{customdata[0]}<br>Az: %{customdata[1]}<extra></extra>',\n });\n }\n\n const layout = {\n shapes,\n height: 400,\n margin: { l: 40, r: 10, t: 10, b: 40 },\n xaxis: {\n title: 'Dip (°)',\n autorange: true,\n fixedrange: true,\n zeroline: true,\n tickvals: [-90, -60, -30, 0, 30, 60, 90],\n },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: !!showLegend,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout };\n}\n\n/**\n * Build a Plotly categorical strip log config for structural interval measurements.\n *\n * @param {Array<Object>} intervals - Structural interval rows\n * @param {Object} opts\n * @param {string} [opts.labelCol='defect'] - Column for interval label/color\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildStructuralStripConfig(intervals, {\n labelCol = 'structure_type',\n palette = DEFAULT_PALETTE,\n fromCol = FROM,\n toCol = TO,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .filter(iv => {\n const lv = iv[labelCol];\n if (lv == null) return false;\n const s = String(lv).trim();\n return s !== '' && !/^(nan|null|none)$/i.test(s);\n })\n .map(iv => ({ from: Number(iv[fromCol]), to: Number(iv[toCol]), label: String(iv[labelCol]).trim() }))\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textY = [];\n const texts = [];\n\n records.forEach((rec, idx) => {\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: palette[idx % palette.length],\n line: { width: 0 },\n layer: 'below',\n });\n textY.push(0.5 * (rec.from + rec.to));\n texts.push(rec.label);\n });\n\n const data = [{\n type: 'scatter',\n x: Array(texts.length).fill(0.5),\n y: textY,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n }];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Word-wrap text at word boundaries, inserting Plotly HTML line breaks.\n * @private\n * @param {string} text\n * @param {number} charsPerLine\n * @returns {string}\n */\nfunction wrapComment(text, charsPerLine) {\n if (!text) return '';\n const words = String(text).trim().split(/\\s+/);\n const lines = [];\n let current = '';\n for (const word of words) {\n if (current && current.length + 1 + word.length > charsPerLine) {\n lines.push(current);\n current = word;\n } else {\n current = current ? `${current} ${word}` : word;\n }\n }\n if (current) lines.push(current);\n return lines.join('<br>');\n}\n\n/**\n * Build a Plotly comments log config — depth intervals with text annotations overlaid.\n *\n * Each interval is drawn as a lightly shaded rectangle spanning its from/to depth.\n * Non-empty comments are word-wrapped and centered inside the rectangle.\n * Intervals with no comment show a thin border only.\n *\n * @param {Array<Object>} intervals - Interval rows (must have from, to, and a comment column)\n * @param {Object} opts\n * @param {string} [opts.commentCol='comments'] - Column containing comment text\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {string} [opts.bgColor='#f1f5f9'] - Fill color for intervals with a comment\n * @param {string} [opts.borderColor='#cbd5e1'] - Rectangle border color\n * @param {string} [opts.textColor='#1e293b'] - Comment text color\n * @param {number} [opts.charsPerLine=18] - Characters before word-wrapping\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildCommentsConfig(intervals, {\n commentCol = 'comments',\n fromCol = FROM,\n toCol = TO,\n bgColor = '#f1f5f9',\n borderColor = '#cbd5e1',\n textColor = '#1e293b',\n charsPerLine = 18,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .map(iv => {\n const raw = iv[commentCol];\n const comment = (raw != null && String(raw).trim() !== '' && String(raw) !== 'null')\n ? String(raw).trim()\n : '';\n return { from: Number(iv[fromCol]), to: Number(iv[toCol]), comment };\n })\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textXs = [];\n const textYs = [];\n const texts = [];\n const hovers = [];\n\n for (const rec of records) {\n const mid = 0.5 * (rec.from + rec.to);\n const hasComment = !!rec.comment;\n\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: hasComment ? bgColor : 'rgba(0,0,0,0)',\n line: { color: borderColor, width: 1 },\n layer: 'below',\n });\n\n if (hasComment) {\n textXs.push(0.5);\n textYs.push(mid);\n texts.push(wrapComment(rec.comment, charsPerLine));\n hovers.push(`${rec.from.toFixed(3)}–${rec.to.toFixed(3)} m<br>${wrapComment(rec.comment, 40)}`);\n }\n }\n\n const data = textXs.length ? [{\n type: 'scatter',\n x: textXs,\n y: textYs,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n textfont: { color: textColor, size: 10 },\n hovertext: hovers,\n hoverinfo: 'text',\n showlegend: false,\n }] : [];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Compute 2D map strike/dip symbol geometry for a single structural measurement.\n *\n * Returns the geometry needed to draw a strike line and dip tick on a map.\n *\n * @param {Object} point - Structural measurement with x, y, dip, azimuth\n * @param {Object} opts\n * @param {number} [opts.symbolSize=10] - Strike line half-length in map units\n * @param {string} [opts.xCol='easting'] - X coordinate column\n * @param {string} [opts.yCol='northing'] - Y coordinate column\n * @returns {{ strike: number, dipValue: number, x: number, y: number,\n * strikeX0: number, strikeY0: number, strikeX1: number, strikeY1: number,\n * tickX1: number, tickY1: number } | null}\n */\nexport function buildStrikeDipSymbol(point, {\n symbolSize = 10,\n xCol = 'easting',\n yCol = 'northing',\n} = {}) {\n const x = point[xCol] != null ? Number(point[xCol]) : null;\n const y = point[yCol] != null ? Number(point[yCol]) : null;\n const dip = point[DIP] != null ? Number(point[DIP]) : null;\n const az = point[AZIMUTH] != null ? Number(point[AZIMUTH]) : null;\n\n if (x === null || y === null || dip === null || az === null) return null;\n\n const strike = ((az - 90) + 360) % 360;\n const strikeRad = (strike * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n\n // Strike line half-endpoints\n const dxS = symbolSize * Math.sin(strikeRad);\n const dyS = symbolSize * Math.cos(strikeRad);\n\n // Dip tick from center (length scaled by dip magnitude)\n const tickLen = symbolSize * 0.4 * (dip / 90);\n const dxD = tickLen * Math.sin(azRad);\n const dyD = tickLen * Math.cos(azRad);\n\n return {\n strike,\n dipValue: dip,\n x,\n y,\n strikeX0: x - dxS,\n strikeY0: y - dyS,\n strikeX1: x + dxS,\n strikeY1: y + dyS,\n tickX1: x + dxD,\n tickY1: y + dyD,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { DISPLAY_COMMENT, DISPLAY_TADPOLE } from '../data/columnMeta.js';\n\n/**\n * Decide what the TracePlot body should display for a given state.\n * Returns either { kind: 'chart' } when a chart should render, or\n * { kind: 'placeholder', text } with the message to show.\n *\n * Pure — does not touch the DOM or Plotly — so callers and tests can\n * verify the state matrix without rendering.\n *\n * @param {Object} state\n * @param {string} state.holeId - The selected hole id (config.holeId)\n * @param {Object|null} state.hole - The resolved hole (graph.hole)\n * @param {Array} state.holeOptions - Available holes\n * @param {string} state.property - The selected property (config.property)\n * @param {Array} state.propertyOptions - Available properties\n * @param {string} state.displayType - DISPLAY_* constant\n * @param {Array} state.points - Plot points\n * @param {string} [state.renderError] - Local plot-build error, if any\n * @returns {{ kind: 'chart' } | { kind: 'placeholder', text: string }}\n */\nexport function resolveTracePlotBody({\n holeId,\n hole,\n holeOptions,\n property,\n propertyOptions,\n displayType,\n points,\n renderError,\n}) {\n if (renderError) {\n return { kind: 'placeholder', text: `Plot error: ${renderError}` };\n }\n const holes = holeOptions || [];\n if (!holeId) {\n if (holes.length === 0) {\n return { kind: 'placeholder', text: 'No holes loaded' };\n }\n return { kind: 'placeholder', text: 'Select a hole' };\n }\n if (!hole) {\n return { kind: 'placeholder', text: `Loading ${holeId}…` };\n }\n const props = propertyOptions || [];\n if (!property) {\n if (props.length === 0) {\n return { kind: 'placeholder', text: `No properties available for hole ${holeId}` };\n }\n return { kind: 'placeholder', text: 'Select a property' };\n }\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n const pts = points || [];\n if (!isComment && !isTadpole && pts.length === 0) {\n return { kind: 'placeholder', text: `No values for ${property} in hole ${holeId}` };\n }\n return { kind: 'chart' };\n}\n\n/**\n * Read a group value off a hole option using either a string key\n * (looked up on the option object) or a function.\n *\n * @param {string|Object} holeOption\n * @param {string|Function} groupBy\n * @returns {*} the group value, or undefined if it can't be derived\n */\nexport function deriveGroupValue(holeOption, groupBy) {\n if (typeof groupBy === 'function') return groupBy(holeOption);\n if (typeof groupBy !== 'string' || !groupBy) return undefined;\n if (holeOption == null || typeof holeOption !== 'object') return undefined;\n return holeOption[groupBy];\n}\n\n/**\n * Unique, non-empty group values from a list of hole options, in\n * first-seen order.\n *\n * @param {Array} holeOptions\n * @param {string|Function} groupBy\n * @returns {Array} unique group values\n */\nexport function groupValuesFromHoles(holeOptions, groupBy) {\n const out = [];\n const seen = new Set();\n for (const h of holeOptions || []) {\n const g = deriveGroupValue(h, groupBy);\n if (g == null || g === '') continue;\n if (seen.has(g)) continue;\n seen.add(g);\n out.push(g);\n }\n return out;\n}\n\n/**\n * Filter hole options to those whose group matches groupValue.\n * Returns the full list when groupValue is empty.\n *\n * @param {Array} holeOptions\n * @param {string|Function} groupBy\n * @param {*} groupValue\n * @returns {Array}\n */\nexport function filterHolesByGroup(holeOptions, groupBy, groupValue) {\n if (groupValue == null || groupValue === '') return holeOptions || [];\n return (holeOptions || []).filter((h) => deriveGroupValue(h, groupBy) === groupValue);\n}\n\n/**\n * Decide which control selects render for a given configuration.\n *\n * @param {Object} args\n * @param {Array} args.chartOptions\n * @param {boolean} args.showHoleSelect\n * @param {boolean} args.showPropertySelect\n * @param {boolean} args.showChartTypeSelect\n * @returns {{ hole: boolean, property: boolean, chartType: boolean }}\n */\nexport function resolveTracePlotSelectVisibility({\n chartOptions,\n showHoleSelect,\n showPropertySelect,\n showChartTypeSelect,\n}) {\n const opts = chartOptions || [];\n return {\n hole: showHoleSelect !== false,\n property: showPropertySelect !== false,\n chartType: showChartTypeSelect !== false && opts.length > 1,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useRef, useState } from 'react';\nimport Plotly from 'plotly.js-dist-min';\nimport { buildPlotConfig } from './drillholeViz.js';\nimport { formatPropertyLabel } from '../data/propertyLabels.js';\nimport { buildCommentsConfig, buildTadpoleConfig } from './structuralViz.js';\nimport { getChartOptions, DISPLAY_COMMENT, DISPLAY_CATEGORICAL, DISPLAY_NUMERIC, DISPLAY_TADPOLE } from '../data/columnMeta.js';\nimport {\n resolveTracePlotBody,\n resolveTracePlotSelectVisibility,\n deriveGroupValue,\n groupValuesFromHoles,\n filterHolesByGroup,\n} from './tracePlotState.js';\nimport './TracePlot.css';\n\nexport {\n resolveTracePlotBody,\n resolveTracePlotSelectVisibility,\n deriveGroupValue,\n groupValuesFromHoles,\n filterHolesByGroup,\n};\n\nconst DEFAULT_NUMERIC_CHART_TYPE = 'markers+line';\n\n/**\n * Resolve chart type from available options.\n * @private\n */\nfunction resolveChartType(displayType, requestedChartType) {\n const chartOptions = getChartOptions(displayType);\n if (chartOptions.some((opt) => opt.value === requestedChartType)) return requestedChartType;\n return chartOptions[0]?.value || DEFAULT_NUMERIC_CHART_TYPE;\n}\n\nfunction holeOptionAsTuple(h) {\n const value = typeof h === 'string' ? h : h.holeId;\n const label = typeof h === 'string' ? h : (h.label || h.holeId);\n return [value, label];\n}\n\nfunction genericOptionAsTuple(o) {\n const value = typeof o === 'string' ? o : o.value;\n const label = typeof o === 'string' ? o : (o.label ?? o.value);\n return [value, label];\n}\n\nfunction renderHoleSelector({ selector, holeOptions, selectedHoleId, onConfigChange }) {\n const kind = selector?.kind || 'hole';\n\n if (kind === 'field') {\n const value = selector.value ?? '';\n const opts = selector.options || [];\n const label = selector.label || 'Selection';\n return (\n <select\n className=\"plot-select plot-select--field\"\n value={value}\n onChange={(e) => selector.onChange && selector.onChange(e.target.value)}\n disabled={opts.length === 0}\n aria-label={label}\n >\n {opts.length === 0 && <option value=\"\">—</option>}\n {!value && opts.length > 0 && (\n <option value=\"\" disabled hidden>{`Select ${label.toLowerCase()}`}</option>\n )}\n {opts.map((o) => {\n const [v, l] = genericOptionAsTuple(o);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n );\n }\n\n if (kind === 'group+hole') {\n const groupBy = selector.groupBy;\n const groupValue = selector.groupValue ?? '';\n const groupLabel = selector.groupLabel || 'Group';\n const groupOptions = selector.groupOptions\n || groupValuesFromHoles(holeOptions, groupBy);\n const visibleHoles = filterHolesByGroup(holeOptions, groupBy, groupValue);\n return (\n <>\n <select\n className=\"plot-select plot-select--group\"\n value={groupValue}\n onChange={(e) => selector.onGroupChange && selector.onGroupChange(e.target.value)}\n disabled={groupOptions.length === 0}\n aria-label={groupLabel}\n >\n {groupOptions.length === 0 && <option value=\"\">No {groupLabel.toLowerCase()}s</option>}\n {!groupValue && groupOptions.length > 0 && (\n <option value=\"\" disabled hidden>{`Select ${groupLabel.toLowerCase()}`}</option>\n )}\n {groupOptions.map((g) => {\n const [v, l] = genericOptionAsTuple(g);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n <select\n className=\"plot-select plot-select--hole\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n disabled={visibleHoles.length === 0}\n aria-label=\"Hole\"\n >\n {visibleHoles.length === 0 && <option value=\"\">No holes</option>}\n {!selectedHoleId && visibleHoles.length > 0 && (\n <option value=\"\" disabled hidden>Select a hole</option>\n )}\n {visibleHoles.map((h) => {\n const [v, l] = holeOptionAsTuple(h);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n </>\n );\n }\n\n // kind === 'hole' (default)\n const enabled = holeOptions.length > 0;\n return (\n <select\n className=\"plot-select plot-select--hole\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n disabled={!enabled}\n aria-label=\"Hole\"\n >\n {!enabled && <option value=\"\">No holes loaded</option>}\n {!selectedHoleId && enabled && (\n <option value=\"\" disabled hidden>Select a hole</option>\n )}\n {holeOptions.map((h) => {\n const [v, l] = holeOptionAsTuple(h);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n );\n}\n\n/**\n * Plotly-based trace plot component for drillhole data.\n *\n * Hole / property / chart-type selects render in every state — including\n * the empty, loading, no-data and error states — so the user can always\n * change selection. The body switches between the Plotly chart and a\n * placeholder message; the controls do not move.\n *\n * @param {Object} props\n * @param {Object} props.config - Plot configuration {holeId, property, chartType}\n * @param {Object} props.graph - Graph data {hole, points, displayType, isCategorical, isComment, loading}\n * @param {Array} props.holeOptions - Available holes for dropdown\n * @param {Array} props.propertyOptions - Available properties for dropdown\n * @param {Function} props.onConfigChange - Handler for configuration changes\n * @param {Object} [props.propertyMeta] - Optional per-property metadata map\n * (`{ [property]: { label?, unit?, sourceAttribute? } }`). When the selected\n * property has an entry, its unit / source attribute are folded into the\n * axis title, hover tooltip and property dropdown label — e.g. \"Au (ppm)\".\n * The bare property key is still used for selection and `onConfigChange`.\n * @param {Object} [props.template] - Plotly template to apply. Defaults to the Baselode template.\n * @param {boolean} [props.showHoleSelect=true] - Render the hole selector area.\n * @param {boolean} [props.showPropertySelect=true] - Render the property select.\n * @param {boolean} [props.showChartTypeSelect=true] - Render the chart-type select (when >1 option).\n * @param {Object} [props.holeSelector] - Shape of the hole selector area.\n * Defaults to `{ kind: 'hole' }` (single hole dropdown).\n * - `{ kind: 'hole' }` — one dropdown over `holeOptions`, value = `config.holeId`.\n * - `{ kind: 'group+hole', groupBy, groupValue, onGroupChange, groupLabel?, groupOptions? }` —\n * a group dropdown plus a hole dropdown filtered to that group. `groupBy` is\n * either a string key on each hole option or a `(holeOption) => value` function.\n * - `{ kind: 'field', value, options, onChange, label? }` — a single dropdown\n * bound to an arbitrary field, fully controlled by the caller.\n * @returns {JSX.Element}\n */\nfunction TracePlot({\n config,\n graph,\n holeOptions = [],\n propertyOptions = [],\n propertyMeta,\n onConfigChange,\n template,\n showHoleSelect = true,\n showPropertySelect = true,\n showChartTypeSelect = true,\n holeSelector,\n}) {\n const bodyRef = useRef(null);\n const containerRef = useRef(null);\n const hole = graph?.hole;\n const points = graph?.points || [];\n const property = config?.property || '';\n const meta = propertyMeta?.[property];\n const chartType = config?.chartType || DEFAULT_NUMERIC_CHART_TYPE;\n const selectedHoleId = config?.holeId || '';\n\n // Derive display type from graph metadata (set by useDrillholeTraceGrid)\n const displayType = graph?.displayType\n || (graph?.isComment ? DISPLAY_COMMENT : (graph?.isCategorical ? DISPLAY_CATEGORICAL : DISPLAY_NUMERIC));\n\n const chartOptions = getChartOptions(displayType);\n const effectiveChartType = resolveChartType(displayType, chartType);\n\n const [renderError, setRenderError] = useState('');\n const [plotSize, setPlotSize] = useState({ width: 0, height: 0 });\n\n const bodyState = resolveTracePlotBody({\n holeId: selectedHoleId,\n hole,\n holeOptions,\n property,\n propertyOptions,\n displayType,\n points,\n renderError,\n });\n const isPlaceholder = bodyState.kind !== 'chart';\n\n const visibility = resolveTracePlotSelectVisibility({\n chartOptions,\n showHoleSelect,\n showPropertySelect,\n showChartTypeSelect,\n });\n const propertySelectEnabled = propertyOptions.length > 0;\n\n useEffect(() => {\n const body = bodyRef.current;\n if (!body || typeof ResizeObserver === 'undefined') return undefined;\n\n let frame = 0;\n const updatePlotSize = () => {\n if (frame) cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const width = Math.max(0, Math.floor(body.clientWidth));\n const height = Math.max(0, Math.floor(body.clientHeight));\n setPlotSize((prev) => (\n prev.width === width && prev.height === height ? prev : { width, height }\n ));\n });\n };\n\n updatePlotSize();\n const observer = new ResizeObserver(updatePlotSize);\n observer.observe(body);\n\n return () => {\n if (frame) cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, []);\n\n useEffect(() => {\n if (bodyState.kind !== 'chart') return;\n const target = containerRef.current;\n if (!target) return;\n if (plotSize.width <= 0 || plotSize.height <= 0) return;\n\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n\n let plotData;\n try {\n if (isComment) {\n plotData = buildCommentsConfig(points, { commentCol: property, fromCol: 'from', toCol: 'to' });\n } else if (isTadpole) {\n plotData = buildTadpoleConfig(points);\n } else {\n plotData = buildPlotConfig({\n points,\n isCategorical: displayType === DISPLAY_CATEGORICAL,\n property,\n chartType: effectiveChartType,\n template,\n meta,\n });\n }\n } catch (err) {\n console.error('Plot build error', err);\n setRenderError(err?.message || 'Plot build error');\n return;\n }\n\n if (!plotData?.data || plotData.data.length === 0) {\n if (!isComment) return;\n }\n\n const plotConfig = {\n displayModeBar: true,\n responsive: false,\n modeBarButtonsToRemove: ['select2d', 'lasso2d', 'autoScale2d']\n };\n const layout = {\n ...plotData.layout,\n autosize: false,\n width: plotSize.width,\n height: plotSize.height,\n };\n\n try {\n setRenderError('');\n Plotly.react(target, plotData.data, layout, plotConfig);\n } catch (err) {\n console.error('Plot render error', err);\n setRenderError(err?.message || 'Plot render error');\n }\n\n return () => {\n if (target) {\n try {\n Plotly.purge(target);\n } catch (err) {\n console.warn('Plot purge error', err);\n }\n }\n };\n }, [\n bodyState.kind,\n hole,\n property,\n meta,\n effectiveChartType,\n displayType,\n points,\n template,\n plotSize.width,\n plotSize.height,\n ]);\n\n useEffect(() => {\n const target = containerRef.current;\n if (!target || typeof ResizeObserver === 'undefined') return undefined;\n const resizeObserver = new ResizeObserver(() => {\n try {\n if (target && target.data) {\n const width = Math.max(0, Math.floor(target.clientWidth));\n const height = Math.max(0, Math.floor(target.clientHeight));\n if (width > 0 && height > 0) {\n Plotly.relayout(target, { width, height, autosize: false });\n }\n }\n } catch (err) {\n console.warn('Plot resize error', err);\n }\n });\n resizeObserver.observe(target);\n return () => resizeObserver.disconnect();\n }, [bodyState.kind]);\n\n return (\n <div className={`plot-card${isPlaceholder ? ' empty' : ''}`}>\n <header className=\"plot-card__controls\">\n {visibility.hole && (\n <div className=\"plot-title\">\n {renderHoleSelector({\n selector: holeSelector,\n holeOptions,\n selectedHoleId,\n onConfigChange,\n })}\n </div>\n )}\n {(visibility.property || visibility.chartType) && (\n <div className=\"plot-controls column\">\n {visibility.property && (\n <select\n className=\"plot-select plot-select--property\"\n value={property}\n onChange={(e) => onConfigChange && onConfigChange({ property: e.target.value })}\n disabled={!propertySelectEnabled}\n aria-label=\"Property\"\n >\n {!propertySelectEnabled && (\n <option value=\"\">—</option>\n )}\n {!property && propertySelectEnabled && (\n <option value=\"\" disabled hidden>Select a property</option>\n )}\n {propertyOptions.map((p) => (\n <option key={p} value={p}>{formatPropertyLabel(p, propertyMeta?.[p])}</option>\n ))}\n </select>\n )}\n {visibility.chartType && (\n <select\n className=\"plot-select plot-select--chart-type\"\n value={effectiveChartType}\n onChange={(e) => onConfigChange && onConfigChange({ chartType: e.target.value })}\n aria-label=\"Chart type\"\n >\n {chartOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n )}\n </header>\n <div className=\"plot-card__body\" ref={bodyRef}>\n {bodyState.kind === 'chart'\n ? <div className=\"plotly-chart\" ref={containerRef} />\n : <div className=\"placeholder\">{bodyState.text}</div>\n }\n </div>\n </div>\n );\n}\n\nexport default TracePlot;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n deriveAssayProps,\n loadAssayHole,\n loadAssayMetadata\n} from '../data/assayDataLoader.js';\nimport {\n buildTraceConfigsForHoleIds,\n coerceChartTypeForProperty,\n reorderHoleIds\n} from '../data/traceGridConfig.js';\nimport { buildIntervalPoints, holeHasData } from './drillholeViz.js';\n\n/**\n * Merge two arrays of hole objects by holeId.\n * For holes that appear in both arrays, their points are concatenated.\n * @private\n */\nfunction mergeHoleSets(primary, extra) {\n if (!extra?.length) return primary;\n const byId = new Map(primary.map((h) => [h.id || h.holeId, { ...h }]));\n for (const eh of extra) {\n const id = eh.id || eh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...(existing.points || []), ...(eh.points || [])] });\n } else {\n byId.set(id, eh);\n }\n }\n return Array.from(byId.values());\n}\n\n/**\n * Build comment-type interval points from a hole.\n * Unlike buildIntervalPoints, this keeps intervals with empty/null comment values\n * so that buildCommentsConfig can draw the interval border rectangles.\n * @private\n */\nfunction buildCommentPoints(hole, property) {\n if (!hole || !property) return [];\n const seen = new Set();\n const out = [];\n for (const p of hole.points || []) {\n const from = Number(p.from ?? p.samp_from ?? p.depth_from ?? p.from_depth);\n const to = Number(p.to ?? p.samp_to ?? p.depth_to ?? p.to_depth);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const key = `${from}-${to}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ from, to, [property]: p[property] ?? '' });\n }\n return out;\n}\n\n/**\n * React hook for managing a grid of drillhole trace plots.\n * Handles loading assay data, optional extra hole data (e.g. structural intervals),\n * column metadata classification, and trace config coordination.\n *\n * @param {Object} options\n * @param {string} options.initialFocusedHoleId - Initial focused hole ID\n * @param {File|Blob|null} options.sourceFile - Assay data CSV file\n * @param {Array<Object>} options.extraHoles - Pre-parsed extra hole data (e.g. structural)\n * @param {number} options.plotCount - Number of plots in grid (default: 4)\n * @returns {Object} Hook state and actions\n */\nexport default function useDrillholeTraceGrid({\n initialFocusedHoleId = '',\n sourceFile = null,\n extraHoles = [],\n plotCount = 4\n} = {}) {\n const [holes, setHoles] = useState([]);\n const [holeIds, setHoleIds] = useState([]);\n const [numericProps, setNumericProps] = useState([]);\n const [categoricalProps, setCategoricalProps] = useState([]);\n const [commentProps, setCommentProps] = useState([]);\n const [columnMeta, setColumnMeta] = useState({});\n const [defaultProp, setDefaultProp] = useState('');\n const [traceConfigs, setTraceConfigs] = useState([]);\n const [error, setError] = useState('');\n const [focusedHoleId, setFocusedHoleId] = useState(initialFocusedHoleId || '');\n const [loadingHoles, setLoadingHoles] = useState([]);\n const loadedSourceFileRef = useRef(null);\n\n const applyAssayState = (state) => {\n if (!state) return;\n setError('');\n setHoles(state.holes || []);\n setHoleIds((state.holes || []).map((h) => ({\n holeId: h.id || h.holeId\n })).filter((h) => h.holeId));\n setNumericProps(state.numericProps || []);\n setCategoricalProps(state.categoricalProps || []);\n setCommentProps(state.commentProps || []);\n setColumnMeta(state.columnMeta || {});\n setDefaultProp(state.defaultProp || '');\n setTraceConfigs(state.traceConfigs || []);\n };\n\n // Load metadata (hole IDs) from assay CSV on first mount\n useEffect(() => {\n if (!sourceFile || loadedSourceFileRef.current === sourceFile) return;\n loadedSourceFileRef.current = sourceFile;\n loadAssayMetadata(sourceFile)\n .then((ids) => {\n if (!ids) return;\n const uniqueIds = Array.from(new Map(ids.map((h) => [h.holeId, h])).values());\n setHoleIds(uniqueIds);\n setTraceConfigs(buildTraceConfigsForHoleIds({\n holeIds: uniqueIds.map((h) => h.holeId),\n focusedHoleId,\n plotCount,\n defaultProp: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n }));\n })\n .catch((err) => {\n console.info('Assay metadata load skipped:', err.message);\n });\n }, [sourceFile, focusedHoleId, plotCount, categoricalProps, commentProps]);\n\n // Inject extra holes (structural etc.) into holeIds — always, regardless of whether\n // an assay sourceFile is also present. This ensures structural-only holes appear in\n // the dropdown even when assay data is loaded from a separate file.\n useEffect(() => {\n if (!extraHoles?.length) return;\n const ids = extraHoles\n .map((h) => ({ holeId: h.id || h.holeId }))\n .filter((h) => h.holeId);\n setHoleIds((prev) => {\n const existing = new Set(prev.map((h) => h.holeId));\n const newIds = ids.filter((h) => !existing.has(h.holeId));\n return newIds.length ? [...prev, ...newIds] : prev;\n });\n }, [extraHoles]);\n\n useEffect(() => {\n setError((prev) => (prev && prev.startsWith('Loading ') && prev.includes(' for hole ') ? prev : ''));\n }, [traceConfigs]);\n\n useEffect(() => {\n if (!holeIds.length) {\n setTraceConfigs([]);\n return;\n }\n const orderedHoleIds = reorderHoleIds(holeIds.map((h) => h.holeId), focusedHoleId);\n setTraceConfigs((prev) => {\n const next = Array.from({ length: plotCount }).map((_, idx) => {\n const existing = prev[idx] || {};\n const holeId = holeIds.some((h) => h.holeId === existing.holeId) ? existing.holeId : orderedHoleIds[idx] || holeIds[idx]?.holeId || '';\n const property = existing.property || defaultProp;\n const chartType = coerceChartTypeForProperty({\n property,\n chartType: existing.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n return { holeId, property, chartType };\n });\n return next;\n });\n }, [holeIds, focusedHoleId, defaultProp, categoricalProps, commentProps, plotCount]);\n\n // Load assay hole data on demand as trace configs change\n useEffect(() => {\n if (!sourceFile) return;\n const needed = traceConfigs.map((cfg) => cfg.holeId).filter(Boolean);\n needed.forEach((holeId) => {\n const already = holes.some((h) => (h.id || h.holeId) === holeId);\n const loading = loadingHoles.includes(holeId);\n if (already || loading) return;\n setLoadingHoles((prev) => [...prev, holeId]);\n loadAssayHole(sourceFile, holeId)\n .then((hole) => {\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n if (!hole) return;\n setHoles((prev) => {\n const merged = mergeHoleSets(\n [...prev.filter((h) => (h.id || h.holeId) !== holeId), hole],\n extraHoles\n );\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) {\n setDefaultProp(props.defaultProp);\n setTraceConfigs((configs) => configs.map((cfg) => ({\n ...cfg,\n property: cfg.property || props.defaultProp,\n chartType: coerceChartTypeForProperty({\n property: cfg.property || props.defaultProp,\n chartType: cfg.chartType,\n categoricalProps: props.categoricalProps,\n commentProps: props.commentProps,\n numericDefaultChartType: 'markers+line'\n })\n })));\n }\n return merged;\n });\n })\n .catch((err) => {\n console.error(err);\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n setError(err.message || `Error loading hole ${holeId}`);\n });\n });\n }, [traceConfigs, sourceFile, holes, loadingHoles, defaultProp, extraHoles]);\n\n // Merge extra holes whenever they change (and assay holes are present)\n useEffect(() => {\n if (!extraHoles?.length) return;\n setHoles((prev) => {\n if (!prev.length) {\n // No assay data yet — seed from extra holes only\n const props = deriveAssayProps(extraHoles);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return extraHoles;\n }\n const merged = mergeHoleSets(prev, extraHoles);\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return merged;\n });\n }, [extraHoles]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const propertyOptions = useMemo(\n () => [...numericProps, ...categoricalProps, ...commentProps],\n [numericProps, categoricalProps, commentProps]\n );\n\n const labeledHoleOptions = useMemo(\n () => holeIds\n .map((h) => ({ holeId: h.holeId, label: h.holeId }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n [holeIds]\n );\n\n const traceGraphs = useMemo(() => {\n const allProps = [...numericProps, ...categoricalProps, ...commentProps];\n return Array.from({ length: plotCount }).map((_, idx) => {\n const cfg = traceConfigs[idx] || {};\n const hole = holes.find((h) => (h.id || h.holeId) === cfg.holeId) || null;\n\n // Per-hole property list: only columns this hole actually has data for\n const holePropertyOptions = hole\n ? allProps.filter((p) => holeHasData(hole, p))\n : allProps;\n\n let property = cfg.property || defaultProp;\n // Auto-select first available property if the configured one has no data for this hole\n if (hole && !holePropertyOptions.includes(property)) {\n property = holePropertyOptions[0] || property;\n }\n\n const isComment = commentProps.includes(property);\n const isCategorical = !isComment && categoricalProps.includes(property);\n const isTadpole = !isComment && !isCategorical && property === 'dip';\n const displayType = isComment ? 'comment' : isTadpole ? 'tadpole' : (isCategorical ? 'categorical' : 'numeric');\n\n const chartType = isTadpole ? 'tadpole' : cfg.chartType || (isComment ? 'comment' : (isCategorical ? 'categorical' : 'markers+line'));\n const holeId = cfg.holeId || hole?.id || hole?.holeId || '';\n\n const points = isTadpole\n ? (hole?.points || [])\n : isComment\n ? buildCommentPoints(hole, property)\n : buildIntervalPoints(hole, property, isCategorical);\n\n return {\n config: { holeId, property, chartType },\n hole,\n loading: loadingHoles.includes(cfg.holeId),\n isCategorical,\n isComment,\n isTadpole,\n displayType,\n points,\n propertyOptions: holePropertyOptions,\n label: holeId\n };\n });\n }, [traceConfigs, holes, defaultProp, categoricalProps, commentProps, loadingHoles, plotCount, numericProps]);\n\n const handleConfigChange = (index, patch) => {\n setTraceConfigs((prev) => {\n const next = [...prev];\n const base = next[index] || {};\n const merged = { ...base, ...patch };\n if (patch.property) {\n merged.chartType = coerceChartTypeForProperty({\n property: patch.property,\n chartType: merged.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n }\n next[index] = merged;\n return next;\n });\n };\n\n return {\n error,\n focusedHoleId,\n setFocusedHoleId,\n setError,\n holeCount: holeIds.length,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n propertyOptions,\n labeledHoleOptions,\n traceGraphs,\n handleConfigChange\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n * @param {*} rows - Value to convert to array\n * @returns {Array} Array\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n * @param {*} value - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Normalize a point to ensure x, y, z are finite numbers\n * @private\n * @param {Object} point - Point object\n * @returns {Object} Normalized point\n */\nfunction normalizePoint(point = {}) {\n return {\n ...point,\n x: toNumber(point.x),\n y: toNumber(point.y),\n z: toNumber(point.z)\n };\n}\n\n/**\n * Project 3D trace points onto a 2D cross-section plane\n * @param {Array<Object>} traces - Array of trace points with x, y, z coordinates\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @returns {Array<Object>} Trace points with added 'along' and 'across' section coordinates\n */\nexport function projectTraceToSection(traces = [], origin = [0, 0], azimuth = 0) {\n const [ox, oy] = origin;\n const azRad = (Number(azimuth) * Math.PI) / 180;\n const cosA = Math.cos(azRad);\n const sinA = Math.sin(azRad);\n\n return toArray(traces)\n .map(normalizePoint)\n .map((row) => {\n if (!Number.isFinite(row.x) || !Number.isFinite(row.y)) return { ...row };\n const dx = row.x - ox;\n const dy = row.y - oy;\n return {\n ...row,\n along: (dx * sinA) + (dy * cosA),\n across: (dx * cosA) - (dy * sinA)\n };\n });\n}\n\n/**\n * Filter trace points to those within a width window around a section line\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width (points within ±width/2 are included)\n * @returns {Array<Object>} Filtered trace points within section window\n */\nexport function sectionWindow(traces = [], origin = [0, 0], azimuth = 0, width = 50) {\n const projected = projectTraceToSection(traces, origin, azimuth);\n const half = 0.5 * Number(width || 0);\n if (!Number.isFinite(half) || half <= 0) return projected;\n return projected.filter((row) => Number.isFinite(row.across) && Math.abs(row.across) <= half);\n}\n\n/**\n * Generate plan view of traces, optionally filtered by depth slice\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>|null} depthSlice - Optional [top, bottom] depth range to filter\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Trace points for plan view\n */\nexport function planView(traces = [], depthSlice = null, colorBy = null) {\n let rows = toArray(traces).map(normalizePoint);\n if (Array.isArray(depthSlice) && depthSlice.length === 2) {\n const [top, bottom] = depthSlice;\n rows = rows.filter((row) => Number.isFinite(row.z) && row.z <= Number(top) && row.z >= Number(bottom));\n }\n if (colorBy) {\n rows = rows.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return rows;\n}\n\n/**\n * Generate cross-section view of traces within a window\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Section trace points with along/across coordinates\n */\nexport function sectionView(traces = [], origin = [0, 0], azimuth = 0, width = 50, colorBy = null) {\n let section = sectionWindow(traces, origin, azimuth, width);\n if (colorBy) {\n section = section.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return section;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Extract hole ID from a row using common field name variations\n * @private\n */\nfunction getHoleId(row = {}) {\n return row.hole_id ?? row.holeId ?? row.id;\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Convert trace points to line segments grouped by hole ID\n * @param {Array<Object>} traces - Array of trace points with x, y, z, md coordinates\n * @param {string|null} colorBy - Optional property name for per-point color values\n * @returns {Array<{hole_id: string, x: Array<number>, y: Array<number>, z: Array<number>, color: Array|null}>} Array of line segments\n */\nexport function tracesAsSegments(traces = [], colorBy = null) {\n const grouped = new Map();\n\n toArray(traces).forEach((row) => {\n const holeId = getHoleId(row);\n if (holeId === undefined || holeId === null || `${holeId}`.trim() === '') return;\n const key = `${holeId}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(row);\n });\n\n const segments = [];\n grouped.forEach((rows, holeId) => {\n const sorted = [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0));\n const payload = {\n hole_id: holeId,\n x: sorted.map((row) => toNumber(row.x, 0)),\n y: sorted.map((row) => toNumber(row.y, 0)),\n z: sorted.map((row) => toNumber(row.z, 0)),\n color: null\n };\n if (colorBy) {\n payload.color = sorted.map((row) => row?.[colorBy]);\n }\n segments.push(payload);\n });\n\n return segments;\n}\n\n/**\n * Convert interval data to tube/cylinder representations for 3D rendering\n * @param {Array<Object>} intervals - Array of interval objects with from/to depths and x, y, z coordinates\n * @param {number} radius - Tube radius\n * @param {string|null} colorBy - Optional property name for color value\n * @returns {Array<{hole_id: string, x: number, y: number, z: number, from: number, to: number, radius: number, color_value: *}>} Array of tube specifications\n */\nexport function intervalsAsTubes(intervals = [], radius = 1, colorBy = null) {\n return toArray(intervals).map((row) => ({\n hole_id: getHoleId(row),\n from: row?.from,\n to: row?.to,\n radius,\n color: colorBy ? row?.[colorBy] : null,\n value: colorBy ? row?.[colorBy] : null\n }));\n}\n\n/**\n * Extract annotation labels from interval data for 3D text display\n * @param {Array<Object>} intervals - Array of interval objects\n * @param {string|null} labelCol - Property name to use for label text\n * @returns {Array<{hole_id: string, label: string, depth: number}>} Array of annotations with computed mid-depth\n */\nexport function annotationsFromIntervals(intervals = [], labelCol = null) {\n if (!labelCol) return [];\n return toArray(intervals)\n .filter((row) => Object.prototype.hasOwnProperty.call(row || {}, labelCol))\n .map((row) => ({\n hole_id: getHoleId(row),\n label: row?.[labelCol],\n depth: 0.5 * ((toNumber(row?.from, 0)) + (toNumber(row?.to, 0)))\n }));\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './Baselode3DControls.css';\n\n/**\n * 3D scene control buttons component\n * Provides UI controls for camera manipulation in the 3D drillhole viewer\n * @param {Object} props - Component props\n * @param {string} props.controlMode - Current control mode ('orbit' or 'fly')\n * @param {Function} props.onToggleFly - Handler for toggling fly mode\n * @param {Function} props.onRecenter - Handler for recentering camera\n * @param {Function} props.onLookDown - Handler for top-down view\n * @param {Function} props.onFit - Handler for fitting camera to scene\n * @returns {JSX.Element} Control buttons component\n */\nfunction Baselode3DControls({\n controlMode = 'orbit',\n onToggleFly = () => {},\n onRecenter = () => {},\n onLookDown = () => {},\n onFit = () => {},\n darkBackground = false,\n onToggleDarkBackground = () => {},\n}) {\n return (\n <div className=\"baselode-3d-controls\">\n <button type=\"button\" className=\"ghost-button\" onClick={onRecenter}>\n Recenter to (0,0,0)\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onLookDown}>\n Look down\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onFit}>\n Fit to scene\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onToggleFly}>\n {controlMode === 'orbit' ? 'Enable fly controls' : 'Disable fly controls'}\n </button>\n <label className=\"baselode-3d-controls-checkbox\">\n <input\n type=\"checkbox\"\n checked={darkBackground}\n onChange={onToggleDarkBackground}\n />\n Dark background\n </label>\n </div>\n );\n}\n\nexport default Baselode3DControls;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './BlockModelWidget.css';\n\n/**\n * A panel of controls for exploring a block model in the 3-D scene.\n *\n * Exports widget state (attribute selection, opacity) so that a wrapping\n * application can pass the values on to the 3-D scene. Renders:\n * - An attribute (color-by) drop-down selector\n * - A translucency / opacity slider\n * - A simple color-scale legend (numeric attributes)\n * - A popup card showing all attributes of the last clicked block\n *\n * @param {Object} props\n * @param {Array<string>} props.properties - List of attribute column names\n * @param {string} props.selectedProperty - Currently selected attribute\n * @param {Function} props.onPropertyChange - Called with new property name string\n * @param {number} props.opacity - Current opacity value (0–1, default 0.85)\n * @param {Function} props.onOpacityChange - Called with new opacity number\n * @param {Object|null} props.propertyStats - Stats object for the selected\n * property (``{type, min?, max?, categories?}`` from calculatePropertyStats)\n * @param {Object|null} props.clickedBlock - Block row data to display in the\n * popup, or null when no block is selected\n * @param {Function} props.onPopupClose - Called when the user dismisses the popup\n */\nfunction BlockModelWidget({\n properties = [],\n selectedProperty = '',\n onPropertyChange = () => {},\n opacity = 0.85,\n onOpacityChange = () => {},\n propertyStats = null,\n clickedBlock = null,\n onPopupClose = () => {},\n}) {\n return (\n <div className=\"bm-widget\">\n {/* Attribute selector */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-property-select\">\n Color by\n </label>\n <select\n id=\"bm-property-select\"\n className=\"bm-widget__select\"\n value={selectedProperty}\n onChange={(e) => onPropertyChange(e.target.value)}\n >\n {properties.length === 0 && (\n <option value=\"\">— no attributes —</option>\n )}\n {properties.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n\n {/* Color scale legend */}\n {propertyStats && propertyStats.type === 'numeric' && (\n <div className=\"bm-widget__scale\">\n <span className=\"bm-widget__scale-label bm-widget__scale-label--min\">\n {propertyStats.min?.toFixed(2) ?? '—'}\n </span>\n <div className=\"bm-widget__scale-bar\" />\n <span className=\"bm-widget__scale-label bm-widget__scale-label--max\">\n {propertyStats.max?.toFixed(2) ?? '—'}\n </span>\n </div>\n )}\n {propertyStats && propertyStats.type === 'categorical' && (\n <div className=\"bm-widget__categories\">\n {(propertyStats.categories || []).map((cat, i) => {\n const hue = Math.round((i / Math.max(propertyStats.categories.length, 1)) * 360);\n return (\n <span key={cat} className=\"bm-widget__category-chip\"\n style={{ background: `hsl(${hue},70%,50%)` }}>\n {cat}\n </span>\n );\n })}\n </div>\n )}\n\n {/* Opacity slider */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-opacity-slider\">\n Opacity ({Math.round(opacity * 100)}%)\n </label>\n <input\n id=\"bm-opacity-slider\"\n type=\"range\"\n className=\"bm-widget__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={opacity}\n onChange={(e) => onOpacityChange(parseFloat(e.target.value))}\n />\n\n {/* Block attribute popup */}\n {clickedBlock && (\n <div className=\"bm-widget__popup\">\n <div className=\"bm-widget__popup-header\">\n <span>Block attributes</span>\n <button\n type=\"button\"\n className=\"bm-widget__popup-close\"\n onClick={onPopupClose}\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n <table className=\"bm-widget__popup-table\">\n <tbody>\n {Object.entries(clickedBlock).map(([key, value]) => (\n <tr key={key}>\n <th>{key}</th>\n <td>{value === null || value === undefined ? '—' : String(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nexport default BlockModelWidget;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Core photo table visualization helpers.\n *\n * Provides depth-ordered layout utilities, LOD image selection, and depth\n * marker generation for the CorePhotoTable component.\n */\n\n/**\n * Default LOD (Level of Detail) breakpoints.\n *\n * At low zoom values small/thumbnail images are loaded; at high zoom values\n * full-resolution images are used. Each entry specifies the minimum zoom level\n * that activates the corresponding LOD key.\n *\n * Photo objects may supply a ``lod_urls`` map keyed by these values. If a\n * photo has no ``lod_urls``, ``image_url`` is used regardless of zoom.\n */\nexport const DEFAULT_LOD_BREAKPOINTS = [\n { minZoom: 0, lodKey: 'thumb' },\n { minZoom: 4, lodKey: 'medium' },\n { minZoom: 7, lodKey: 'full' },\n];\n\n/**\n * Base pixels-per-metre at zoom level 5. At other zoom levels this value is\n * scaled linearly so that ``pixelsPerMetre = BASE_PIXELS_PER_METRE * zoom / 5``.\n */\nexport const BASE_PIXELS_PER_METRE = 50;\n\n/**\n * Select the image URL appropriate for the given zoom level.\n *\n * If the photo object contains a ``lod_urls`` map the function returns the\n * URL for the highest LOD whose ``minZoom`` is still ≤ the current zoom\n * level. If no ``lod_urls`` are present (or the resolved key is missing)\n * the function falls back to ``photo.image_url``.\n *\n * @param {Object} photo - Photo entry. Expected fields: ``image_url`` (string),\n * ``lod_urls`` (optional object mapping lodKey → URL string).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {Array<{minZoom: number, lodKey: string}>} [lodBreakpoints] - LOD\n * breakpoints to use. Defaults to {@link DEFAULT_LOD_BREAKPOINTS}.\n * @returns {string} The selected image URL, or an empty string if none found.\n */\nexport function selectPhotoLodUrl(photo, zoom, lodBreakpoints = DEFAULT_LOD_BREAKPOINTS) {\n const lodUrls = photo.lod_urls;\n if (!lodUrls || typeof lodUrls !== 'object' || Array.isArray(lodUrls)) {\n return photo.image_url || '';\n }\n\n // Walk breakpoints and keep the last one whose minZoom ≤ zoom.\n let selectedKey = lodBreakpoints[0]?.lodKey ?? 'thumb';\n for (const bp of lodBreakpoints) {\n if (zoom >= bp.minZoom) {\n selectedKey = bp.lodKey;\n }\n }\n\n return lodUrls[selectedKey] || photo.image_url || '';\n}\n\n/**\n * Return a copy of ``photos`` sorted ascending by ``from_depth``.\n *\n * @param {Array<Object>} photos - Array of photo objects, each with a\n * numeric ``from_depth`` field.\n * @returns {Array<Object>} New sorted array.\n */\nexport function sortPhotosByDepth(photos) {\n return [...photos].sort((a, b) => (a.from_depth ?? 0) - (b.from_depth ?? 0));\n}\n\n/**\n * Group photos by their ``photo_set`` value.\n *\n * Photos without a ``photo_set`` (null, undefined, or empty string) are\n * placed in the ``'default'`` group. The returned object's keys preserve\n * insertion order (first-seen order within ``photos``).\n *\n * @param {Array<Object>} photos - Array of photo objects.\n * @returns {Object.<string, Array<Object>>} Map of set name → photo array.\n */\nexport function groupPhotosBySet(photos) {\n /** @type {Object.<string, Array<Object>>} */\n const sets = {};\n for (const photo of photos) {\n const key =\n photo.photo_set != null && photo.photo_set !== ''\n ? String(photo.photo_set)\n : 'default';\n if (!sets[key]) sets[key] = [];\n sets[key].push(photo);\n }\n return sets;\n}\n\n/**\n * Build an array of depth marker descriptors at regular intervals.\n *\n * Markers start at the first multiple of ``intervalMetres`` that is ≥\n * ``minDepth`` and continue up to and including ``maxDepth``.\n *\n * @param {number} minDepth - Shallowest depth of the view (m).\n * @param {number} maxDepth - Deepest depth of the view (m).\n * @param {number} [intervalMetres=10] - Spacing between markers in metres.\n * @returns {Array<{depth: number, label: string}>} Ordered marker descriptors.\n */\nexport function buildDepthMarkers(minDepth, maxDepth, intervalMetres = 10) {\n const markers = [];\n if (minDepth >= maxDepth || intervalMetres <= 0) return markers;\n\n const first = Math.ceil(minDepth / intervalMetres) * intervalMetres;\n // Guard against floating-point drift with a small epsilon.\n const epsilon = intervalMetres * 1e-9;\n for (let d = first; d <= maxDepth + epsilon; d += intervalMetres) {\n const rounded = Math.round(d * 1e6) / 1e6;\n markers.push({ depth: rounded, label: `${rounded} m` });\n }\n return markers;\n}\n\n/**\n * Choose an appropriate depth-marker interval (in metres) for the given zoom\n * level so that labels are legible without overcrowding.\n *\n * @param {number} zoom - Current zoom level (1–10).\n * @returns {number} Interval in metres.\n */\nexport function depthMarkerInterval(zoom) {\n if (zoom >= 9) return 1;\n if (zoom >= 7) return 2;\n if (zoom >= 5) return 5;\n if (zoom >= 3) return 10;\n return 20;\n}\n\n/**\n * Default filename generator used by {@link buildTrayPhotos}.\n *\n * @param {number} index - Zero-based tray index.\n * @returns {string}\n */\nexport function defaultTrayFilename(index) {\n return `tray_${String(index).padStart(3, '0')}.jpg`;\n}\n\n/**\n * Build a CorePhotoTable-compatible photos array from tray depth intervals\n * and a pair of image base URLs.\n *\n * This is the same transform performed internally by CorePhotoViewer; it is\n * exported so callers that need to combine multiple datasets (e.g. two\n * concurrent scan runs for the same hole) can build the array themselves and\n * pass it directly to CorePhotoTable.\n *\n * @param {string} holeId - Drillhole identifier stored on every photo entry.\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} trays - One entry per image, in any depth order.\n * @param {string} thumbBaseUrl - URL prefix for thumbnail images.\n * @param {string} fullBaseUrl - URL prefix for full-resolution images.\n * @param {string} [photoSet='Tray Images'] - Default column label; overridden\n * per-tray via ``tray.photoSet``.\n * @param {function} [getFilename] - ``(index: number) => string`` filename\n * generator. ``index`` is the zero-based position in ``trays``. Defaults to\n * {@link defaultTrayFilename} (``tray_000.jpg``, ``tray_001.jpg``, …).\n * @returns {Array<Object>} Photos array suitable for ``CorePhotoTable.photos``.\n */\nexport function buildTrayPhotos(\n holeId,\n trays,\n thumbBaseUrl,\n fullBaseUrl,\n photoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n) {\n const thumb = (thumbBaseUrl ?? '').replace(/\\/$/, '');\n const full = (fullBaseUrl ?? '').replace(/\\/$/, '');\n return trays.map((tray, index) => {\n const filename = tray.filename ?? getFilename(index);\n const set = tray.photoSet ?? photoSet;\n return {\n hole_id: holeId,\n from_depth: tray.fromDepth,\n to_depth: tray.toDepth,\n photo_set: set,\n image_url: `${thumb}/${filename}`,\n lod_urls: {\n thumb: `${thumb}/${filename}`,\n full: `${full}/${filename}`,\n },\n };\n });\n}\n\n/**\n * Convert a depth interval in metres to a pixel height for the given zoom.\n *\n * Uses a linear scale so that the displayed height is always proportional to\n * the physical depth span, enabling correct alignment across photo columns.\n *\n * @param {number} depthInterval - Depth span in metres (``to_depth - from_depth``).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {number} [basePixelsPerMetre] - Pixels per metre at zoom 5.\n * Defaults to {@link BASE_PIXELS_PER_METRE}.\n * @returns {number} Pixel height (always ≥ 1).\n */\nexport function depthIntervalToPixels(depthInterval, zoom, basePixelsPerMetre = BASE_PIXELS_PER_METRE) {\n const scale = zoom / 5;\n return Math.max(1, Math.round(depthInterval * basePixelsPerMetre * scale));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n BASE_PIXELS_PER_METRE,\n buildDepthMarkers,\n depthMarkerInterval,\n groupPhotosBySet,\n selectPhotoLodUrl,\n sortPhotosByDepth,\n} from './corePhotoViz.js';\nimport './CorePhotoTable.css';\n\nconst BASE_ZOOM = 5; // fixed layout zoom (no reflow on pan/zoom)\nconst ZOOM_FACTOR = 1.12; // scale multiplier per wheel tick\nconst SCALE_MIN = 0.05;\nconst SCALE_MAX = 40;\n\n/**\n * CorePhotoTable\n *\n * Renders a pannable/zoomable core photograph table with:\n *\n * - Depth-ordered images stacked from shallow → deep\n * - Multiple photo sets displayed side-by-side (one column per set)\n * - A depth ruler for spatial orientation\n * - Mouse-wheel zoom centred on cursor (Leaflet-style)\n * - Left-click drag to pan\n *\n * @param {Object} props\n * @param {Array<CorePhoto>} [props.photos=[]] - Photo entries to display.\n * Each entry should have: ``hole_id`` (string), ``from_depth`` (number),\n * ``to_depth`` (number), ``image_url`` (string), ``photo_set`` (string,\n * optional), ``lod_urls`` (object, optional).\n * @param {string} [props.holeId=''] - Hole identifier shown in the control bar.\n * @param {number} [props.initialZoom=5] - Starting zoom level (1–10) for LOD.\n */\nexport function CorePhotoTable({\n photos = [],\n holeId = '',\n initialZoom = 5,\n transform: controlledTransform,\n onTransformChange,\n}) {\n // ── Pan / zoom transform ────────────────────────────────────────────────\n // Supports both uncontrolled (internal state) and controlled modes.\n // Pass transform + onTransformChange to share state across multiple tables.\n const [internalTransform, setInternalTransform] = useState({ scale: 1, tx: 0, ty: 0 });\n const transform = controlledTransform ?? internalTransform;\n const transformRef = useRef(transform);\n transformRef.current = transform;\n\n // Always resolves functional updaters to a plain object before dispatching,\n // so onTransformChange is always called with a transform object, never a function.\n const setTransform = useCallback(\n (updater) => {\n const next = typeof updater === 'function' ? updater(transformRef.current) : updater;\n if (onTransformChange) {\n onTransformChange(next);\n } else {\n setInternalTransform(next);\n }\n },\n [onTransformChange],\n );\n\n const [dragging, setDragging] = useState(false);\n const dragOrigin = useRef(null);\n\n const viewportRef = useRef(null);\n\n // ── Derived layout values (fixed at BASE_ZOOM, no reflow on pan/zoom) ──\n\n const sorted = useMemo(() => sortPhotosByDepth(photos), [photos]);\n const grouped = useMemo(() => groupPhotosBySet(sorted), [sorted]);\n\n // Derive column order from the original (unsorted) photos so that callers\n // control left-to-right ordering by the sequence they pass photos in.\n const setNames = useMemo(() => {\n const seen = new Set();\n for (const p of photos) {\n const key = p.photo_set != null && p.photo_set !== '' ? String(p.photo_set) : 'default';\n seen.add(key);\n }\n return [...seen];\n }, [photos]);\n\n const { minDepth, maxDepth } = useMemo(() => {\n if (!sorted.length) return { minDepth: 0, maxDepth: 0 };\n const allTo = sorted.map((p) => p.to_depth ?? p.from_depth ?? 0);\n return {\n minDepth: sorted[0].from_depth ?? 0,\n maxDepth: Math.max(...allTo),\n };\n }, [sorted]);\n\n const pixelsPerMetre = (BASE_PIXELS_PER_METRE * BASE_ZOOM) / 5;\n\n const totalHeight = useMemo(\n () => Math.max(1, Math.round((maxDepth - minDepth) * pixelsPerMetre)),\n [minDepth, maxDepth, pixelsPerMetre],\n );\n\n const markers = useMemo(() => {\n const interval = depthMarkerInterval(BASE_ZOOM);\n return buildDepthMarkers(minDepth, maxDepth, interval);\n }, [minDepth, maxDepth]);\n\n const imageWidth = (540 * BASE_ZOOM) / 5; // fixed 540 px at base zoom\n\n // Effective LOD zoom derived from visual scale so thumbnails swap to full\n // resolution when the user zooms in close enough.\n const lodZoom = useMemo(\n () => Math.max(1, Math.min(10, Math.round(initialZoom * transform.scale))),\n [initialZoom, transform.scale],\n );\n\n // ── Wheel zoom (non-passive, centred on cursor) ─────────────────────────\n\n const handleWheel = useCallback((e) => {\n e.preventDefault();\n const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;\n const rect = viewportRef.current.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n\n setTransform((prev) => {\n const newScale = Math.max(SCALE_MIN, Math.min(SCALE_MAX, prev.scale * factor));\n const ratio = newScale / prev.scale;\n return {\n scale: newScale,\n tx: cx - (cx - prev.tx) * ratio,\n ty: cy - (cy - prev.ty) * ratio,\n };\n });\n }, [setTransform]);\n\n useEffect(() => {\n const el = viewportRef.current;\n if (!el) return;\n el.addEventListener('wheel', handleWheel, { passive: false });\n return () => el.removeEventListener('wheel', handleWheel);\n }, [handleWheel]);\n\n // ── Left-drag pan ───────────────────────────────────────────────────────\n\n const handleMouseDown = useCallback((e) => {\n if (e.button !== 0) return;\n e.preventDefault();\n dragOrigin.current = {\n x: e.clientX,\n y: e.clientY,\n tx: transformRef.current.tx,\n ty: transformRef.current.ty,\n };\n setDragging(true);\n }, []);\n\n useEffect(() => {\n const handleMouseMove = (e) => {\n if (!dragOrigin.current) return;\n // Snapshot ref values before entering the async updater — the ref may\n // be nulled by handleMouseUp before React executes the updater.\n const { tx, ty, x, y } = dragOrigin.current;\n setTransform((prev) => ({\n ...prev,\n tx: tx + (e.clientX - x),\n ty: ty + (e.clientY - y),\n }));\n };\n const handleMouseUp = () => {\n dragOrigin.current = null;\n setDragging(false);\n };\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n };\n }, [setTransform]);\n\n // ── Render ──────────────────────────────────────────────────────────────\n\n return (\n <div className=\"core-photo-table\">\n {/* Controls */}\n <div className=\"core-photo-controls\">\n {holeId && <span className=\"core-photo-hole-id\">{holeId}</span>}\n <span className=\"core-photo-zoom-label\">\n {Math.round(transform.scale * 100)}%\n </span>\n <button\n className=\"core-photo-zoom-btn\"\n onClick={() => setTransform({ scale: 1, tx: 0, ty: 0 })}\n aria-label=\"Reset view\"\n title=\"Reset view\"\n >\n ⌂\n </button>\n </div>\n\n {/* Column label strip */}\n {photos.length > 0 && (\n <div className=\"core-photo-col-headers\">\n <div className=\"core-photo-ruler-spacer\" />\n {setNames.map((name) => (\n <div\n key={name}\n className=\"core-photo-set-header\"\n style={{ width: imageWidth }}\n >\n {name}\n </div>\n ))}\n </div>\n )}\n\n {/* Body */}\n {photos.length === 0 ? (\n <div className=\"core-photo-empty\">No photos to display.</div>\n ) : (\n <div\n className={`core-photo-scroll${dragging ? ' is-dragging' : ''}`}\n ref={viewportRef}\n onMouseDown={handleMouseDown}\n >\n <div\n className=\"core-photo-inner\"\n style={{\n height: totalHeight,\n transform: `translate(${transform.tx}px, ${transform.ty}px) scale(${transform.scale})`,\n transformOrigin: '0 0',\n }}\n >\n {/* Depth ruler */}\n <div\n className=\"core-photo-depth-ruler\"\n style={{ height: totalHeight }}\n >\n {markers.map(({ depth, label }) => (\n <div\n key={depth}\n className=\"core-photo-depth-marker\"\n style={{\n top: Math.round((depth - minDepth) * pixelsPerMetre),\n }}\n >\n {label}\n </div>\n ))}\n </div>\n\n {/* Photo set columns */}\n {setNames.map((setName) => (\n <div\n key={setName}\n className=\"core-photo-col-body\"\n style={{ height: totalHeight, width: imageWidth }}\n >\n {grouped[setName].map((photo) => {\n const fromDepth = photo.from_depth ?? 0;\n const toDepth = photo.to_depth ?? fromDepth;\n const top = Math.round(\n (fromDepth - minDepth) * pixelsPerMetre,\n );\n const height = Math.max(\n 2,\n Math.round((toDepth - fromDepth) * pixelsPerMetre),\n );\n const src = selectPhotoLodUrl(photo, lodZoom);\n\n return (\n <div\n key={`${photo.hole_id ?? ''}-${fromDepth}-${toDepth}-${setName}`}\n className=\"core-photo-item\"\n style={{ top, height, width: imageWidth }}\n title={`${fromDepth}–${toDepth} m`}\n >\n {src ? (\n <img\n src={src}\n alt={`Core ${fromDepth}–${toDepth} m`}\n loading=\"lazy\"\n />\n ) : (\n <div className=\"core-photo-no-image\" />\n )}\n {height >= 18 && (\n <span className=\"core-photo-item-label\">\n {fromDepth}–{toDepth} m\n </span>\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default CorePhotoTable;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useMemo } from 'react';\nimport CorePhotoTable from './CorePhotoTable.jsx';\nimport { buildTrayPhotos, defaultTrayFilename } from './corePhotoViz.js';\n\n/**\n * CorePhotoViewer\n * ===============\n *\n * A self-contained, pannable/zoomable core box photograph viewer for use in\n * any React application. Point it at a sequence of tray images (e.g. from an\n * NVCL HyLogger dataset) and it renders them depth-aligned with a ruler,\n * mouse-wheel zoom, and left-drag panning.\n *\n *\n * ## Minimal usage\n *\n * ```jsx\n * import { CorePhotoViewer } from 'baselode';\n * import 'baselode/style.css'; // required\n *\n * <div style={{ width: '100%', height: '80vh' }}>\n * <CorePhotoViewer\n * holeId=\"09RTD001\"\n * trays={[\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * // … one entry per image, in ascending depth order\n * ]}\n * thumbBaseUrl=\"/data/09RTD001/thumb\"\n * fullBaseUrl=\"/data/09RTD001/full\"\n * />\n * </div>\n * ```\n *\n *\n * ## What you must supply\n *\n * ### 1 · Tray depth intervals (`trays`)\n *\n * An array of objects, one per image file, each containing the downhole depth\n * range (metres) that the tray covers:\n *\n * ```js\n * [\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * ]\n * ```\n *\n * **Where to get these values:**\n * - Core scanning system export (HyLogger, Corescan, etc.) — usually a CSV\n * with from/to depths per tray.\n * - LIMS or core database — query from/to depths for the hole ordered by depth.\n * - Manual entry or calculation from known tray length and start depth.\n *\n *\n * ### 2 · Image directories (`thumbBaseUrl` / `fullBaseUrl`)\n *\n * Two URL prefixes pointing to directories that contain the image files:\n *\n * | Prop | Purpose | Typical size |\n * |----------------|-------------------------------------------|----------------|\n * | `thumbBaseUrl` | Low-resolution thumbnails — always loaded | ~10–50 KB each |\n * | `fullBaseUrl` | High-resolution originals — loaded on zoom| 1–10 MB each |\n *\n * If you only have one resolution pass the same URL for both props.\n *\n * The directories must be **browser-accessible** — e.g.:\n * - Served from your web server's `public/` folder\n * - An S3 bucket / CDN with CORS enabled (`Access-Control-Allow-Origin: *`)\n * - A Vercel / Netlify static asset path\n *\n * The viewer never loads all full-res images at once; it swaps to full\n * resolution only when the user zooms in past a threshold.\n *\n *\n * ### 3 · Container size\n *\n * The component fills 100 % of its parent's width and height. Wrap it in a\n * `<div>` with an explicit height (e.g. `height: '80vh'`).\n *\n *\n * ## Filename conventions\n *\n * By default filenames are generated as `tray_000.jpg`, `tray_001.jpg`, …\n * (zero-padded three-digit index, 0-based).\n *\n * Override per-tray via `tray.filename`:\n * ```js\n * { fromDepth: 3.4, toDepth: 6.8, filename: 'DDH001_box002.jpg' }\n * ```\n *\n * Or supply a custom generator for the whole dataset:\n * ```jsx\n * <CorePhotoViewer\n * getFilename={(index) => `box_${String(index + 1).padStart(4, '0')}.jpg`}\n * // …\n * />\n * ```\n *\n *\n * ## Multiple photo sets (columns)\n *\n * If you have parallel image sets (e.g. \"Wet\" and \"Dry\") for the same hole,\n * interleave the trays and tag each with `photoSet`:\n *\n * ```js\n * trays={[\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Wet', filename: 'wet_000.jpg' },\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Dry', filename: 'dry_000.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Wet', filename: 'wet_001.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Dry', filename: 'dry_001.jpg' },\n * ]}\n * ```\n *\n *\n * ## Interaction\n *\n * | Input | Action |\n * |------------------------|-------------------------------------|\n * | Mouse wheel | Zoom in / out (centred on cursor) |\n * | Left-click drag | Pan |\n * | ⌂ button in header | Reset to default view |\n *\n *\n * @param {Object} props\n * @param {string} [props.holeId='']\n * Drillhole identifier — shown in the header bar.\n *\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} [props.trays=[]]\n * Tray depth intervals. `fromDepth` / `toDepth` in metres downhole.\n * Optional `filename` overrides the auto-generated name for that tray.\n * Optional `photoSet` overrides the column label for that tray.\n *\n * @param {string} [props.thumbBaseUrl='']\n * URL prefix for thumbnail images (no trailing slash needed).\n * Example: `'https://cdn.example.com/holes/09RTD001/thumb'`\n *\n * @param {string} [props.fullBaseUrl='']\n * URL prefix for full-resolution images.\n * Example: `'https://cdn.example.com/holes/09RTD001/full'`\n *\n * @param {string} [props.photoSet='Tray Images']\n * Default column label used when `tray.photoSet` is absent.\n *\n * @param {function} [props.getFilename]\n * `(index: number) => string` — custom filename generator.\n * `index` is 0-based. Defaults to `tray_NNN.jpg`.\n *\n * @param {number} [props.initialZoom=5]\n * Starting LOD level (1–10). Controls when the viewer switches from\n * thumbnails to full-res as the user zooms in. 5 is a sensible default.\n */\nexport function CorePhotoViewer({\n holeId = '',\n trays = [],\n thumbBaseUrl = '',\n fullBaseUrl = '',\n photoSet: defaultPhotoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n initialZoom = 5,\n transform,\n onTransformChange,\n}) {\n const photos = useMemo(\n () => buildTrayPhotos(holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename),\n [holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename],\n );\n\n return (\n <CorePhotoTable\n photos={photos}\n holeId={holeId}\n initialZoom={initialZoom}\n transform={transform}\n onTransformChange={onTransformChange}\n />\n );\n}\n\nexport default CorePhotoViewer;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * @module grade_blocks\n *\n * Loading and Three.js rendering of 3D polygonal grade block meshes.\n *\n * A grade block is a closed polyhedral mesh defined by:\n * - An array of 3-D vertices [[x,y,z], ...]\n * - An array of triangle indices [[i,j,k], ...]\n * - Optional attributes and material hints\n *\n * Schema version \"1.0\" is the only version supported.\n */\n\nimport * as THREE from 'three';\n\n// ---------------------------------------------------------------------------\n// Data model helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and validate a raw JSON object (or JSON string) and return a\n * GradeBlockSet plain object.\n *\n * @param {object|string} input - Parsed JSON object or a JSON string.\n * @returns {{ schema_version: string, units: string, blocks: GradeBlock[] }}\n * @throws {Error} If schema_version is not \"1.0\" or required fields are missing.\n */\nexport function loadGradeBlocksFromJson(input) {\n const data = typeof input === 'string' ? JSON.parse(input) : input;\n\n if (data.schema_version !== '1.0') {\n throw new Error(\n `Unsupported schema_version: ${JSON.stringify(data.schema_version)}. Expected \"1.0\".`\n );\n }\n\n if (!Array.isArray(data.blocks)) {\n throw new Error('\"blocks\" must be a JSON array.');\n }\n\n const blocks = data.blocks.map((raw, i) => {\n if (raw.id == null) throw new Error(`Block at index ${i} is missing required field \"id\".`);\n if (raw.name == null) throw new Error(`Block \"${raw.id}\" is missing required field \"name\".`);\n if (!Array.isArray(raw.vertices)) throw new Error(`Block \"${raw.id}\" is missing required field \"vertices\".`);\n if (!Array.isArray(raw.triangles)) throw new Error(`Block \"${raw.id}\" is missing required field \"triangles\".`);\n\n return {\n id: raw.id,\n name: raw.name,\n vertices: raw.vertices,\n triangles: raw.triangles,\n attributes: raw.attributes ?? {},\n material: raw.material ?? {},\n };\n });\n\n return {\n schema_version: data.schema_version,\n units: data.units ?? '',\n blocks,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Three.js geometry\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a grade block to a Three.js BufferGeometry.\n *\n * @param {object} block - A grade block object (from loadGradeBlocksFromJson).\n * @returns {THREE.BufferGeometry}\n */\nexport function gradeBlockToThreeGeometry(block) {\n const geometry = new THREE.BufferGeometry();\n\n // Flatten vertices: [[x,y,z], ...] -> Float32Array\n const positionData = new Float32Array(block.vertices.length * 3);\n block.vertices.forEach(([x, y, z], i) => {\n positionData[i * 3] = x;\n positionData[i * 3 + 1] = y;\n positionData[i * 3 + 2] = z;\n });\n geometry.setAttribute('position', new THREE.BufferAttribute(positionData, 3));\n\n // Flatten triangles: [[i,j,k], ...] -> Uint32Array\n const indexData = new Uint32Array(block.triangles.length * 3);\n block.triangles.forEach(([a, b, c], i) => {\n indexData[i * 3] = a;\n indexData[i * 3 + 1] = b;\n indexData[i * 3 + 2] = c;\n });\n geometry.setIndex(new THREE.BufferAttribute(indexData, 1));\n\n return geometry;\n}\n\n// ---------------------------------------------------------------------------\n// Scene helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Create Three.js meshes for all blocks and add them to the scene.\n *\n * @param {THREE.Scene} scene\n * @param {{ blocks: object[] }} blockSet - Result of loadGradeBlocksFromJson.\n * @param {object} [options]\n * @param {number} [options.defaultOpacity=1.0] - Fallback opacity when not set per block.\n * @returns {THREE.Group}\n */\nexport function addGradeBlocksToScene(scene, blockSet, options = {}) {\n const { defaultOpacity = 1.0 } = options;\n const group = new THREE.Group();\n\n blockSet.blocks.forEach((block) => {\n const geometry = gradeBlockToThreeGeometry(block);\n\n const color = block.material?.color ?? '#888888';\n const opacity = block.material?.opacity ?? defaultOpacity;\n const transparent = opacity < 1.0;\n\n const material = new THREE.MeshStandardMaterial({\n color: new THREE.Color(color),\n opacity,\n transparent,\n side: THREE.DoubleSide,\n flatShading: true,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData = {\n id: block.id,\n attributes: block.attributes,\n };\n\n // Edge highlight — hidden by default, shown on selection\n const edgeGeo = new THREE.EdgesGeometry(geometry, 15);\n const edgeMat = new THREE.LineBasicMaterial({ color: '#ffffbb', linewidth: 1 });\n const edgeLines = new THREE.LineSegments(edgeGeo, edgeMat);\n edgeLines.visible = false;\n mesh.add(edgeLines);\n\n group.add(mesh);\n });\n\n scene.add(group);\n return group;\n}\n"],"names":["normalizeFieldName","name","standardizeColumns","row","columnMap","sourceColumnMap","lookup","_COLUMN_LOOKUP","rawName","expectedName","normalizedKey","normalizedValue","renamed","col","value","key","mapped","standardizeRowArray","rows","ASSAY_NON_VALUE_FIELDS","normalizeRow","rawRow","extractIdFields","HOLE_ID","extractInterval","holeIdRaw","holeId","project","PROJECT_ID","from","FROM","to","TO","intervalsToHole","intervals","sorted","a","b","points","iv","rest","pointData","_a","parseAssayHoleIds","file","resolve","reject","holeIds","Papa","results","hid","error","withDataErrorContext","hasAssayValue","k","v","parseAssayHoleIdsWithAssays","byHole","parseAssayHole","config","wanted","interval","hole","parseAssaysCSV","holes","normalizeCsvRow","normalized","pickFirstPresent","keys","fallback","DEFAULT_TRACE_PLOT_COUNT","reorderHoleIds","ids","focusId","matchIdx","id","selected","_","idx","coerceChartTypeForProperty","property","chartType","categoricalProps","commentProps","numericDefaultChartType","buildTraceConfigsForHoleIds","focusedHoleId","plotCount","defaultProp","ordered","deriveAssayProps","h","numericCols","categoricalCols","commentCols","byType","classifyColumns","loadAssayMetadata","loadAssayHole","buildAssayState","numericProps","columnMeta","traceConfigs","loadAssayFile","state","parseSurveyCSV","DEPTH","DIP","AZIMUTH","err","norm","lat","toNumber","LATITUDE","lng","LONGITUDE","surveyDepth","dip","azimuth","maxDepth","desurveyTraces","collars","surveys","collarByKey","c","refLat","_b","refLng","_c","_d","refMetersPerDegLat","refMetersPerDegLon","grouped","s","stations","collar","lat0","lng0","metersPerDegLat","metersPerDegLon","baseX","baseY","accX","accY","accZ","i","curr","prev","currDepth","currAzimuth","currDip","prevDepth","prevAzimuth","prevDip","deltaMD","inc1","toInclination","inc2","az1","degToRad","az2","beta","rf","dx","dy","dzDown","pointsWithGeo","p","d","dipDeg","val","incDeg","clamped","n","normalizeHoleIdValue","canonicalizeHoleIdRows","holeIdCol","preferred","resolved","angle","directionCosines","azRad","dipRad","ca","cb","cc","segmentDisplacement","deltaMd","az0","dip0","dip1","method","dc0","dc1","azAvg","dipAvg","dcAvg","dot","dogleg","desurvey","rowsCollars","rowsSurveys","options","step","safeStep","collarsCanonical","surveysCanonical","collarsByHole","surveysByHole","out","x","y","z","mdCursor","azPrev","dipPrev","firstRecord","s0","s1","md0","segmentSteps","mdIncrement","stepIdx","weight","azInterp","dipInterp","disp","record","minimumCurvatureDesurvey","tangentialDesurvey","balancedTangentialDesurvey","buildTraces","nearestByMeasuredDepth","traceRows","midMd","best","bestDist","md","dist","attachAssayPositions","assays","traces","assaysCanonical","tracesCanonical","tracesByHole","assay","nearest","merged","parseDrillholesCSV","EASTING","NORTHING","ELEVATION","order","pts","toArray","source","sortByColumns","columns","av","bv","validateNoOverlappingIntervals","label","prevToByHole","fromValue","toValue","prevTo","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","MID","loadGeology","hasCode","GEOLOGY_CODE","hasDescription","GEOLOGY_DESCRIPTION","code","description","keep","BASELODE_DATA_MODEL_DRILL_GEOLOGY","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","geology","structures","metadata","detectSchema","first","hasInterval","hasPoint","extractStructuralPoint","depth","extractStructuralInterval","mid","validateStructuralPoints","valid","errors","messages","az","parseStructuralPointsCSV","opts","point","parseStructuralIntervalsCSV","groupRowsByHole","byId","parseStructuralCSV","r","schema","parsed","parseAssayCsvTextToHoles","csvText","_dip","_az","rowWithoutStructural","parseGeologyCsvText","parseUnifiedDataset","assayCsv","structuralCsv","geologyCsv","assayHoles","structuralHoles","geologyHoles","sh","existing","significantIntercepts","assayField","minGrade","minLength","fromCol","toCol","holeCol","holeIntervals","qualifying","grade","runs","currentRun","f","t","run","totalFrom","totalTo","totalLength","weightedSum","totalWeight","len","avgGrade","nSamples","DEPTH_ALIASES","resolveDepth","alias","detectValueColumns","skipSet","numericCount","parseGeophysicsCSV","rawRows","valueCols","samples","channels","depths","values","GEOPHYSICS_NULL_SENTINEL","geophysicsToStripLogs","geophysicsHoles","channel","stripLogs","parseMnemonicLine","line","dotIdx","mnem","afterDot","unitMatch","unit","valueAndDesc","lastColon","splitSections","lasText","sections","current","raw","parseHeaderSection","lines","map","trimmed","parseLasFile","versionSection","wellSection","curveSection","dataSection","version","well","curveEntries","_f","_e","_h","_g","nullSentinel","_i","valueCurves","units","channelData","tokens","data","DEFAULT_PALETTE","STRIPLOG_COMPACT_MARGIN","STRIPLOG_AXIS_TICK_FONT_SIZE","STRIPLOG_AXIS_TITLE_FONT_SIZE","STRIPLOG_XAXIS_TITLE_STANDOFF","applyStriplogLayoutDefaults","layout","buildTadpoleConfig","tailScale","colorBy","palette","depthCol","dipCol","azCol","template","colorMap","cat","byCat","shapes","color","group","length","showLegend","BASELODE_TEMPLATE","buildStructuralStripConfig","labelCol","records","lv","textY","texts","rec","wrapComment","text","charsPerLine","words","word","buildCommentsConfig","commentCol","bgColor","borderColor","textColor","comment","textXs","textYs","hovers","hasComment","buildStrikeDipSymbol","symbolSize","xCol","yCol","strike","strikeRad","dxS","dyS","tickLen","dxD","dyD","resolveTracePlotBody","holeOptions","propertyOptions","displayType","renderError","props","DISPLAY_COMMENT","DISPLAY_TADPOLE","deriveGroupValue","holeOption","groupBy","groupValuesFromHoles","seen","g","filterHolesByGroup","groupValue","resolveTracePlotSelectVisibility","chartOptions","showHoleSelect","showPropertySelect","showChartTypeSelect","DEFAULT_NUMERIC_CHART_TYPE","resolveChartType","requestedChartType","getChartOptions","opt","holeOptionAsTuple","genericOptionAsTuple","o","renderHoleSelector","selector","selectedHoleId","onConfigChange","jsxs","e","jsx","l","groupLabel","groupOptions","visibleHoles","Fragment","enabled","TracePlot","graph","propertyMeta","holeSelector","bodyRef","useRef","containerRef","meta","DISPLAY_CATEGORICAL","DISPLAY_NUMERIC","effectiveChartType","setRenderError","useState","plotSize","setPlotSize","bodyState","isPlaceholder","visibility","propertySelectEnabled","useEffect","body","frame","updatePlotSize","width","height","observer","target","isComment","isTadpole","plotData","buildPlotConfig","plotConfig","Plotly","resizeObserver","formatPropertyLabel","mergeHoleSets","primary","extra","eh","buildCommentPoints","useDrillholeTraceGrid","initialFocusedHoleId","sourceFile","extraHoles","setHoles","setHoleIds","setNumericProps","setCategoricalProps","setCommentProps","setColumnMeta","setDefaultProp","setTraceConfigs","setError","setFocusedHoleId","loadingHoles","setLoadingHoles","loadedSourceFileRef","uniqueIds","newIds","orderedHoleIds","cfg","already","loading","configs","useMemo","labeledHoleOptions","traceGraphs","allProps","holePropertyOptions","holeHasData","isCategorical","buildIntervalPoints","handleConfigChange","index","patch","normalizePoint","projectTraceToSection","origin","ox","oy","cosA","sinA","sectionWindow","projected","half","planView","depthSlice","top","bottom","sectionView","section","getHoleId","tracesAsSegments","segments","payload","intervalsAsTubes","radius","annotationsFromIntervals","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit","darkBackground","onToggleDarkBackground","BlockModelWidget","properties","selectedProperty","onPropertyChange","opacity","onOpacityChange","propertyStats","clickedBlock","onPopupClose","hue","DEFAULT_LOD_BREAKPOINTS","BASE_PIXELS_PER_METRE","selectPhotoLodUrl","photo","zoom","lodBreakpoints","lodUrls","selectedKey","bp","sortPhotosByDepth","photos","groupPhotosBySet","sets","buildDepthMarkers","minDepth","intervalMetres","markers","epsilon","rounded","depthMarkerInterval","defaultTrayFilename","buildTrayPhotos","trays","thumbBaseUrl","fullBaseUrl","photoSet","getFilename","thumb","full","tray","filename","set","depthIntervalToPixels","depthInterval","basePixelsPerMetre","scale","BASE_ZOOM","ZOOM_FACTOR","SCALE_MIN","SCALE_MAX","CorePhotoTable","initialZoom","controlledTransform","onTransformChange","internalTransform","setInternalTransform","transform","transformRef","setTransform","useCallback","updater","dragging","setDragging","dragOrigin","viewportRef","setNames","allTo","pixelsPerMetre","totalHeight","imageWidth","lodZoom","handleWheel","factor","rect","cx","cy","newScale","ratio","el","handleMouseDown","handleMouseMove","tx","ty","handleMouseUp","setName","fromDepth","toDepth","src","CorePhotoViewer","defaultPhotoSet","loadGradeBlocksFromJson","input","blocks","gradeBlockToThreeGeometry","block","geometry","THREE","positionData","indexData","addGradeBlocksToScene","scene","blockSet","defaultOpacity","transparent","material","mesh","edgeGeo","edgeMat","edgeLines"],"mappings":";;;;;;;AAWO,SAASA,GAAmBC,GAAM;AACvC,UAAQA,KAAQ,IAAI,WAAW,OAAO,cAAc,QAAQ,QAAQ,GAAG;AACzE;AASO,SAASC,GAAmBC,GAAKC,IAAY,MAAMC,IAAkB,MAAM;AAChF,QAAMC,IAAS,EAAE,GAAGC,GAAc;AAGlC,MAAIF;AACF,eAAW,CAACG,GAASC,CAAY,KAAK,OAAO,QAAQJ,CAAe;AAClE,UAAIG,KAAW,QAAQC,KAAgB,MAAM;AAC3C,cAAMC,IAAgBV,GAAmBQ,CAAO,GAC1CG,IAAkBX,GAAmBS,CAAY;AACvD,QAAAH,EAAOI,CAAa,IAAIC;AAAA,MAC1B;AAAA;AAIJ,QAAMC,IAAU,CAAA;AAChB,aAAW,CAACC,GAAKC,CAAK,KAAK,OAAO,QAAQX,CAAG,GAAG;AAC9C,UAAMY,IAAMf,GAAmBa,CAAG,GAC5BG,IAASV,EAAOS,CAAG,KAAKA;AAC9B,IAAAH,EAAQI,CAAM,IAAIF;AAAA,EACpB;AAEA,SAAOF;AACT;AASO,SAASK,GAAoBC,GAAMd,IAAY,MAAMC,IAAkB,MAAM;AAClF,SAAOa,EAAK,IAAI,CAAAf,MAAOD,GAAmBC,GAAKC,GAAWC,CAAe,CAAC;AAC5E;AClDY,MAACc,KAAyB,oBAAI,IAAI;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC,GCvBKC,KAAe,CAACC,GAAQhB,IAAkB,SAASH,GAAmBmB,GAAQ,MAAMhB,CAAe;AAQzG,SAASiB,GAAgBnB,GAAK;AAE5B,SAAO,EAAE,QADMA,EAAIoB,CAAO,EACX;AACjB;AASA,SAASC,GAAgBrB,GAAKE,IAAkB,MAAM;AACpD,QAAMoB,IAAYtB,EAAIoB,CAAO,GACvBG,IAASD,MAAc,SAAY,GAAGA,CAAS,GAAG,KAAI,IAAK;AACjE,MAAI,CAACC,EAAQ,QAAO;AAEpB,QAAMC,IAAUxB,EAAIyB,EAAU,KAAKzB,EAAI,WAAWA,EAAI,cAChD0B,IAAO,OAAO1B,EAAI2B,CAAI,CAAC,GACvBC,IAAK,OAAO5B,EAAI6B,CAAE,CAAC;AACzB,SAAI,CAAC,OAAO,SAASH,CAAI,KAAK,CAAC,OAAO,SAASE,CAAE,KAAKA,KAAMF,IAAa,OAElE;AAAA,IACL,QAAAH;AAAA,IACA,SAAAC;AAAA,IACA,MAAAE;AAAA,IACA,IAAAE;AAAA,IACA,GAAG5B;AAAA,EACP;AACA;AASA,SAAS8B,GAAgBP,GAAQQ,GAAW;;AAC1C,QAAMC,IAASD,EAAU,KAAK,CAACE,GAAGC,MAAMD,EAAE,OAAOC,EAAE,IAAI,GACjDC,IAAS,CAAA;AACf,SAAAH,EAAO,QAAQ,CAACI,MAAO;AACrB,UAAM,EAAE,MAAAV,GAAM,IAAAE,GAAI,SAAAJ,GAAS,GAAGa,EAAI,IAAKD,GACjCE,IAAY;AAAA,MAChB,GAAGZ;AAAA,MACH,MAAAA;AAAA,MACA,IAAAE;AAAA,MACA,CAACR,CAAO,GAAGG;AAAA,MACX,CAACE,EAAU,GAAGD;AAAA,MACd,GAAGa;AAAA,IACT;AACI,IAAAF,EAAO,KAAKG,CAAS,GACrBH,EAAO,KAAK,EAAE,GAAGG,GAAW,GAAGV,EAAE,CAAE;AAAA,EACrC,CAAC,GACM,EAAE,IAAIL,GAAQ,UAASgB,IAAAP,EAAO,CAAC,MAAR,gBAAAO,EAAW,SAAS,QAAAJ,EAAM;AAC1D;AAQO,SAASK,GAAkBC,GAAMvC,IAAkB,MAAM;AAC9D,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,UAAMC,IAAU,oBAAI,IAAG;AACvB,IAAAC,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,MAAM,CAACK,MAAY;AAEjB,cAAMC,IADM9B,GAAa6B,EAAQ,MAAM5C,CAAe,EACtCkB,CAAO;AACvB,QAAI2B,MAAQ,UAAa,GAAGA,CAAG,GAAG,KAAI,MAAO,MAC3CH,EAAQ,IAAI,GAAGG,CAAG,GAAG,KAAI,CAAE;AAAA,MAE/B;AAAA,MACA,UAAU,MAAML,EAAQ,MAAM,KAAKE,CAAO,CAAC;AAAA,MAC3C,OAAO,CAACI,MAAUL,EAAOM,EAAqB,qBAAqBD,CAAK,CAAC;AAAA,IAC/E,CAAK;AAAA,EACH,CAAC;AACH;AAQA,SAASE,GAAclD,GAAK;AAC1B,SAAO,OAAO,QAAQA,KAAO,CAAA,CAAE,EAAE,KAAK,CAAC,CAACmD,GAAGC,CAAC,MACtC,EAAApC,GAAuB,IAAImC,CAAC,KACTC,KAAM,QACzB,OAAOA,KAAM,YAAYA,EAAE,KAAI,MAAO,GAE3C;AACH;AAQO,SAASC,GAA4BZ,GAAMvC,IAAkB,MAAM;AACxE,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,UAAMW,IAAS,oBAAI,IAAG;AACtB,IAAAT,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,MAAM,CAACK,MAAY;AACjB,cAAM9C,IAAMiB,GAAa6B,EAAQ,MAAM5C,CAAe;AACtD,YAAI,CAACgD,GAAclD,CAAG,EAAG;AAEzB,cAAM+C,IADM5B,GAAgBnB,CAAG,EACf;AAChB,YAAI+C,MAAQ,UAAa,GAAGA,CAAG,GAAG,KAAI,MAAO,IAAI;AAC/C,gBAAMnC,IAAM,GAAGmC,CAAG,GAAG,KAAI;AACzB,UAAKO,EAAO,IAAI1C,CAAG,KACjB0C,EAAO,IAAI1C,GAAK;AAAA,YACd,QAAQA;AAAA,UACtB,CAAa;AAAA,QAEL;AAAA,MACF;AAAA,MACA,UAAU,MAAM8B,EAAQ,MAAM,KAAKY,EAAO,OAAM,CAAE,CAAC;AAAA,MACnD,OAAO,CAACN,MAAUL,EAAOM,EAAqB,+BAA+BD,CAAK,CAAC;AAAA,IACzF,CAAK;AAAA,EACH,CAAC;AACH;AAUO,SAASO,GAAed,GAAMlB,GAAQiC,IAAS,MAAMtD,IAAkB,MAAM;AAClF,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,UAAMc,IAAS,GAAGlC,CAAM,GAAG,KAAI;AAC/B,QAAI,CAACkC,GAAQ;AACX,MAAAd,EAAOM,EAAqB,kBAAkB,IAAI,MAAM,iBAAiB,CAAC,CAAC;AAC3E;AAAA,IACF;AACA,UAAMlB,IAAY,CAAA;AAClB,IAAAc,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,MAAM,CAACK,MAAY;AACjB,cAAM9C,IAAMiB,GAAa6B,EAAQ,MAAM5C,CAAe,GAChDwD,IAAWrC,GAAgBrB,GAAKE,CAAe;AACrD,QAAKwD,KACD,GAAGA,EAAS,MAAM,GAAG,KAAI,MAAOD,KACpC1B,EAAU,KAAK2B,CAAQ;AAAA,MACzB;AAAA,MACA,UAAU,MAAM;AACd,YAAI,CAAC3B,EAAU,QAAQ;AACrB,UAAAW,EAAQ,IAAI;AACZ;AAAA,QACF;AACA,cAAMiB,IAAO7B,GAAgB2B,GAAQ1B,CAAS;AAC9C,QAAAW,EAAQiB,CAAI;AAAA,MACd;AAAA,MACA,OAAO,CAACX,MAAUL,EAAOM,EAAqB,kBAAkBD,CAAK,CAAC;AAAA,IAC5E,CAAK;AAAA,EACH,CAAC;AACH;AASO,SAASY,GAAenB,GAAMe,IAAS,MAAMtD,IAAkB,MAAM;AAC1E,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAACK,MAAY;AACrB,cAAMQ,IAAS,oBAAI,IAAG;AACtB,QAAAR,EAAQ,KAAK,QAAQ,CAAC5B,MAAW;AAC/B,gBAAMlB,IAAMiB,GAAaC,GAAQhB,CAAe,GAC1CwD,IAAWrC,GAAgBrB,GAAKE,CAAe;AACrD,UAAKwD,MACAJ,EAAO,IAAII,EAAS,MAAM,KAAGJ,EAAO,IAAII,EAAS,QAAQ,EAAE,GAChEJ,EAAO,IAAII,EAAS,MAAM,EAAE,KAAKA,CAAQ;AAAA,QAC3C,CAAC;AAED,cAAMG,IAAQ,MAAM,KAAKP,EAAO,QAAO,CAAE,EAAE,IAAI,CAAC,CAACP,GAAKhB,CAAS,MAAMD,GAAgBiB,GAAKhB,CAAS,CAAC;AACpG,QAAAW,EAAQ,EAAE,OAAAmB,GAAO;AAAA,MACnB;AAAA,MACA,OAAO,CAACb,MAAUL,EAAOM,EAAqB,kBAAkBD,CAAK,CAAC;AAAA,IAC5E,CAAK;AAAA,EACH,CAAC;AACH;ACpNO,SAASc,GAAgB9D,IAAM,IAAI;AACxC,QAAM+D,IAAa,CAAA;AACnB,gBAAO,QAAQ/D,KAAO,CAAA,CAAE,EAAE,QAAQ,CAAC,CAACY,GAAKD,CAAK,MAAM;AAClD,IAAKC,MACLmD,EAAWlE,GAAmBe,CAAG,CAAC,IAAID;AAAA,EACxC,CAAC,GACMoD;AACT;AASO,SAASC,GAAiBD,IAAa,CAAA,GAAIE,IAAO,CAAA,GAAIC,GAAU;AACrE,aAAWtD,KAAOqD,GAAM;AACtB,UAAMtD,IAAQoD,EAAWnD,CAAG;AAC5B,QAA2BD,KAAU,QAAQ,GAAGA,CAAK,GAAG,KAAI,MAAO;AACjE,aAAOA;AAAA,EAEX;AACA,SAAOuD;AACT;AC5BO,MAAMC,KAA2B;AAQjC,SAASC,GAAeC,IAAM,IAAIC,IAAU,IAAI;AACrD,MAAI,CAACD,EAAI,OAAQ,QAAO,CAAA;AACxB,MAAI,CAACC,EAAS,QAAOD;AACrB,QAAME,IAAWF,EAAI,UAAU,CAACG,MAAOA,MAAOF,CAAO;AACrD,MAAIC,MAAa,GAAI,QAAOF;AAC5B,QAAMI,IAAWJ,EAAIE,CAAQ,GACvBlC,IAAOgC,EAAI,OAAO,CAACK,GAAGC,MAAQA,MAAQJ,CAAQ;AACpD,SAAO,CAACE,GAAU,GAAGpC,CAAI;AAC3B;AAcO,SAASuC,GAA2B;AAAA,EACzC,UAAAC,IAAW;AAAA,EACX,WAAAC,IAAY;AAAA,EACZ,kBAAAC,IAAmB,CAAA;AAAA,EACnB,cAAAC,IAAe,CAAA;AAAA,EACf,yBAAAC,IAA0B;AAC5B,IAAI,IAAI;AACN,SAAKJ,IACDG,EAAa,SAASH,CAAQ,IAAU,YACxCE,EAAiB,SAASF,CAAQ,IAAU,gBAC5CA,MAAa,QAAc,YAC3B,CAACC,KAAaA,MAAc,iBAAiBA,MAAc,aAAaA,MAAc,YAAkBG,IACrGH,IALeA,KAAaG;AAMrC;AAcO,SAASC,GAA4B;AAAA,EAC1C,SAAAtC,IAAU,CAAA;AAAA,EACV,eAAAuC,IAAgB;AAAA,EAChB,WAAAC,IAAYjB;AAAA,EACZ,aAAAkB,IAAc;AAAA,EACd,kBAAAN,IAAmB,CAAA;AAAA,EACnB,cAAAC,IAAe,CAAA;AAAA,EACf,yBAAAC,IAA0B;AAC5B,IAAI,IAAI;AACN,QAAMK,IAAUlB,GAAexB,GAASuC,CAAa;AACrD,SAAO,MAAM,KAAK,EAAE,QAAQC,EAAS,CAAE,EAAE,IAAI,CAACV,GAAGC,MAAQ;AACvD,UAAMpD,IAAS+D,EAAQX,CAAG,KAAK/B,EAAQ+B,CAAG,KAAK,IACzCG,IAAYF,GAA2B;AAAA,MAC3C,UAAUS;AAAA,MACV,WAAW;AAAA,MACX,kBAAAN;AAAA,MACA,cAAAC;AAAA,MACA,yBAAAC;AAAA,IACN,CAAK;AACD,WAAO;AAAA,MACL,QAAA1D;AAAA,MACA,UAAU8D;AAAA,MACV,WAAAP;AAAA,IACN;AAAA,EACE,CAAC;AACH;AClEO,SAASS,GAAiB1B,IAAQ,IAAI;AAC3C,QAAM1B,IAAS0B,EAAM,QAAQ,CAAC2B,MAAMA,EAAE,UAAU,EAAE,GAC5C,EAAE,aAAAC,GAAa,iBAAAC,GAAiB,aAAAC,GAAa,QAAAC,EAAM,IAAKC,GAAgB1D,CAAM,GAE9EkD,IAAcI,EAAY,CAAC,KAAKC,EAAgB,CAAC,KAAK;AAE5D,SAAO;AAAA,IACL,cAAcD;AAAA,IACd,kBAAkBC;AAAA,IAClB,cAAcC;AAAA,IACd,YAAYC;AAAA,IACZ,aAAAP;AAAA,EACJ;AACA;AAQO,eAAeS,GAAkBrD,GAAMe,IAAS,MAAM;AAE3D,SADgB,MAAMH,GAA4BZ,CAAI;AAExD;AASO,eAAesD,GAActD,GAAMlB,GAAQiC,IAAS,MAAM;AAE/D,SADa,MAAMD,GAAed,GAAMlB,CAAM;AAEhD;AAQO,SAASyE,GAAgBnC,IAAQ,IAAIsB,IAAgB,IAAI;AAC9D,MAAI,CAACtB,EAAM,OAAQ,QAAO;AAC1B,QAAM,EAAE,cAAAoC,GAAc,kBAAAlB,GAAkB,cAAAC,GAAc,YAAAkB,GAAY,aAAAb,EAAW,IAAKE,GAAiB1B,CAAK,GAClGjB,IAAUiB,EAAM,IAAI,CAAC2B,MAAMA,EAAE,MAAMA,EAAE,MAAM,EAAE,OAAO,OAAO,GAC3DW,IAAejB,GAA4B;AAAA,IAC/C,SAAAtC;AAAA,IACA,eAAAuC;AAAA,IACA,WAAW;AAAA,IACX,aAAAE;AAAA,IACA,kBAAAN;AAAA,IACA,cAAAC;AAAA,IACA,yBAAyB;AAAA,EAC7B,CAAG;AACD,SAAO;AAAA,IACL,OAAAnB;AAAA,IACA,cAAAoC;AAAA,IACA,kBAAAlB;AAAA,IACA,cAAAC;AAAA,IACA,YAAAkB;AAAA,IACA,aAAAb;AAAA,IACA,cAAAc;AAAA,EACJ;AACA;AAUO,eAAeC,GAAc3D,GAAM0C,IAAgB,IAAIjF,IAAkB,MAAM;AACpF,QAAM,EAAE,OAAA2D,EAAK,IAAK,MAAMD,GAAenB,GAAMvC,CAAe,GACtDmG,IAAQL,GAAgBnC,GAAOsB,CAAa;AAClD,MAAI,CAACkB,EAAO,OAAM,IAAI,MAAM,iCAAiC;AAC7D,SAAOA;AACT;ACvFO,SAASC,GAAe7D,GAAMvC,IAAkB,MAAM;AAC3D,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAACK,MAAY;AACrB,cAAM/B,IAAO+B,EAAQ,KAClB,IAAI,CAAC9C,MAAQiB,GAAajB,GAAKE,CAAe,CAAC,EAC/C,OAAO,CAACF,MAAQA,EAAIoB,CAAO,KAAK,OAAO,SAASpB,EAAIuG,CAAK,CAAC,KAAK,OAAO,SAASvG,EAAIwG,CAAG,CAAC,KAAK,OAAO,SAASxG,EAAIyG,CAAO,CAAC,CAAC;AAC5H,QAAA/D,EAAQ3B,CAAI;AAAA,MACd;AAAA,MACA,OAAO,CAAC2F,MAAQ/D,EAAOM,EAAqB,kBAAkByD,CAAG,CAAC;AAAA,IACxE,CAAK;AAAA,EACH,CAAC;AACH;AASA,SAASzF,GAAajB,GAAKE,IAAkB,MAAM;AACjD,QAAMyG,IAAO5G,GAAmBC,GAAK,MAAME,CAAe,GAEpDqB,IAASoF,EAAKvF,CAAO,GACrBI,IAAUmF,EAAKlF,EAAU,KAAKkF,EAAK,WAAWA,EAAK,cACnDC,IAAMC,GAASF,EAAKG,EAAQ,CAAC,GAC7BC,IAAMF,GAASF,EAAKK,EAAS,CAAC,GAC9BC,IAAcJ,GAASF,EAAKJ,CAAK,CAAC,GAClCW,IAAML,GAASF,EAAKH,CAAG,CAAC,GACxBW,IAAUN,GAASF,EAAKF,CAAO,CAAC,GAChCW,IAAWP,GAASF,EAAK,QAAQ;AAEvC,SAAO;AAAA,IACL,KAAKA;AAAA,IACL,CAACvF,CAAO,GAAGG;AAAA,IACX,CAACE,EAAU,GAAGD;AAAA,IACd,CAACsF,EAAQ,GAAGF;AAAA,IACZ,CAACI,EAAS,GAAGD;AAAA,IACb,CAACR,CAAK,GAAGU;AAAA,IACT,CAACT,CAAG,GAAGU;AAAA,IACP,CAACT,CAAO,GAAGU;AAAA,IACX,UAAUC;AAAA;AAAA,IAEV,cAAc5F;AAAA,IACd,UAAUoF;AAAA,IACV,WAAWG;AAAA,IACX,aAAaE;AAAA,EACjB;AACA;AAQA,MAAMJ,KAAW,CAACzD,MAAM;AACtB,QAAM,IAAI,OAAOA,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AASO,SAASiE,GAAeC,GAASC,GAAS;;AAC/C,QAAMC,IAAc,oBAAI,IAAG;AAC3B,EAAAF,EAAQ,QAAQ,CAACG,MAAM;AACrB,UAAMlG,KAAUkG,EAAErG,CAAO,KAAKqG,EAAE,UAAUA,EAAE,MAAM,IAAI,SAAQ,EAAG,KAAI;AACrE,QAAI,CAAClG,EAAQ;AACb,UAAMX,IAAMW,EAAO,YAAW;AAC9B,IAAKiG,EAAY,IAAI5G,CAAG,KACtB4G,EAAY,IAAI5G,GAAK6G,CAAC;AAAA,EAE1B,CAAC;AAED,QAAMC,MAASnF,IAAA+E,EAAQ,CAAC,MAAT,gBAAA/E,EAAY,UAAOoF,IAAAL,EAAQ,CAAC,MAAT,gBAAAK,EAAab,QAAa,GACtDc,MAASC,IAAAP,EAAQ,CAAC,MAAT,gBAAAO,EAAY,UAAOC,IAAAR,EAAQ,CAAC,MAAT,gBAAAQ,EAAad,QAAc,GACvDe,IAAqB,QACrBC,IAAqB,SAAS,KAAK,IAAKN,IAAS,KAAK,KAAM,GAAG,GAE/DO,IAAU,oBAAI,IAAG;AACvB,EAAAV,EAAQ,QAAQ,CAACW,MAAM;AACrB,UAAM3G,KAAU2G,EAAE9G,CAAO,KAAK,IAAI,SAAQ,EAAG,KAAI;AACjD,QAAI,CAACG,EAAQ;AACb,UAAMX,IAAMW,EAAO,YAAW;AAC9B,IAAK0G,EAAQ,IAAIrH,CAAG,KAAGqH,EAAQ,IAAIrH,GAAK,EAAE,GAC1CqH,EAAQ,IAAIrH,CAAG,EAAE,KAAKsH,CAAC;AAAA,EACzB,CAAC;AAED,QAAMrE,IAAQ,CAAA;AACd,SAAAoE,EAAQ,QAAQ,CAACE,GAAUvH,MAAQ;AACjC,UAAMwH,IAASZ,EAAY,IAAI5G,CAAG;AAClC,QAAI,CAACwH,EAAQ;AACb,UAAMpG,IAASmG,EACZ,OAAO,CAACD,MAAM,OAAO,SAASA,EAAE3B,CAAK,KAAK2B,EAAE,WAAW,CAAC,EACxD,KAAK,CAACjG,GAAGC,OAAOD,EAAEsE,CAAK,KAAKtE,EAAE,gBAAgBC,EAAEqE,CAAK,KAAKrE,EAAE,YAAY;AAC3E,QAAI,CAACF,EAAO,OAAQ;AAEpB,UAAMqG,IAAOD,EAAO,OAAOA,EAAOtB,EAAQ,GACpCwB,IAAOF,EAAO,OAAOA,EAAOpB,EAAS,GACrCuB,IAAkB,QAClBC,IAAkB,SAAS,KAAK,IAAKH,IAAO,KAAK,KAAM,GAAG,GAC1DI,KAASH,IAAOV,KAAUI,GAC1BU,KAASL,IAAOX,KAAUK,GAE1B5F,IAAS,CAAA;AACf,QAAIwG,IAAO,GACPC,IAAO,GACPC,IAAO;AAEX,aAASC,IAAI,GAAGA,IAAI9G,EAAO,QAAQ8G,KAAK,GAAG;AACzC,YAAMC,IAAO/G,EAAO8G,CAAC,GACfE,IAAOhH,EAAO8G,IAAI,CAAC,GACnBG,IAAYF,EAAKxC,CAAK,KAAKwC,EAAK,aAChCG,IAAcH,EAAKtC,CAAO,KAAKsC,EAAK,SACpCI,IAAUJ,EAAKvC,CAAG,KAAKuC,EAAK;AAElC,UAAI,CAACC,GAAM;AACT,QAAA7G,EAAO,KAAK;AAAA,UACV,GAAGsG,IAAQE;AAAA,UACX,GAAGD,IAAQE;AAAA,UACX,GAAG;AAAA,UACH,IAAIK;AAAA,UACJ,SAASC;AAAA,UACT,KAAKC;AAAA,QACf,CAAS;AACD;AAAA,MACF;AAEA,YAAMC,IAAYJ,EAAKzC,CAAK,KAAKyC,EAAK,aAChCK,IAAcL,EAAKvC,CAAO,KAAKuC,EAAK,SACpCM,IAAUN,EAAKxC,CAAG,KAAKwC,EAAK,KAE5BO,IAAUN,IAAYG;AAC5B,UAAIG,KAAW,EAAG;AAElB,YAAMC,IAAOC,GAAcH,CAAO,GAC5BI,IAAOD,GAAcN,CAAO,GAC5BQ,IAAMC,GAASP,CAAW,GAC1BQ,KAAMD,GAASV,CAAW,GAE1BY,KAAO,KAAK;AAAA,QAChB,KAAK,IAAIN,CAAI,IAAI,KAAK,IAAIE,CAAI,IAAI,KAAK,IAAIC,IAAME,EAAG,IAClD,KAAK,IAAIL,CAAI,IAAI,KAAK,IAAIE,CAAI;AAAA,MACxC,GAEYK,KAAKD,KAAO,OAAQ,IAAIA,KAAQ,KAAK,IAAIA,KAAO,CAAC,IAAI,GAErDE,KAAK,MAAMT,KAAW,KAAK,IAAIC,CAAI,IAAI,KAAK,IAAIG,CAAG,IAAI,KAAK,IAAID,CAAI,IAAI,KAAK,IAAIG,EAAG,KAAKE,IACzFE,KAAK,MAAMV,KAAW,KAAK,IAAIC,CAAI,IAAI,KAAK,IAAIG,CAAG,IAAI,KAAK,IAAID,CAAI,IAAI,KAAK,IAAIG,EAAG,KAAKE,IACzFG,KAAS,MAAMX,KAAW,KAAK,IAAIC,CAAI,IAAI,KAAK,IAAIE,CAAI,KAAKK;AAEnE,MAAApB,KAAQqB,IACRpB,KAAQqB,IACRpB,KAAQqB,IAER/H,EAAO,KAAK;AAAA,QACV,GAAGsG,IAAQE;AAAA,QACX,GAAGD,IAAQE;AAAA,QACX,GAAG,CAACC;AAAA;AAAA,QACJ,IAAII;AAAA,QACJ,SAASC;AAAA,QACT,KAAKC;AAAA,MACb,CAAO;AAAA,IACH;AAGA,UAAMgB,IAAgBhI,EAAO,IAAI,CAACiI,OAAO;AAAA,MACvC,GAAGA;AAAA,MACH,KAAK/B,IAAQ+B,EAAE,IAAI7B;AAAA,MACnB,KAAKD,IAAQ8B,EAAE,IAAI5B;AAAA,IACzB,EAAM;AAEF,IAAA3E,EAAM,KAAK;AAAA,MACT,IAAIuE,EAAOhH,CAAO,KAAKgH,EAAO,UAAUxH;AAAA,MACxC,SAASwH,EAAO3G,EAAU,KAAK2G,EAAO,cAAcA,EAAO,WAAW;AAAA,MACtE,QAAQ+B;AAAA,MACR,QAAA/B;AAAA,IACN,CAAK;AAAA,EACH,CAAC,GAEMvE;AACT;AAQA,MAAM+F,KAAW,CAACS,MAAOA,IAAI,KAAK,KAAM,KAQlCZ,KAAgB,CAACa,MAAW;AAChC,QAAMC,IAAM,OAAOD,CAAM,GACnBE,IAAS,MAAM,OAAO,SAASD,CAAG,IAAIA,IAAM,IAC5CE,IAAU,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGD,CAAM,CAAC;AACjD,SAAOZ,GAASa,CAAO;AACzB;AC3NG,SAAS5D,GAASlG,GAAOuD,IAAW,QAAW;AAChD,QAAMwG,IAAI,OAAO/J,CAAK;AACtB,SAAO,OAAO,SAAS+J,CAAC,IAAIA,IAAIxG;AAClC;AAMA,SAASyG,GAAqBhK,GAAO;AACnC,SAA2BA,KAAU,OAAa,KAC3C,GAAGA,CAAK,GAAG,KAAI;AACxB;AAMA,SAASiK,GAAuB7J,IAAO,IAAI8J,IAAY,MAAM;AAC3D,QAAMC,IAAYD,KAAa,WAEzBE,IADa,CAACD,GAAW,WAAW,UAAU,IAAI,EAC5B,KAAK,CAACpK,MAAQK,EAAK,KAAK,CAACf,MAAQ2K,GAAqB3K,KAAA,gBAAAA,EAAMU,EAAI,CAAC,CAAC;AAC9F,MAAI,CAACqK;AACH,UAAM9H,EAAqB,0BAA0B,IAAI,MAAM,mBAAmB6H,CAAS,aAAa,CAAC;AAE3G,SAAO;AAAA,IACL,UAAUC;AAAA,IACV,MAAMhK,EAAK,IAAI,CAACf,OAAS;AAAA,MACvB,GAAGA;AAAA,MACH,SAAS2K,GAAqB3K,KAAA,gBAAAA,EAAM+K,EAAS;AAAA,IACnD,EAAM;AAAA,EACN;AACA;AAMA,SAASnB,GAASoB,GAAO;AACvB,SAAQ,OAAOA,CAAK,IAAI,KAAK,KAAM;AACrC;AAMA,SAASC,GAAiB9D,GAASD,GAAK;AACtC,QAAMgE,IAAQtB,GAASzC,CAAO,GACxBgE,IAASvB,GAAS1C,CAAG,GACrBkE,IAAK,KAAK,IAAID,CAAM,IAAI,KAAK,IAAID,CAAK,GACtCG,IAAK,KAAK,IAAIF,CAAM,IAAI,KAAK,IAAID,CAAK,GACtCI,IAAK,KAAK,IAAIH,CAAM,IAAI;AAC9B,SAAO,EAAE,IAAAC,GAAI,IAAAC,GAAI,IAAAC,EAAE;AACrB;AAMA,SAASC,GAAoBC,GAASC,GAAKC,GAAM/B,GAAKgC,GAAMC,IAAS,qBAAqB;AACxF,QAAMC,IAAMZ,GAAiBQ,GAAKC,CAAI,GAChCI,IAAMb,GAAiBtB,GAAKgC,CAAI;AAEtC,MAAIC,MAAW;AACb,WAAO;AAAA,MACL,IAAIJ,IAAUK,EAAI;AAAA,MAClB,IAAIL,IAAUK,EAAI;AAAA,MAClB,IAAIL,IAAUK,EAAI;AAAA,MAClB,SAASJ;AAAA,MACT,KAAKC;AAAA,IACX;AAGE,MAAIE,MAAW,uBAAuB;AACpC,UAAMG,IAAQ,OAAON,IAAM9B,IACrBqC,IAAS,OAAON,IAAOC,IACvBM,IAAQhB,GAAiBc,GAAOC,CAAM;AAC5C,WAAO;AAAA,MACL,IAAIR,IAAUS,EAAM;AAAA,MACpB,IAAIT,IAAUS,EAAM;AAAA,MACpB,IAAIT,IAAUS,EAAM;AAAA,MACpB,SAASF;AAAA,MACT,KAAKC;AAAA,IACX;AAAA,EACE;AAEA,QAAME,IAAOL,EAAI,KAAKC,EAAI,KAAOD,EAAI,KAAKC,EAAI,KAAOD,EAAI,KAAKC,EAAI,IAC5DK,IAAS,KAAK,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,GAAGD,CAAG,CAAC,CAAC,GACjDnC,IAAKoC,IAAS,OAAQ,IAAI,KAAK,IAAIA,IAAS,CAAC,IAAKA,IAAS;AAEjE,SAAO;AAAA,IACL,IAAI,MAAMX,KAAWK,EAAI,KAAKC,EAAI,MAAM/B;AAAA,IACxC,IAAI,MAAMyB,KAAWK,EAAI,KAAKC,EAAI,MAAM/B;AAAA,IACxC,IAAI,MAAMyB,KAAWK,EAAI,KAAKC,EAAI,MAAM/B;AAAA,IACxC,SAASJ;AAAA,IACT,KAAKgC;AAAA,EACT;AACA;AAEA,SAASS,GAASC,IAAc,CAAA,GAAIC,IAAc,CAAA,GAAIC,IAAU,IAAI;AAClE,QAAM;AAAA,IACJ,MAAAC,IAAO;AAAA,IACP,WAAA3B,IAAY;AAAA,IACZ,QAAAe,IAAS;AAAA,EACb,IAAMW,GAEEE,IAAW,OAAO,SAAS,OAAOD,CAAI,CAAC,KAAK,OAAOA,CAAI,IAAI,IAAI,OAAOA,CAAI,IAAI,GAE9EE,IAAmB9B,GAAuByB,GAAaxB,CAAS,GAChE8B,IAAmB/B,GAAuB0B,GAAazB,KAAa6B,EAAiB,QAAQ;AAEnG,MAAI,CAACA,EAAiB,KAAK,UAAU,CAACC,EAAiB,KAAK,OAAQ,QAAO,CAAA;AAE3E,QAAMC,IAAgB,oBAAI,IAAG;AAC7B,EAAAF,EAAiB,KAAK,QAAQ,CAAC1M,MAAQ;AACrC,IAAI,CAACA,EAAI,WAAW4M,EAAc,IAAI5M,EAAI,OAAO,KACjD4M,EAAc,IAAI5M,EAAI,SAASA,CAAG;AAAA,EACpC,CAAC;AAED,QAAM6M,IAAgB,oBAAI,IAAG;AAC7B,EAAAF,EAAiB,KAAK,QAAQ,CAAC3M,MAAQ;AACrC,IAAKA,EAAI,YACJ6M,EAAc,IAAI7M,EAAI,OAAO,KAAG6M,EAAc,IAAI7M,EAAI,SAAS,EAAE,GACtE6M,EAAc,IAAI7M,EAAI,OAAO,EAAE,KAAKA,CAAG;AAAA,EACzC,CAAC;AAED,QAAM8M,IAAM,CAAA;AACZ,SAAAD,EAAc,QAAQ,CAAC1E,GAAU5G,MAAW;AAC1C,UAAM6G,IAASwE,EAAc,IAAIrL,CAAM;AACvC,QAAI,CAAC6G,EAAQ;AAEb,UAAMpG,IAAS,CAAC,GAAGmG,CAAQ,EACxB,IAAI,CAACnI,OAAS;AAAA,MACb,GAAGA;AAAA,MACH,MAAM6G,GAAS7G,EAAI,IAAI;AAAA,MACvB,SAAS6G,GAAS7G,EAAI,OAAO;AAAA,MAC7B,KAAK6G,GAAS7G,EAAI,GAAG;AAAA,IAC7B,EAAQ,EACD,OAAO,CAACA,MAAQ,OAAO,SAASA,EAAI,IAAI,KAAK,OAAO,SAASA,EAAI,OAAO,KAAK,OAAO,SAASA,EAAI,GAAG,CAAC,EACrG,KAAK,CAACiC,GAAGC,MAAMD,EAAE,OAAOC,EAAE,IAAI;AAEjC,QAAI,CAACF,EAAO,OAAQ;AAEpB,QAAI+K,IAAIlG,GAASuB,EAAO,GAAG,CAAC,GACxB4E,IAAInG,GAASuB,EAAO,GAAG,CAAC,GACxB6E,IAAIpG,GAASuB,EAAO,GAAG,CAAC,GACxB8E,IAAWlL,EAAO,CAAC,EAAE;AACzB,UAAMmL,IAASnL,EAAO,CAAC,EAAE,SACnBoL,IAAUpL,EAAO,CAAC,EAAE,KAEpBqL,IAAc;AAAA,MAClB,SAAS9L;AAAA,MACT,IAAI2L;AAAA,MACJ,GAAAH;AAAA,MACA,GAAAC;AAAA,MACA,GAAAC;AAAA,MACA,SAASE;AAAA,MACT,KAAKC;AAAA,IACX;AAEI,IAAIV,EAAiB,aAAa,aAAatE,EAAOsE,EAAiB,QAAQ,MAAM,WACnFW,EAAYX,EAAiB,QAAQ,IAAItE,EAAOsE,EAAiB,QAAQ,IAE3EI,EAAI,KAAKO,CAAW;AAEpB,aAAS1I,IAAM,GAAGA,IAAM3C,EAAO,SAAS,GAAG2C,KAAO,GAAG;AACnD,YAAM2I,IAAKtL,EAAO2C,CAAG,GACf4I,IAAKvL,EAAO2C,IAAM,CAAC,GACnB6I,IAAMF,EAAG,MAET9B,IADM+B,EAAG,OACOC;AACtB,UAAIhC,KAAW,EAAG;AAElB,YAAMiC,IAAe,KAAK,IAAI,GAAG,KAAK,KAAKjC,IAAUiB,CAAQ,CAAC,GACxDiB,IAAclC,IAAUiC;AAE9B,eAASE,IAAU,GAAGA,IAAUF,GAAcE,KAAW,GAAG;AAC1D,QAAAT,KAAYQ;AACZ,cAAME,KAAUV,IAAWM,KAAOhC,GAC5BqC,IAAWP,EAAG,UAAUM,KAAUL,EAAG,UAAUD,EAAG,UAClDQ,IAAYR,EAAG,MAAMM,KAAUL,EAAG,MAAMD,EAAG,MAE3CS,IAAOxC,GAAoBmC,GAAaJ,EAAG,SAASA,EAAG,KAAKC,EAAG,SAASA,EAAG,KAAK3B,CAAM;AAC5F,QAAAmB,KAAKgB,EAAK,IACVf,KAAKe,EAAK,IACVd,KAAKc,EAAK;AAEV,cAAMC,IAAS;AAAA,UACb,SAASzM;AAAA,UACT,IAAI2L;AAAA,UACJ,GAAAH;AAAA,UACA,GAAAC;AAAA,UACA,GAAAC;AAAA,UACA,SAASrB,MAAW,sBAAsBiC,IAAWE,EAAK;AAAA,UAC1D,KAAKnC,MAAW,sBAAsBkC,IAAYC,EAAK;AAAA,QACjE;AAEQ,QAAIrB,EAAiB,aAAa,aAAatE,EAAOsE,EAAiB,QAAQ,MAAM,WACnFsB,EAAOtB,EAAiB,QAAQ,IAAItE,EAAOsE,EAAiB,QAAQ,IAEtEI,EAAI,KAAKkB,CAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF,CAAC,GAEMlB;AACT;AASO,SAASmB,GAAyB3G,GAASC,GAASgF,IAAU,CAAA,GAAI;AACvE,SAAOH,GAAS9E,GAASC,GAAS,EAAE,GAAGgF,GAAS,QAAQ,qBAAqB;AAC/E;AASO,SAAS2B,GAAmB5G,GAASC,GAASgF,IAAU,CAAA,GAAI;AACjE,SAAOH,GAAS9E,GAASC,GAAS,EAAE,GAAGgF,GAAS,QAAQ,cAAc;AACxE;AASO,SAAS4B,GAA2B7G,GAASC,GAASgF,IAAU,CAAA,GAAI;AACzE,SAAOH,GAAS9E,GAASC,GAAS,EAAE,GAAGgF,GAAS,QAAQ,uBAAuB;AACjF;AASO,SAAS6B,GAAY9G,GAASC,GAASgF,IAAU,CAAA,GAAI;AAC1D,SAAO0B,GAAyB3G,GAASC,GAASgF,CAAO;AAC3D;AAMA,SAAS8B,GAAuBC,GAAWC,GAAO;AAChD,MAAI,CAACD,EAAU,UAAU,CAAC,OAAO,SAASC,CAAK,EAAG,QAAO;AACzD,MAAIC,IAAO,MACPC,IAAW;AACf,WAAS3F,IAAI,GAAGA,IAAIwF,EAAU,QAAQxF,KAAK,GAAG;AAC5C,UAAM9I,IAAMsO,EAAUxF,CAAC,GACjB4F,IAAK7H,GAAS7G,EAAI,EAAE;AAC1B,QAAI,CAAC,OAAO,SAAS0O,CAAE,EAAG;AAC1B,UAAMC,IAAO,KAAK,IAAID,IAAKH,CAAK;AAChC,IAAII,IAAOF,MACTA,IAAWE,GACXH,IAAOxO;AAAA,EAEX;AACA,SAAOwO;AACT;AAWO,SAASI,GAAqBC,IAAS,CAAA,GAAIC,IAAS,CAAA,GAAIvC,IAAU,IAAI;AAC3E,QAAM1B,IAAY0B,EAAQ,aAAa,WACjCwC,IAAkBnE,GAAuBiE,GAAQhE,CAAS,GAC1DmE,IAAkBpE,GAAuBkE,GAAQjE,CAAS;AAEhE,MAAI,CAACkE,EAAgB,KAAK,UAAU,CAACC,EAAgB,KAAK,OAAQ,QAAO,CAAC,GAAGD,EAAgB,IAAI;AAEjG,QAAME,IAAe,oBAAI,IAAG;AAC5B,SAAAD,EAAgB,KAAK,QAAQ,CAAChP,MAAQ;AACpC,IAAKA,EAAI,YACJiP,EAAa,IAAIjP,EAAI,OAAO,KAAGiP,EAAa,IAAIjP,EAAI,SAAS,EAAE,GACpEiP,EAAa,IAAIjP,EAAI,OAAO,EAAE,KAAKA,CAAG;AAAA,EACxC,CAAC,GACDiP,EAAa,QAAQ,CAAClO,GAAMQ,MAAW;AACrC,IAAA0N,EAAa,IAAI1N,GAAQ,CAAC,GAAGR,CAAI,EAAE,KAAK,CAACkB,GAAGC,MAAM2E,GAAS5E,EAAE,IAAI,CAAC,IAAI4E,GAAS3E,EAAE,IAAI,CAAC,CAAC,CAAC;AAAA,EAC1F,CAAC,GAEM6M,EAAgB,KAAK,IAAI,CAACG,MAAU;AACzC,UAAMxN,IAAOmF,GAASqI,EAAM,IAAI,GAC1BtN,IAAKiF,GAASqI,EAAM,EAAE,GACtBX,IAAQ,OAAO,SAAS7M,CAAI,KAAK,OAAO,SAASE,CAAE,IAAI,OAAOF,IAAOE,KAAM;AACjF,QAAI,CAACsN,EAAM,WAAW,CAAC,OAAO,SAASX,CAAK,EAAG,QAAO,EAAE,GAAGW,EAAK;AAEhE,UAAMC,IAAUd,GAAuBY,EAAa,IAAIC,EAAM,OAAO,KAAK,CAAA,GAAIX,CAAK;AACnF,QAAI,CAACY,EAAS,QAAO,EAAE,GAAGD,EAAK;AAE/B,UAAME,IAAS,EAAE,GAAGF,EAAK;AACzB,YAAC,MAAM,KAAK,KAAK,KAAK,WAAW,KAAK,EAAE,QAAQ,CAACtO,MAAQ;AACvD,MAAIuO,EAAQvO,CAAG,MAAM,WACjB,OAAO,UAAU,eAAe,KAAKwO,GAAQxO,CAAG,IAClDwO,EAAO,GAAGxO,CAAG,QAAQ,IAAIuO,EAAQvO,CAAG,IAEpCwO,EAAOxO,CAAG,IAAIuO,EAAQvO,CAAG;AAAA,IAE7B,CAAC,GACMwO;AAAA,EACT,CAAC;AACH;ACvTO,SAASC,GAAmB5M,GAAMvC,IAAkB,MAAM;AAC/D,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAMJ,GAAM;AAAA,MACf,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAACK,MAAY;AACrB,cAAMQ,IAAS,oBAAI,IAAG;AACtB,QAAAR,EAAQ,KAAK,QAAQ,CAAC5B,GAAQyD,MAAQ;AACpC,gBAAM3E,IAAMD,GAAmBmB,GAAQ,MAAMhB,CAAe,GAEtDoB,IAAYtB,EAAIoB,CAAO,GACvBG,IAASD,MAAc,SAAY,GAAGA,CAAS,GAAG,KAAI,IAAK,IAC3DyL,IAAI/M,EAAIsP,EAAO,KAAKtP,EAAI,GACxBgN,IAAIhN,EAAIuP,EAAQ,KAAKvP,EAAI,GACzBiN,IAAIjN,EAAIwP,EAAS,KAAKxP,EAAI,GAC1ByP,IAAQzP,EAAI,SAAS2E;AAE3B,UAAI,CAACpD,KAAUwL,MAAM,QAAQA,MAAM,UAAaC,MAAM,QAAQA,MAAM,UAAaC,MAAM,QAAQA,MAAM,WAEhG3J,EAAO,IAAI/B,CAAM,KAAG+B,EAAO,IAAI/B,GAAQ,EAAE,GAC9C+B,EAAO,IAAI/B,CAAM,EAAE,KAAK;AAAA,YACtB,GAAGvB;AAAA,YACH,QAAAuB;AAAA,YACA,OAAAkO;AAAA,YACA,GAAG,OAAO1C,CAAC,KAAK;AAAA,YAChB,GAAG,OAAOC,CAAC,KAAK;AAAA,YAChB,GAAG,OAAOC,CAAC,KAAK;AAAA,UAC5B,CAAW;AAAA,QACH,CAAC;AAED,cAAMpJ,IAAQ,MAAM,KAAKP,EAAO,SAAS,EAAE,IAAI,CAAC,CAAC/B,GAAQmO,CAAG,OAAO;AAAA,UACjE,IAAInO;AAAA,UACJ,QAAQmO,EACL,KAAK,CAACzN,GAAGC,MAAMD,EAAE,QAAQC,EAAE,KAAK,EAChC,IAAI,CAACkI,OAAO;AAAA,YACX,GAAGA;AAAA,YACH,GAAG,OAAOA,EAAE,CAAC,KAAK;AAAA,YAClB,GAAG,OAAOA,EAAE,CAAC,KAAK;AAAA,YAClB,GAAG,OAAOA,EAAE,CAAC,KAAK;AAAA,UAChC,EAAc;AAAA,QACd,EAAU;AAEF,QAAA1H,EAAQ,EAAE,OAAAmB,GAAO;AAAA,MACnB;AAAA,MACA,OAAO,CAACb,MAAUL,EAAOM,EAAqB,sBAAsBD,CAAK,CAAC;AAAA,IAChF,CAAK;AAAA,EACH,CAAC;AACH;AC3BA,SAAS2M,GAAQC,GAAQ;AACvB,SAAKA,IACD,MAAM,QAAQA,CAAM,IAAU,CAAC,GAAGA,CAAM,IACrC,CAAA,IAFa,CAAA;AAGtB;AAMA,SAAS/I,EAASlG,GAAO;AACvB,QAAM,IAAI,OAAOA,CAAK;AACtB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAMA,SAASkP,GAAc9O,IAAO,IAAI+O,IAAU,CAAA,GAAI;AAC9C,QAAMhD,IAAM,CAAC,GAAG/L,CAAI;AACpB,SAAA+L,EAAI,KAAK,CAAC7K,GAAGC,MAAM;AACjB,aAAS,IAAI,GAAG,IAAI4N,EAAQ,QAAQ,KAAK,GAAG;AAC1C,YAAMpP,IAAMoP,EAAQ,CAAC,GACfC,IAAK9N,KAAA,gBAAAA,EAAIvB,IACTsP,IAAK9N,KAAA,gBAAAA,EAAIxB;AACf,UAAIqP,MAAOC;AACX,eAAwBD,KAAO,OAAa,IACpBC,KAAO,OAAa,KACxC,OAAOD,KAAO,YAAY,OAAOC,KAAO,WAAiBD,IAAKC,IAC3D,GAAGD,CAAE,GAAG,cAAc,GAAGC,CAAE,EAAE;AAAA,IACtC;AACA,WAAO;AAAA,EACT,CAAC,GACMlD;AACT;AAEA,SAASmD,GAA+BlP,IAAO,IAAImP,IAAQ,aAAa;AACtE,MAAI,CAACnP,EAAK,OAAQ;AAClB,QAAMuE,IAAUuK,GAAc9O,GAAM,CAACK,GAASO,GAAME,CAAE,CAAC,GACjDsO,IAAe,oBAAI,IAAG;AAE5B,EAAA7K,EAAQ,QAAQ,CAACtF,MAAQ;AACvB,UAAMuB,IAAS,IAAGvB,KAAA,gBAAAA,EAAMoB,OAAY,EAAE,GAAG,KAAI,GACvCgP,IAAY,OAAOpQ,KAAA,gBAAAA,EAAM2B,EAAK,GAC9B0O,IAAU,OAAOrQ,KAAA,gBAAAA,EAAM6B,EAAG;AAChC,QAAI,CAACN,KAAU,CAAC,OAAO,SAAS6O,CAAS,KAAK,CAAC,OAAO,SAASC,CAAO,EAAG;AAEzE,UAAMC,IAASH,EAAa,IAAI5O,CAAM;AACtC,QAAI,OAAO,SAAS+O,CAAM,KAAKF,IAAYE;AACzC,YAAMrN;AAAA,QACJ;AAAA,QACA,IAAI,MAAM,GAAGiN,CAAK,gCAAgC3O,CAAM,WAAW6O,CAAS,6BAA6BE,CAAM,EAAE;AAAA,MACzH;AAEI,IAAAH,EAAa,IAAI5O,GAAQ8O,CAAO;AAAA,EAClC,CAAC;AACH;AAMA,SAASE,GAASX,GAAQY,IAAkB,IAAI;AAC9C,SAAO,IAAI,QAAQ,CAAC9N,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAM+M,GAAQ;AAAA,MACjB,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,GAAGY;AAAA,MACH,UAAU,CAAC1N,MAAYJ,EAAQ,MAAM,QAAQI,KAAA,gBAAAA,EAAS,IAAI,IAAIA,EAAQ,OAAO,CAAA,CAAE;AAAA,MAC/E,OAAO,CAACE,MAAUL,EAAOM,EAAqB,kBAAkBD,CAAK,CAAC;AAAA,IAC5E,CAAK;AAAA,EACH,CAAC;AACH;AASO,SAASjD,GAAmBgB,IAAO,CAAA,GAAId,IAAY,MAAMC,IAAkB,MAAM;AACtF,SAAOa,EAAK,IAAI,CAACf,MAAQyQ,GAAezQ,GAAKC,GAAWC,CAAe,CAAC;AAC1E;AAEO,eAAewQ,GAAUd,GAAQrD,IAAU,IAAI;AACpD,QAAM;AAAA,IACJ,MAAAoE,IAAO;AAAA,IACP,WAAA1Q,IAAY;AAAA,IACZ,iBAAAC,IAAkB;AAAA,IAClB,iBAAAsQ,IAAkB,CAAA;AAAA,EACtB,IAAMjE;AAEJ,MAAIxL;AACJ,MAAI,MAAM,QAAQ6O,CAAM;AACtB,IAAA7O,IAAO4O,GAAQC,CAAM;AAAA,WACZe,MAAS;AAClB,IAAA5P,IAAO,MAAMwP,GAASX,GAAQY,CAAe;AAAA,MACxC,OAAIG,MAAS,aAAaA,MAAS,QAClC1N,EAAqB,aAAa,IAAI,MAAM,mCAAmC0N,CAAI,EAAE,CAAC,IAEtF1N,EAAqB,aAAa,IAAI,MAAM,qBAAqB0N,CAAI,EAAE,CAAC;AAGhF,SAAO5Q,GAAmBgB,GAAMd,GAAWC,CAAe;AAC5D;AAaO,eAAe0Q,GAAYhB,GAAQrD,IAAU,IAAI;AACtD,QAAM;AAAA,IACJ,KAAAsE,IAAM;AAAA,IACN,iBAAA3Q,IAAkB;AAAA,IAClB,SAAA4Q,IAAU;AAAA,IACV,GAAGC;AAAA,EACP,IAAMxE,GAEEyE,IAAe,MAAMN,GAAUd,GAAQ,EAAE,GAAGmB,GAAc,iBAAA7Q,GAAiB;AAIjF,MAAI,CADc8Q,EAAa,KAAK,CAAAhR,MAAOoB,KAAWpB,CAAG;AAEvD,UAAMiD,EAAqB,eAAe,IAAI,MAAM,gCAAgC7B,CAAO,EAAE,CAAC;AAIhG,QAAM6P,IAAQD,EAAa,KAAK,CAAAhR,MAAOsP,MAAWtP,KAAOuP,MAAYvP,CAAG,GAClEkR,IAAYF,EAAa,KAAK,CAAAhR,MAAO8G,MAAY9G,KAAOgH,MAAahH,CAAG;AAE9E,MAAI,CAACiR,KAAS,CAACC;AACb,UAAMjO,EAAqB,eAAe,IAAI,MAAM,uFAAuF,CAAC;AAG9I,QAAMc,IAAaiN,EAAa,IAAI,CAAChR,MAAQ;AAC3C,UAAMmR,IAAS,EAAE,GAAGnR,EAAG;AAGvB,QAAIoB,KAAW+P,GAAQ;AACrB,YAAM5G,IAAM4G,EAAO/P,CAAO;AAC1B,MAAA+P,EAAO/P,CAAO,IAAyBmJ,KAAQ,OAAO,KAAK,GAAGA,CAAG,GAAG,KAAI;AAAA,IAC1E;AAGA,WAAIzD,MAAYqK,MAAQA,EAAOrK,EAAQ,IAAID,EAASsK,EAAOrK,EAAQ,CAAC,IAChEE,MAAamK,MAAQA,EAAOnK,EAAS,IAAIH,EAASsK,EAAOnK,EAAS,CAAC,IACnEwI,MAAa2B,MAAQA,EAAO3B,EAAS,IAAI3I,EAASsK,EAAO3B,EAAS,CAAC,IACnEF,MAAW6B,MAAQA,EAAO7B,EAAO,IAAIzI,EAASsK,EAAO7B,EAAO,CAAC,IAC7DC,MAAY4B,MAAQA,EAAO5B,EAAQ,IAAI1I,EAASsK,EAAO5B,EAAQ,CAAC,IAGhE,EAAE,wBAAwB4B,MAAW/P,KAAW+P,MAClDA,EAAO,qBAAqBA,EAAO/P,CAAO,IAGrC+P;AAAA,EACT,CAAC;AAcD,MAAI,CAXapN,EAAW,MAAM,CAAC/D,MAC7B,GAACA,EAAIoB,CAAO,KACZ8P,MAAc,CAAC,OAAO,SAASlR,EAAI8G,EAAQ,CAAC,KAAK,CAAC,OAAO,SAAS9G,EAAIgH,EAAS,CAAC,MAGhFiK,KAAS,CAACC,MAAc,CAAC,OAAO,SAASlR,EAAIsP,EAAO,CAAC,KAAK,CAAC,OAAO,SAAStP,EAAIuP,EAAQ,CAAC,GAI7F;AAGC,UAAMtM,EAAqB,eAAe,IAAI,MAAM,0CAA0C,CAAC;AAGjG,SAAOc;AACT;AAYO,eAAeqN,GAAYxB,GAAQrD,IAAU,IAAI;AACtD,QAAM;AAAA,IACJ,iBAAArM,IAAkB;AAAA,IAClB,SAAA4Q,IAAU;AAAA,IACV,GAAGC;AAAA,EACP,IAAMxE,GAEEyE,IAAe,MAAMN,GAAUd,GAAQ,EAAE,GAAGmB,GAAc,iBAAA7Q,GAAiB,GAG3EmR,IAAW,CAACjQ,GAASmF,GAAOE,GAASD,CAAG;AAC9C,aAAW9F,KAAO2Q;AAEhB,QAAI,CADcL,EAAa,KAAK,CAAAhR,MAAOU,KAAOV,CAAG;AAEnD,YAAMiD,EAAqB,eAAe,IAAI,MAAM,gCAAgCvC,CAAG,EAAE,CAAC;AAI9F,QAAMqD,IAAaiN,EAAa,IAAI,CAAChR,MAAQ;AAC3C,UAAMmR,IAAS,EAAE,GAAGnR,EAAG;AAGvB,QAAIoB,KAAW+P,GAAQ;AACrB,YAAM5G,IAAM4G,EAAO/P,CAAO;AAC1B,MAAA+P,EAAO/P,CAAO,IAAyBmJ,KAAQ,OAAO,KAAK,GAAGA,CAAG,GAAG,KAAI;AAAA,IAC1E;AAGA,WAAIhE,KAAS4K,MAAQA,EAAO5K,CAAK,IAAIM,EAASsK,EAAO5K,CAAK,CAAC,IACvD1E,KAAMsP,MAAQA,EAAOtP,CAAE,IAAIgF,EAASsK,EAAOtP,CAAE,CAAC,IAC9C4E,KAAW0K,MAAQA,EAAO1K,CAAO,IAAII,EAASsK,EAAO1K,CAAO,CAAC,IAC7DD,KAAO2K,MAAQA,EAAO3K,CAAG,IAAIK,EAASsK,EAAO3K,CAAG,CAAC,IAE9C2K;AAAA,EACT,CAAC;AAWD,MAAI,CARapN,EAAW,MAAM,CAAC/D,MAC7B,GAACA,EAAIoB,CAAO,KACZ,CAAC,OAAO,SAASpB,EAAIuG,CAAK,CAAC,KAC3B,CAAC,OAAO,SAASvG,EAAIyG,CAAO,CAAC,KAC7B,CAAC,OAAO,SAASzG,EAAIwG,CAAG,CAAC,EAE9B;AAGC,UAAMvD,EAAqB,eAAe,IAAI,MAAM,0CAA0C,CAAC;AAGjG,SAAO4M,GAAc9L,GAAY,CAAC3C,GAASmF,CAAK,CAAC;AACnD;AAYO,eAAe+K,GAAW1B,GAAQrD,IAAU,IAAI;AACrD,QAAM;AAAA,IACJ,iBAAArM,IAAkB;AAAA,IAClB,SAAA4Q,IAAU;AAAA,IACV,GAAGC;AAAA,EACP,IAAMxE,GAEEyE,IAAe,MAAMN,GAAUd,GAAQ,EAAE,GAAGmB,GAAc,iBAAA7Q,GAAiB,GAG3EmR,IAAW,CAACjQ,GAASO,GAAME,CAAE;AACnC,aAAWnB,KAAO2Q;AAEhB,QAAI,CADcL,EAAa,KAAK,CAAAhR,MAAOU,KAAOV,CAAG;AAEnD,YAAMiD,EAAqB,cAAc,IAAI,MAAM,+BAA+BvC,CAAG,EAAE,CAAC;AAI5F,QAAMqD,IAAaiN,EAAa,IAAI,CAAChR,MAAQ;AAC3C,UAAMmR,IAAS,EAAE,GAAGnR,EAAG;AAGvB,QAAIoB,KAAW+P,GAAQ;AACrB,YAAM5G,IAAM4G,EAAO/P,CAAO;AAC1B,MAAA+P,EAAO/P,CAAO,IAAyBmJ,KAAQ,OAAO,KAAK,GAAGA,CAAG,GAAG,KAAI;AAAA,IAC1E;AAGA,WAAI5I,KAAQwP,MAAQA,EAAOxP,CAAI,IAAIkF,EAASsK,EAAOxP,CAAI,CAAC,IACpDE,KAAMsP,MAAQA,EAAOtP,CAAE,IAAIgF,EAASsK,EAAOtP,CAAE,CAAC,IAG9CF,KAAQwP,KAAUtP,KAAMsP,KAAU,OAAO,SAASA,EAAOxP,CAAI,CAAC,KAAK,OAAO,SAASwP,EAAOtP,CAAE,CAAC,MAC/FsP,EAAOI,EAAG,IAAI,OAAOJ,EAAOxP,CAAI,IAAIwP,EAAOtP,CAAE,KAGxCsP;AAAA,EACT,CAAC;AAWD,MAAI,CARapN,EAAW,MAAM,CAAC/D,MAC7B,GAACA,EAAIoB,CAAO,KACZ,CAAC,OAAO,SAASpB,EAAI2B,CAAI,CAAC,KAC1B,CAAC,OAAO,SAAS3B,EAAI6B,CAAE,CAAC,KACxB,EAAE7B,EAAI6B,CAAE,IAAI7B,EAAI2B,CAAI,GAEzB;AAGC,UAAMsB,EAAqB,cAAc,IAAI,MAAM,yCAAyC,CAAC;AAG/F,SAAO4M,GAAc9L,GAAY,CAAC3C,GAASO,GAAME,CAAE,CAAC;AACtD;AAUO,eAAe2P,GAAY5B,GAAQrD,IAAU,IAAI;AACtD,QAAM;AAAA,IACJ,iBAAArM,IAAkB;AAAA,IAClB,SAAA4Q,IAAU;AAAA,IACV,GAAGC;AAAA,EACP,IAAMxE,GAEEyE,IAAe,MAAMN,GAAUd,GAAQ,EAAE,GAAGmB,GAAc,iBAAA7Q,GAAiB,GAE3EmR,IAAW,CAACjQ,GAASO,GAAME,CAAE;AACnC,aAAWnB,KAAO2Q;AAEhB,QAAI,CADcL,EAAa,KAAK,CAAChR,MAAQU,KAAOV,CAAG;AAErD,YAAMiD,EAAqB,eAAe,IAAI,MAAM,iCAAiCvC,CAAG,EAAE,CAAC;AAI/F,QAAMqD,IAAaiN,EAAa,IAAI,CAAChR,MAAQ;AAC3C,UAAMmR,IAAS,EAAE,GAAGnR,EAAG;AAEvB,QAAIoB,KAAW+P,GAAQ;AACrB,YAAM5G,IAAM4G,EAAO/P,CAAO;AAC1B,MAAA+P,EAAO/P,CAAO,IAAyBmJ,KAAQ,OAAO,KAAK,GAAGA,CAAG,GAAG,KAAI;AAAA,IAC1E;AAEA,IAAI5I,KAAQwP,MAAQA,EAAOxP,CAAI,IAAIkF,EAASsK,EAAOxP,CAAI,CAAC,IACpDE,KAAMsP,MAAQA,EAAOtP,CAAE,IAAIgF,EAASsK,EAAOtP,CAAE,CAAC,IAE9CF,KAAQwP,KAAUtP,KAAMsP,KAAU,OAAO,SAASA,EAAOxP,CAAI,CAAC,KAAK,OAAO,SAASwP,EAAOtP,CAAE,CAAC,MAE3FsP,EAAOtP,CAAE,MAAMsP,EAAOxP,CAAI,MAC5BwP,EAAOxP,CAAI,IAAI,KAAK,MAAMwP,EAAOxP,CAAI,IAAI,GAAI,IAAI,KACjDwP,EAAOtP,CAAE,IAAIsP,EAAOxP,CAAI,IAAI,OAE9BwP,EAAOI,EAAG,IAAI,OAAOJ,EAAOxP,CAAI,IAAIwP,EAAOtP,CAAE;AAG/C,UAAM4P,IAAUN,EAAOO,EAAY,MAAM,UAAaP,EAAOO,EAAY,MAAM,QAAQ,GAAGP,EAAOO,EAAY,CAAC,GAAG,KAAI,MAAO,IACtHC,IAAiBR,EAAOS,EAAmB,MAAM,UAAaT,EAAOS,EAAmB,MAAM,QAAQ,GAAGT,EAAOS,EAAmB,CAAC,GAAG,KAAI,MAAO;AACxJ,WAAI,CAACH,KAAWE,MACdR,EAAOO,EAAY,IAAIP,EAAOS,EAAmB,IAE/CH,KAAW,CAACE,MACdR,EAAOS,EAAmB,IAAIT,EAAOO,EAAY,IAG5CP;AAAA,EACT,CAAC;AAUD,MAAI,CARapN,EAAW,MAAM,CAAC/D,MAC7B,GAACA,EAAIoB,CAAO,KACZ,CAAC,OAAO,SAASpB,EAAI2B,CAAI,CAAC,KAC1B,CAAC,OAAO,SAAS3B,EAAI6B,CAAE,CAAC,KACxB,EAAE7B,EAAI6B,CAAE,IAAI7B,EAAI2B,CAAI,GAEzB;AAGC,UAAMsB,EAAqB,eAAe,IAAI,MAAM,sDAAsD,CAAC;AAU7G,MAAI,CAPgBc,EAAW,KAAK,CAAC/D,MAAQ;AAC3C,UAAM6R,IAAO7R,EAAI0R,EAAY,GACvBI,IAAc9R,EAAI4R,EAAmB;AAC3C,WAA8BC,KAAS,QAAQ,GAAGA,CAAI,GAAG,KAAI,MAAO,MACpCC,KAAgB,QAAQ,GAAGA,CAAW,GAAG,KAAI,MAAO;AAAA,EACtF,CAAC;AAGC,UAAM7O,EAAqB,eAAe,IAAI,MAAM,8CAA8CyO,EAAY,OAAOE,EAAmB,EAAE,CAAC;AAK7I,MAFA3B,GAA+BlM,GAAY,SAAS,GAEhD,CAAC+M,GAAS;AACZ,UAAMiB,IAAO,IAAI,IAAI,OAAO,KAAKC,EAAiC,CAAC;AACnE,WAAOnC;AAAA,MACL9L,EAAW,IAAI,CAAC/D,MAAQ,OAAO,YAAY,OAAO,QAAQA,CAAG,EAAE,OAAO,CAAC,CAACmD,CAAC,MAAM4O,EAAK,IAAI5O,CAAC,CAAC,CAAC,CAAC;AAAA,MAC5F,CAAC/B,GAASO,GAAME,CAAE;AAAA,IACxB;AAAA,EACE;AAEA,SAAOgO,GAAc9L,GAAY,CAAC3C,GAASO,GAAME,CAAE,CAAC;AACtD;AAWO,SAASoQ,GAAmBpD,IAAS,CAAA,GAAIC,IAAS,CAAA,GAAIvC,IAAU,IAAI;AACzE,QAAM2F,IAAS,MAAM,QAAQ3F,EAAQ,MAAM,KAAKA,EAAQ,OAAO,SAASA,EAAQ,SAAS,CAACnL,CAAO;AACjG,MAAI,CAAC0N,EAAO,OAAQ,QAAO,CAAC,GAAGD,CAAM;AAErC,QAAMsD,IAAQ,CAACnS,MAAQkS,EAAO,IAAI,CAACxR,MAAQ,IAAGV,KAAA,gBAAAA,EAAMU,OAAQ,EAAE,EAAE,EAAE,KAAK,GAAG,GACpE0R,IAAc,oBAAI,IAAG;AAC3B,SAAAtD,EAAO,QAAQ,CAACuD,MAAU;AACxB,IAAAD,EAAY,IAAID,EAAME,CAAK,GAAGA,CAAK;AAAA,EACrC,CAAC,GAEMxD,EAAO,IAAI,CAACK,MAAU;AAC3B,UAAMmD,IAAQD,EAAY,IAAID,EAAMjD,CAAK,CAAC;AAC1C,QAAI,CAACmD,EAAO,QAAO,EAAE,GAAGnD,EAAK;AAC7B,UAAME,IAAS,EAAE,GAAGF,EAAK;AACzB,kBAAO,QAAQmD,CAAK,EAAE,QAAQ,CAAC,CAACzR,GAAKD,CAAK,MAAM;AAC9C,MAAIuR,EAAO,SAAStR,CAAG,MACnB,OAAO,UAAU,eAAe,KAAKwO,GAAQxO,CAAG,IAClDwO,EAAO,GAAGxO,CAAG,QAAQ,IAAID,IAEzByO,EAAOxO,CAAG,IAAID;AAAA,IAElB,CAAC,GACMyO;AAAA,EACT,CAAC;AACH;AAQO,SAASkD,GAAgBvR,IAAO,IAAIwR,IAAY,MAAM;AAC3D,SAAIA,KAAc,OAAwC,CAAC,GAAGxR,CAAI,IAC7DA,EAAK,SAGWA,EAAK,KAAK,CAAAf,MAAOyB,MAAczB,CAAG,IAGhDe,EAAK,OAAO,CAACf,OAAQA,KAAA,gBAAAA,EAAMyB,SAAgB8Q,CAAS,IAFjC,CAAC,GAAGxR,CAAI,IAJT,CAAA;AAO3B;AAQO,SAASyR,GAAczR,IAAO,IAAI+O,IAAU,CAAA,GAAI;AACrD,SAAO/O,EAAK,IAAI,CAACf,MAAQ;AACvB,UAAMyS,IAAO,EAAE,GAAGzS,EAAG;AACrB,WAAA8P,EAAQ,QAAQ,CAAC4C,MAAW;AAC1B,UAAI,EAAEA,KAAUD,GAAO;AACvB,YAAM/H,IAAI7D,EAAS4L,EAAKC,CAAM,CAAC;AAC/B,MAAAD,EAAKC,CAAM,IAAIhI;AAAA,IACjB,CAAC,GACM+H;AAAA,EACT,CAAC;AACH;AAaO,SAASE,GAAgB;AAAA,EAC9B,SAAArL,IAAU,CAAA;AAAA,EACV,SAAAC,IAAU,CAAA;AAAA,EACV,QAAAsH,IAAS,CAAA;AAAA,EACT,SAAA+D,IAAU,CAAA;AAAA,EACV,YAAAC,IAAa,CAAA;AAAA,EACb,UAAAC,IAAW,CAAA;AACb,IAAI,IAAI;AACN,SAAO;AAAA,IACL,SAASnD,GAAQrI,CAAO;AAAA,IACxB,SAASqI,GAAQpI,CAAO;AAAA,IACxB,QAAQoI,GAAQd,CAAM;AAAA,IACtB,SAASc,GAAQiD,CAAO;AAAA,IACxB,YAAYjD,GAAQkD,CAAU;AAAA,IAC9B,UAAUC,KAAY,CAAA;AAAA,EAC1B;AACA;AChhBA,MAAM7R,KAAe,CAACC,GAAQhB,IAAkB,SAASH,GAAmBmB,GAAQ,MAAMhB,CAAe;AAQzG,SAAS6S,GAAahS,GAAM;AAC1B,MAAI,CAACA,EAAK,OAAQ,QAAO;AACzB,QAAMiS,IAAQjS,EAAK,CAAC,GACdkS,IAActR,KAAQqR,KAASnR,KAAMmR,GACrCE,IAAW3M,KAASyM,KAAS,CAACC;AACpC,SAAIA,IAAoB,aACpBC,IAAiB,UACd;AACT;AAMA,SAASrM,GAASzD,GAAG;AACnB,QAAM,IAAI,OAAOA,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAMA,SAAS+P,GAAuBnT,GAAK;AACnC,QAAMuB,IAASvB,EAAIoB,CAAO,MAAM,SAAY,GAAGpB,EAAIoB,CAAO,CAAC,GAAG,KAAI,IAAK;AACvE,MAAI,CAACG,EAAQ,QAAO;AACpB,QAAM6R,IAAQvM,GAAS7G,EAAIuG,CAAK,CAAC;AACjC,SAAI6M,MAAU,OAAa,OAEpB;AAAA,IACL,CAAChS,CAAO,GAAGG;AAAA,IACX,CAACgF,CAAK,GAAG6M;AAAA,IACT,CAAC5M,CAAG,GAAGK,GAAS7G,EAAIwG,CAAG,CAAC;AAAA,IACxB,CAACC,CAAO,GAAGI,GAAS7G,EAAIyG,CAAO,CAAC;AAAA,IAChC,UAAUzG,EAAI,YAAY,OAAO,GAAGA,EAAI,QAAQ,KAAK;AAAA,IACrD,GAAGA;AAAA,EACP;AACA;AAMA,SAASqT,GAA0BrT,GAAK;AACtC,QAAMuB,IAASvB,EAAIoB,CAAO,MAAM,SAAY,GAAGpB,EAAIoB,CAAO,CAAC,GAAG,KAAI,IAAK;AACvE,MAAI,CAACG,EAAQ,QAAO;AACpB,QAAMG,IAAOmF,GAAS7G,EAAI2B,CAAI,CAAC,GACzBC,IAAKiF,GAAS7G,EAAI6B,CAAE,CAAC;AAC3B,MAAIH,MAAS,QAAQE,MAAO,QAAQA,KAAMF,EAAM,QAAO;AAEvD,QAAM4R,IAAM,OAAO5R,IAAOE;AAC1B,SAAO;AAAA,IACL,CAACR,CAAO,GAAGG;AAAA,IACX,CAACI,CAAI,GAAGD;AAAA,IACR,CAACG,CAAE,GAAGD;AAAA,IACN,KAAA0R;AAAA,IACA,CAAC9M,CAAG,GAAGK,GAAS7G,EAAIwG,CAAG,CAAC;AAAA,IACxB,CAACC,CAAO,GAAGI,GAAS7G,EAAIyG,CAAO,CAAC;AAAA,IAChC,gBAAgBzG,EAAI,kBAAkB,OAAO,GAAGA,EAAI,cAAc,KAAK;AAAA,IACvE,UAAUA,EAAI,YAAY,OAAO,GAAGA,EAAI,QAAQ,KAAK;AAAA,IACrD,GAAGA;AAAA,EACP;AACA;AASO,SAASuT,GAAyBxS,GAAM;AAC7C,QAAMyS,IAAQ,CAAA,GACRC,IAAS,CAAA;AAEf,aAAWzT,KAAOe,GAAM;AACtB,UAAM2S,IAAW,CAAA,GACXxM,IAAML,GAAS7G,EAAIwG,CAAG,CAAC,GACvBmN,IAAK9M,GAAS7G,EAAIyG,CAAO,CAAC;AAEhC,IAAIS,MAAQ,SAASA,IAAM,KAAKA,IAAM,OACpCwM,EAAS,KAAK,OAAOxM,CAAG,uBAAuB,GAE7CyM,MAAO,SAASA,IAAK,KAAKA,KAAM,QAClCD,EAAS,KAAK,WAAWC,CAAE,wBAAwB,GAEjDD,EAAS,SACXD,EAAO,KAAK,EAAE,KAAAzT,GAAK,SAAS0T,EAAS,KAAK,IAAI,GAAG,IAEjDF,EAAM,KAAKxT,CAAG;AAAA,EAElB;AAEA,SAAO,EAAE,OAAAwT,GAAO,QAAAC,EAAM;AACxB;AASO,SAASG,GAAyBhE,GAAQ1P,IAAkB,MAAM;AACvE,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,UAAMkR,IAAO;AAAA,MACX,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAAC/Q,MAAY;AACrB,cAAM/B,IAAO,CAAA;AACb,mBAAWG,KAAU4B,EAAQ,MAAM;AACjC,gBAAM9C,IAAMiB,GAAaC,GAAQhB,CAAe,GAC1C4T,IAAQX,GAAuBnT,CAAG;AACxC,UAAI8T,KAAO/S,EAAK,KAAK+S,CAAK;AAAA,QAC5B;AACA,QAAApR,EAAQ3B,CAAI;AAAA,MACd;AAAA,MACA,OAAO,CAACiC,MAAUL,EAAOM,EAAqB,4BAA4BD,CAAK,CAAC;AAAA,IACtF;AAEI,IAAI,OAAO4M,KAAW,YAAY,CAACA,EAAO,WAAW,OAAO,KAAKA,EAAO,SAAS;AAAA,CAAI,IACnF/M,EAAK,MAAM+M,GAAQiE,CAAI,IAEvBhR,EAAK,MAAM+M,GAAQiE,CAAI;AAAA,EAE3B,CAAC;AACH;AASO,SAASE,GAA4BnE,GAAQ1P,IAAkB,MAAM;AAC1E,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAM+M,GAAQ;AAAA,MACjB,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAAC9M,MAAY;AACrB,cAAM/B,IAAO,CAAA;AACb,mBAAWG,KAAU4B,EAAQ,MAAM;AACjC,gBAAM9C,IAAMiB,GAAaC,GAAQhB,CAAe,GAC1CwD,IAAW2P,GAA0BrT,CAAG;AAC9C,UAAI0D,KAAU3C,EAAK,KAAK2C,CAAQ;AAAA,QAClC;AACA,QAAAhB,EAAQ3B,CAAI;AAAA,MACd;AAAA,MACA,OAAO,CAACiC,MAAUL,EAAOM,EAAqB,+BAA+BD,CAAK,CAAC;AAAA,IACzF,CAAK;AAAA,EACH,CAAC;AACH;AAYO,SAASgR,GAAgBjT,GAAM8J,IAAYzJ,GAAS;AACzD,QAAM6S,IAAO,oBAAI,IAAG;AACpB,aAAWjU,KAAOe,GAAM;AACtB,UAAMyD,IAAKxE,EAAI6K,CAAS,KAAK,OAAO,OAAO7K,EAAI6K,CAAS,CAAC,EAAE,KAAI,IAAK;AACpE,IAAKrG,MACAyP,EAAK,IAAIzP,CAAE,KAAGyP,EAAK,IAAIzP,GAAI,EAAE,QAAQA,GAAI,QAAQ,CAAA,EAAE,CAAE,GAC1DyP,EAAK,IAAIzP,CAAE,EAAE,OAAO,KAAKxE,CAAG;AAAA,EAC9B;AACA,SAAO,MAAM,KAAKiU,EAAK,OAAM,CAAE;AACjC;AASO,SAASC,GAAmBtE,GAAQ1P,IAAkB,MAAM;AACjE,SAAO,IAAI,QAAQ,CAACwC,GAASC,MAAW;AACtC,IAAAE,EAAK,MAAM+M,GAAQ;AAAA,MACjB,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAAC9M,MAAY;AACrB,cAAMiB,IAAajB,EAAQ,KAAK,IAAI,CAAAqR,MAAKlT,GAAakT,GAAGjU,CAAe,CAAC,GACnEkU,IAASrB,GAAahP,CAAU;AAEtC,YAAI,CAACqQ,GAAQ;AACX,UAAAzR,EAAOM;AAAA,YAAqB;AAAA,YAC1B,IAAI,MAAM,kFAAkF;AAAA,UAAC,CAAC;AAChG;AAAA,QACF;AAEA,cAAMlC,IAAO,CAAA;AACb,mBAAWf,KAAO+D,GAAY;AAC5B,gBAAMsQ,IAASD,MAAW,aACtBf,GAA0BrT,CAAG,IAC7BmT,GAAuBnT,CAAG;AAC9B,UAAIqU,KAAQtT,EAAK,KAAKsT,CAAM;AAAA,QAC9B;AACA,QAAA3R,EAAQ,EAAE,QAAA0R,GAAQ,MAAArT,GAAM;AAAA,MAC1B;AAAA,MACA,OAAO,CAACiC,MAAUL,EAAOM,EAAqB,sBAAsBD,CAAK,CAAC;AAAA,IAChF,CAAK;AAAA,EACH,CAAC;AACH;ACrNO,SAASsR,GAAyBC,GAAS;AAChD,SAAO,IAAI,QAAQ,CAAC7R,MAAY;AAC9B,IAAAG,EAAK,MAAM0R,GAAS;AAAA,MAClB,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAACzR,MAAY;AACrB,cAAMQ,IAAS,oBAAI,IAAG;AACtB,mBAAWpC,KAAU4B,EAAQ,MAAM;AACjC,gBAAM9C,IAAMD,GAAmBmB,CAAM,GAC/BK,IAASvB,EAAIoB,CAAO,KAAK,OAAO,GAAGpB,EAAIoB,CAAO,CAAC,GAAG,KAAI,IAAK;AACjE,cAAI,CAACG,EAAQ;AACb,gBAAMG,IAAO,OAAO1B,EAAI2B,CAAI,CAAC,GACvBC,IAAK,OAAO5B,EAAI6B,CAAE,CAAC;AACzB,cAAI,CAAC,OAAO,SAASH,CAAI,KAAK,CAAC,OAAO,SAASE,CAAE,KAAKA,KAAMF,EAAM;AAClE,gBAAM4R,KAAO5R,IAAOE,KAAM,GAMpB,EAAE,CAAC4E,CAAG,GAAGgO,GAAM,CAAC/N,CAAO,GAAGgO,GAAK,GAAGC,EAAoB,IAAK1U,GAC3D8T,IAAQ;AAAA,YACZ,GAAGY;AAAA,YACH,CAACtT,CAAO,GAAGG;AAAA,YACX,CAACI,CAAI,GAAGD;AAAA,YACR,CAACG,CAAE,GAAGD;AAAA,YACN,CAAC2P,EAAG,GAAG+B;AAAA,YACP,CAAC/M,CAAK,GAAG+M;AAAA;AAAA,YACT,SAAS;AAAA,UACrB;AACU,UAAKhQ,EAAO,IAAI/B,CAAM,KAAG+B,EAAO,IAAI/B,GAAQ,EAAE,GAC9C+B,EAAO,IAAI/B,CAAM,EAAE,KAAKuS,CAAK;AAAA,QAC/B;AACA,cAAMjQ,IAAQ,MAAM,KAAKP,EAAO,SAAS,EAAE,IAAI,CAAC,CAAC/B,GAAQY,CAAM,OAAO;AAAA,UACpE,QAAAZ;AAAA,UACA,QAAQY,EAAO,KAAK,CAAC,GAAGD,MAAM,EAAEP,CAAI,IAAIO,EAAEP,CAAI,CAAC;AAAA,QACzD,EAAU;AACF,QAAAe,EAAQmB,CAAK;AAAA,MACf;AAAA,IACN,CAAK;AAAA,EACH,CAAC;AACH;AAQO,SAAS8Q,GAAoBJ,GAAS;AAC3C,SAAO,IAAI,QAAQ,CAAC7R,MAAY;AAC9B,IAAAG,EAAK,MAAM0R,GAAS;AAAA,MAClB,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,CAACzR,MAAY;AACrB,cAAMQ,IAAS,oBAAI,IAAG;AACtB,mBAAWpC,KAAU4B,EAAQ,MAAM;AACjC,gBAAM9C,IAAMD,GAAmBmB,CAAM,GAC/BK,KAAUvB,EAAIoB,CAAO,KAAK,IAAI,SAAQ,EAAG,KAAI;AACnD,cAAI,CAACG,EAAQ;AACb,gBAAMG,IAAO,OAAO1B,EAAI2B,CAAI,CAAC,GACvBC,IAAK,OAAO5B,EAAI6B,CAAE,CAAC;AACzB,cAAI,CAAC,OAAO,SAASH,CAAI,KAAK,CAAC,OAAO,SAASE,CAAE,KAAKA,KAAMF,EAAM;AAClE,gBAAM4R,KAAO5R,IAAOE,KAAM,GAEpB,EAAE,CAAC4E,CAAG,GAAGgO,GAAM,CAAC/N,CAAO,GAAGgO,GAAK,GAAGpS,EAAI,IAAKrC,GAC3C8T,IAAQ;AAAA,YACZ,GAAGzR;AAAA,YACH,CAACjB,CAAO,GAAGG;AAAA,YACX,CAACI,CAAI,GAAGD;AAAA,YACR,CAACG,CAAE,GAAGD;AAAA,YACN,CAAC2P,EAAG,GAAG+B;AAAA,YACP,CAAC/M,CAAK,GAAG+M;AAAA,YACT,SAAS;AAAA,UACrB;AACU,UAAKhQ,EAAO,IAAI/B,CAAM,KAAG+B,EAAO,IAAI/B,GAAQ,EAAE,GAC9C+B,EAAO,IAAI/B,CAAM,EAAE,KAAKuS,CAAK;AAAA,QAC/B;AACA,QAAApR,EAAQ;AAAA,UACN,OAAO,MAAM,KAAKY,EAAO,QAAO,CAAE,EAAE,IAAI,CAAC,CAAC/B,GAAQY,CAAM,OAAO;AAAA,YAC7D,QAAAZ;AAAA,YACA,QAAQY,EAAO,KAAK,CAACF,GAAGC,MAAMD,EAAEN,CAAI,IAAIO,EAAEP,CAAI,CAAC;AAAA,UAC3D,EAAY;AAAA,QACZ,CAAS;AAAA,MACH;AAAA,IACN,CAAK;AAAA,EACH,CAAC;AACH;AAwBO,eAAeiT,GAAoB,EAAE,UAAAC,GAAU,eAAAC,GAAe,YAAAC,EAAU,IAAK,CAAA,GAAI;AACtF,QAAM,CAACC,GAAYC,GAAiBC,CAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,IACpEL,IAAWP,GAAyBO,CAAQ,IAAI,QAAQ,QAAQ,CAAA,CAAE;AAAA,IAClEC,IACIZ,GAAmBY,CAAa,EAAE;AAAA,MAAK,CAAC,EAAE,MAAA/T,EAAI,MAC5CiT,GAAgBjT,EAAK,IAAI,CAACoT,OAAO,EAAE,GAAGA,GAAG,SAAS,aAAY,EAAG,CAAC;AAAA,IAC5E,IACQ,QAAQ,QAAQ,EAAE;AAAA,IACtBY,IAAaJ,GAAoBI,CAAU,EAAE,KAAK,CAAC,EAAE,OAAAlR,EAAK,MAAOA,CAAK,IAAI,QAAQ,QAAQ,CAAA,CAAE;AAAA,EAChG,CAAG,GAGKoQ,IAAO,IAAI,IAAIe,EAAW,IAAI,CAACxP,MAAM,CAACA,EAAE,QAAQ,EAAE,GAAGA,GAAG,QAAQ,CAAC,GAAGA,EAAE,MAAM,EAAC,CAAE,CAAC,CAAC;AACvF,aAAW2P,KAAM,CAAC,GAAGF,GAAiB,GAAGC,CAAY,GAAG;AACtD,UAAM1Q,IAAK2Q,EAAG;AACd,QAAK3Q;AACL,UAAIyP,EAAK,IAAIzP,CAAE,GAAG;AAChB,cAAM4Q,IAAWnB,EAAK,IAAIzP,CAAE;AAC5B,QAAAyP,EAAK,IAAIzP,GAAI,EAAE,GAAG4Q,GAAU,QAAQ,CAAC,GAAGA,EAAS,QAAQ,GAAID,EAAG,UAAU,CAAA,CAAG,EAAC,CAAE;AAAA,MAClF;AACE,QAAAlB,EAAK,IAAIzP,GAAI2Q,CAAE;AAAA,EAEnB;AAEA,SAAO,EAAE,OAAO,MAAM,KAAKlB,EAAK,OAAM,CAAE,EAAC;AAC3C;AClIO,SAASoB,GAAsBtT,GAAWuT,GAAYC,GAAUC,GAAWjJ,IAAU,IAAI;AAC9F,QAAMkJ,IAAUlJ,EAAQ,WAAW,QAC7BmJ,IAAQnJ,EAAQ,SAAS,MACzBoJ,IAAUpJ,EAAQ,WAAW;AAEnC,MAAI,CAACxK,KAAa,CAACA,EAAU,OAAQ,QAAO,CAAA;AAG5C,QAAMuB,IAAS,CAAA;AACf,aAAWtD,KAAO+B,GAAW;AAC3B,UAAMR,IAASvB,EAAI2V,CAAO;AAC1B,IAAIpU,KAAU,SACT+B,EAAO/B,CAAM,MAAG+B,EAAO/B,CAAM,IAAI,CAAA,IACtC+B,EAAO/B,CAAM,EAAE,KAAKvB,CAAG;AAAA,EACzB;AAEA,QAAM8C,IAAU,CAAA;AAEhB,aAAW,CAACvB,GAAQqU,CAAa,KAAK,OAAO,QAAQtS,CAAM,GAAG;AAK5D,UAAMuS,IAHS,CAAC,GAAGD,CAAa,EAAE,KAAK,CAAC3T,GAAGC,MAAM,OAAOD,EAAEwT,CAAO,CAAC,IAAI,OAAOvT,EAAEuT,CAAO,CAAC,CAAC,EAG9D,OAAO,CAACzV,MAAQ;AACxC,YAAM8V,IAAQ,OAAO9V,EAAIsV,CAAU,CAAC;AACpC,aAAO,OAAO,SAASQ,CAAK,KAAKA,KAASP;AAAA,IAC5C,CAAC;AAED,QAAI,CAACM,EAAW,OAAQ;AAGxB,UAAME,IAAO,CAAA;AACb,QAAIC,IAAa,CAAA,GACb1F,IAAS;AAEb,eAAWtQ,KAAO6V,GAAY;AAC5B,YAAMI,IAAI,OAAOjW,EAAIyV,CAAO,CAAC,GACvBS,IAAI,OAAOlW,EAAI0V,CAAK,CAAC;AAE3B,MAAIpF,MAAW,QAAQ,KAAK,IAAI2F,IAAI3F,CAAM,IAAI,QACxC0F,EAAW,UAAQD,EAAK,KAAKC,CAAU,GAC3CA,IAAa,CAAChW,CAAG,KAEjBgW,EAAW,KAAKhW,CAAG,GAErBsQ,IAAS4F;AAAA,IACX;AACA,IAAIF,EAAW,UAAQD,EAAK,KAAKC,CAAU;AAE3C,eAAWG,KAAOJ,GAAM;AACtB,YAAMK,IAAY,OAAOD,EAAI,CAAC,EAAEV,CAAO,CAAC,GAClCY,IAAU,OAAOF,EAAIA,EAAI,SAAS,CAAC,EAAET,CAAK,CAAC,GAC3CY,IAAcD,IAAUD;AAE9B,UAAIE,IAAcd,EAAW;AAE7B,UAAIe,IAAc,GACdC,IAAc;AAClB,iBAAWxW,KAAOmW,GAAK;AACrB,cAAML,IAAQ,OAAO9V,EAAIsV,CAAU,CAAC,GAC9BmB,IAAM,OAAOzW,EAAI0V,CAAK,CAAC,IAAI,OAAO1V,EAAIyV,CAAO,CAAC;AACpD,QAAAc,KAAeT,IAAQW,GACvBD,KAAeC;AAAA,MACjB;AACA,YAAMC,IAAWH,IAAcC,GACzBG,IAAWR,EAAI,QACfjG,IAAQ,GAAGoG,EAAY,QAAQ,CAAC,CAAC,QAAQI,EAAS,QAAQ,CAAC,CAAC,IAAIpB,CAAU;AAEhF,MAAAxS,EAAQ,KAAK;AAAA,QACX,CAAC6S,CAAO,GAAGpU;AAAA,QACX,aAAa+T;AAAA,QACb,CAACG,CAAO,GAAGW;AAAA,QACX,CAACV,CAAK,GAAGW;AAAA,QACT,QAAQC;AAAA,QACR,WAAWI;AAAA,QACX,WAAWC;AAAA,QACX,OAAAzG;AAAA,MACR,CAAO;AAAA,IACH;AAAA,EACF;AAEA,SAAOpN;AACT;ACjGA,MAAM8T,KAAgB,CAAC,SAAS,MAAM,kBAAkB,QAAQ,GAAG;AAanE,SAASC,GAAa7W,GAAK;AAEzB,MAAIA,EAAIuG,CAAK,MAAM,QAAW;AAC5B,UAAMnD,IAAI,OAAOpD,EAAIuG,CAAK,CAAC;AAC3B,QAAI,OAAO,SAASnD,CAAC,EAAG,QAAOA;AAAA,EACjC;AACA,aAAW0T,KAASF;AAClB,QAAI5W,EAAI8W,CAAK,MAAM,QAAW;AAC5B,YAAM1T,IAAI,OAAOpD,EAAI8W,CAAK,CAAC;AAC3B,UAAI,OAAO,SAAS1T,CAAC,EAAG,QAAOA;AAAA,IACjC;AAGF,QAAM6S,IAAI,OAAOjW,EAAI2B,CAAI,CAAC,GACpB,IAAI,OAAO3B,EAAI6B,CAAE,CAAC;AACxB,SAAI,OAAO,SAASoU,CAAC,KAAK,OAAO,SAAS,CAAC,KAAK,KAAKA,KAAWA,IAAI,KAAK,IAClE;AACT;AAcA,SAASc,GAAmBhW,GAAM;AAChC,MAAIA,EAAK,WAAW,EAAG,QAAO,CAAA;AAC9B,QAAMiW,IAAU,oBAAI,IAAI,CAAC5V,GAASmF,GAAO5E,GAAME,GAAI0P,IAAK,GAAGqF,EAAa,CAAC;AAGzE,SAFmB,OAAO,KAAK7V,EAAK,CAAC,CAAC,EAAE,OAAO,CAAC0G,MAAM,CAACuP,EAAQ,IAAIvP,CAAC,CAAC,EAEnD,OAAO,CAAC/G,MAAQ;AAChC,QAAIuW,IAAe;AACnB,eAAWjX,KAAOe,GAAM;AACtB,YAAMqC,IAAI,OAAOpD,EAAIU,CAAG,CAAC;AACzB,MAAI,OAAO,SAAS0C,CAAC,KAAG6T;AAAA,IAC1B;AACA,WAAOA,IAAelW,EAAK,SAAS;AAAA,EACtC,CAAC;AACH;AAuCO,SAASmW,GAAmB3C,GAAStU,IAAY,MAAM;AAO5D,QAAMkX,IANStU,EAAK,MAAM0R,GAAS;AAAA,IACjC,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACnB,CAAG,EAEsB,QAAQ,CAAA;AAC/B,MAAI4C,EAAQ,WAAW,EAAG,QAAO,CAAA;AAEjC,QAAMpW,IAAOoW,EAAQ,IAAI,CAAChD,MAAMpU,GAAmBoU,GAAG,MAAMlU,CAAS,CAAC,GAGhEmX,IAAYL,GAAmBhW,CAAI,GAGnCuC,IAAS,oBAAI,IAAG;AACtB,aAAWtD,KAAOe,GAAM;AACtB,UAAMQ,IAASvB,EAAIoB,CAAO,KAAK,OAAO,GAAGpB,EAAIoB,CAAO,CAAC,GAAG,KAAI,IAAK;AACjE,QAAI,CAACG,EAAQ;AAEb,UAAM6R,IAAQyD,GAAa7W,CAAG;AAC9B,IAAIoT,MAAU,QAAQA,IAAQ,MAEzB9P,EAAO,IAAI/B,CAAM,KAAG+B,EAAO,IAAI/B,GAAQ,EAAE,GAC9C+B,EAAO,IAAI/B,CAAM,EAAE,KAAK,EAAE,OAAA6R,GAAO,KAAApT,GAAK;AAAA,EACxC;AAEA,QAAM6D,IAAQ,CAAA;AACd,aAAW,CAACtC,GAAQ8V,CAAO,KAAK/T,GAAQ;AAEtC,IAAA+T,EAAQ,KAAK,CAACpV,GAAGC,MAAMD,EAAE,QAAQC,EAAE,KAAK;AAExC,UAAMoV,IAAW,CAAA;AACjB,eAAW5W,KAAO0W,GAAW;AAC3B,YAAMG,IAAS,CAAA,GACTC,IAAS,CAAA;AACf,iBAAW,EAAE,OAAApE,GAAO,KAAApT,EAAG,KAAMqX,GAAS;AACpC,cAAM,IAAI,OAAOrX,EAAIU,CAAG,CAAC;AACzB,QAAI,OAAO,SAAS,CAAC,KAAK,MAAM+W,OAC9BF,EAAO,KAAKnE,CAAK,GACjBoE,EAAO,KAAK,CAAC;AAAA,MAEjB;AACA,MAAID,EAAO,UAAU,MACnBD,EAAS5W,CAAG,IAAI,EAAE,QAAA6W,GAAQ,QAAAC,EAAM;AAAA,IAEpC;AAEA,IAAI,OAAO,KAAKF,CAAQ,EAAE,SAAS,KACjCzT,EAAM,KAAK,EAAE,QAAAtC,GAAQ,UAAA+V,EAAQ,CAAE;AAAA,EAEnC;AAEA,SAAOzT;AACT;AAoBO,SAAS6T,GAAsBC,GAAiBC,IAAU,MAAMrL,IAAU,CAAA,GAAI;AACnF,QAAMsL,IAAY,CAAA;AAClB,aAAW,EAAE,QAAAtW,GAAQ,UAAA+V,EAAQ,KAAMK,GAAiB;AAClD,UAAM/W,IAAMgX,KAAWN,EAASM,CAAO,IAAIA,IAAU,OAAO,KAAKN,CAAQ,EAAE,CAAC;AAC5E,QAAI,CAAC1W,EAAK;AACV,UAAM,EAAE,QAAA2W,GAAQ,QAAAC,MAAWF,EAAS1W,CAAG;AACvC,IAAAiX,EAAU,KAAK,EAAE,QAAAtW,GAAQ,QAAAgW,GAAQ,QAAAC,GAAQ,SAAAjL,GAAS;AAAA,EACpD;AACA,SAAOsL;AACT;AChLA,SAASC,GAAkBC,GAAM;AAC/B,QAAMC,IAASD,EAAK,QAAQ,GAAG;AAC/B,MAAIC,IAAS,EAAG,QAAO;AAEvB,QAAMC,IAAOF,EAAK,MAAM,GAAGC,CAAM,EAAE,KAAI,EAAG,YAAW,GAC/CE,IAAWH,EAAK,MAAMC,IAAS,CAAC,GAGhCG,IAAYD,EAAS,MAAM,gBAAgB,GAC3CE,IAAOD,IAAYA,EAAU,CAAC,IAAI,IAClCE,IAAeF,IAAYA,EAAU,CAAC,IAAID,GAG1CI,IAAYD,EAAa,YAAY,GAAG,GACxC1X,IAAQ2X,KAAa,IAAID,EAAa,MAAM,GAAGC,CAAS,EAAE,SAASD,EAAa,KAAI,GACpFvG,IAAcwG,KAAa,IAAID,EAAa,MAAMC,IAAY,CAAC,EAAE,KAAI,IAAK;AAEhF,SAAO,EAAE,MAAAL,GAAM,MAAAG,GAAM,OAAAzX,GAAO,aAAAmR,EAAW;AACzC;AASA,SAASyG,GAAcC,GAAS;AAC9B,QAAMC,IAAW,CAAA;AACjB,MAAIC,IAAU;AAEd,aAAWC,KAAOH,EAAQ,MAAM,OAAO,GAAG;AACxC,UAAMT,IAAOY,EAAI,QAAO;AACxB,IAAIZ,EAAK,WAAW,GAAG,KAGrBW,IAAU,EAAE,MADCX,EAAK,MAAM,GAAG,CAAC,EAAE,YAAW,GACvB,OAAO,GAAE,GAC3BU,EAAS,KAAKC,CAAO,KACZA,KACTA,EAAQ,MAAM,KAAKX,CAAI;AAAA,EAE3B;AAEA,SAAOU;AACT;AASA,SAASG,GAAmBC,IAAQ,IAAI;AACtC,QAAMC,IAAM,CAAA;AACZ,aAAWf,KAAQc,GAAO;AACxB,UAAME,IAAUhB,EAAK,UAAS;AAC9B,QAAI,CAACgB,KAAWA,EAAQ,WAAW,GAAG,EAAG;AACzC,UAAM1E,IAASyD,GAAkBC,CAAI;AACrC,IAAI1D,MAAQyE,EAAIzE,EAAO,IAAI,IAAIA;AAAA,EACjC;AACA,SAAOyE;AACT;AAkDO,SAASE,GAAaR,GAASjM,IAAU,IAAI;;AAClD,QAAMkM,IAAWF,GAAcC,CAAO,GAEhCS,IAAiBR,EAAS,KAAK,CAACvQ,MAAMA,EAAE,SAAS,GAAG,GACpDgR,IAAcT,EAAS,KAAK,CAACvQ,MAAMA,EAAE,SAAS,GAAG,GACjDiR,IAAeV,EAAS,KAAK,CAACvQ,MAAMA,EAAE,SAAS,GAAG,GAClDkR,IAAcX,EAAS,KAAK,CAACvQ,MAAMA,EAAE,SAAS,GAAG;AAEvD,MAAI,CAACkR,EAAa,QAAO,CAAA;AAEzB,QAAMC,IAAUT,GAAmBK,KAAA,gBAAAA,EAAgB,KAAK,GAClDK,IAAOV,GAAmBM,KAAA,gBAAAA,EAAa,KAAK,GAC5CK,IAAe,CAAA;AACrB,aAAWxB,MAASoB,KAAA,gBAAAA,EAAc,UAAS,CAAA,GAAK;AAC9C,UAAMJ,IAAUhB,EAAK,UAAS;AAC9B,QAAI,CAACgB,KAAWA,EAAQ,WAAW,GAAG,EAAG;AACzC,UAAM1E,IAASyD,GAAkBC,CAAI;AACrC,IAAI1D,KAAQkF,EAAa,KAAKlF,CAAM;AAAA,EACtC;AAIA,KADa1M,KAAApF,IAAA8W,EAAQ,SAAR,gBAAA9W,EAAc,UAAd,gBAAAoF,EAAqB,OAAO,kBAAiB,UAC7C,SACX,QAAQ;AAAA,IACN;AAAA,EACN;AAIE,QAAMpG,IACJgL,EAAQ,aACPzE,KAAAD,IAAAyR,EAAK,SAAL,gBAAAzR,EAAW,UAAX,gBAAAC,EAAkB,WAAU,YAC5B0R,KAAAC,IAAAH,EAAK,QAAL,gBAAAG,EAAU,UAAV,gBAAAD,EAAiB,WAAU,YAC3BE,KAAAC,IAAAL,EAAK,QAAL,gBAAAK,EAAU,UAAV,gBAAAD,EAAiB,WAAU,SAC5B;AAGF,MAAIE,IAAenC;AACnB,OAAIoC,IAAAP,EAAK,SAAL,QAAAO,EAAW,OAAO;AACpB,UAAMzW,IAAI,OAAOkW,EAAK,KAAK,KAAK;AAChC,IAAI,OAAO,SAASlW,CAAC,MAAGwW,IAAexW;AAAA,EACzC;AAKA,MAJImJ,EAAQ,iBAAiB,WAC3BqN,IAAerN,EAAQ,eAGrBgN,EAAa,WAAW,EAAG,QAAO,CAAA;AAGtC,QAAMO,IAAcP,EAAa,MAAM,CAAC,GAGlCQ,IAAQ,CAAA;AACd,aAAW,EAAE,MAAA9B,GAAM,MAAAG,EAAI,KAAM0B;AAC3B,IAAAC,EAAM9B,EAAK,YAAW,CAAE,IAAIG;AAI9B,QAAM4B,IAAc,CAAA;AACpB,aAAW,EAAE,MAAA/B,EAAI,KAAM6B;AACrB,IAAAE,EAAY/B,EAAK,aAAa,IAAI,EAAE,QAAQ,CAAA,GAAI,QAAQ,GAAE;AAG5D,aAAWF,KAAQqB,EAAY,OAAO;AACpC,UAAML,IAAUhB,EAAK,UAAS;AAC9B,QAAI,CAACgB,KAAWA,EAAQ,WAAW,GAAG,EAAG;AAEzC,UAAMkB,IAASlB,EAAQ,MAAM,KAAK;AAClC,QAAIkB,EAAO,SAAS,EAAG;AAEvB,UAAM7G,IAAQ,OAAO6G,EAAO,CAAC,CAAC;AAC9B,QAAI,GAAC,OAAO,SAAS7G,CAAK,KAAKA,MAAUwG;AAEzC,eAAS9Q,IAAI,GAAGA,IAAIgR,EAAY,QAAQhR,KAAK;AAC3C,cAAMlI,IAAMkZ,EAAYhR,CAAC,EAAE,KAAK,YAAW,GACrC1F,IAAI,OAAO6W,EAAOnR,IAAI,CAAC,CAAC;AAC9B,QAAI,OAAO,SAAS1F,CAAC,KAAKA,MAAMwW,MAC9BI,EAAYpZ,CAAG,EAAE,OAAO,KAAKwS,CAAK,GAClC4G,EAAYpZ,CAAG,EAAE,OAAO,KAAKwC,CAAC;AAAA,MAElC;AAAA,EACF;AAGA,QAAMkU,IAAW,CAAA;AACjB,aAAW,CAAC1W,GAAKsZ,CAAI,KAAK,OAAO,QAAQF,CAAW;AAClD,IAAIE,EAAK,OAAO,UAAU,MAAG5C,EAAS1W,CAAG,IAAIsZ;AAG/C,SAAI,OAAO,KAAK5C,CAAQ,EAAE,WAAW,IAAU,CAAA,IAExC,CAAC,EAAE,QAAA/V,GAAQ,OAAAwY,GAAO,UAAAzC,EAAQ,CAAE;AACrC;AC5MA,MAAM6C,KAAkB;AAAA,EACtB;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5C;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAC9C,GAEMC,KAA0B,EAAE,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAE,GACpDC,KAA+B,IAC/BC,KAAgC,IAChCC,KAAgC;AAEtC,SAASC,GAA4BC,IAAS,IAAI;AAChD,SAAO;AAAA,IACL,GAAGA;AAAA,IACH,QAAQL;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,MACL,GAAIK,EAAO,SAAS;MACpB,UAAU;AAAA,QACR,GAAKA,EAAO,SAASA,EAAO,MAAM,YAAa;QAC/C,MAAMJ;AAAA,MACd;AAAA,MACM,OAAO;AAAA,QACL,GAAKI,EAAO,SAASA,EAAO,MAAM,SAAU;QAC5C,MAAM;AAAA,UACJ,GAAMA,EAAO,SAASA,EAAO,MAAM,SAASA,EAAO,MAAM,MAAM,QAAS,CAAA;AAAA,UACxE,MAAMH;AAAA,QAChB;AAAA,QACQ,WAAWG,EAAO,SAASA,EAAO,MAAM,SAASA,EAAO,MAAM,MAAM,aAC/DF;AAAA,MACb;AAAA,IACA;AAAA,IACI,OAAO;AAAA,MACL,GAAIE,EAAO,SAAS;MACpB,YAAY;AAAA,MACZ,UAAU;AAAA,QACR,GAAKA,EAAO,SAASA,EAAO,MAAM,YAAa;QAC/C,MAAMJ;AAAA,MACd;AAAA,MACM,OAAO;AAAA,QACL,GAAKI,EAAO,SAASA,EAAO,MAAM,SAAU;QAC5C,MAAM;AAAA,UACJ,GAAMA,EAAO,SAASA,EAAO,MAAM,SAASA,EAAO,MAAM,MAAM,QAAS,CAAA;AAAA,UACxE,MAAMH;AAAA,QAChB;AAAA,MACA;AAAA,IACA;AAAA,EACA;AACA;AAmBO,SAASI,GAAmBvY,GAAQ;AAAA,EACzC,WAAAwY,IAAY;AAAA,EACZ,SAAAC,IAAU;AAAA,EACV,SAAAC,IAAUV;AAAA,EACV,UAAAW,IAAWvU;AAAA,EACX,QAAAwU,IAASvU;AAAA,EACT,OAAAwU,IAAQvU;AAAA,EACR,UAAAwU,IAAW;AACb,IAAI,IAAI;AACN,QAAMzH,IAAQrR,EAAO;AAAA,IAAO,OAC1B,EAAE2Y,CAAQ,KAAK,QAAQ,EAAEC,CAAM,KAAK,QAAQ,EAAEC,CAAK,KAAK;AAAA,EAC5D;AAEE,MAAI,CAACxH,EAAM;AACT,WAAO,EAAE,MAAM,IAAI,QAAQ,CAAA,EAAE;AAI/B,QAAM0H,IAAW,CAAA;AACjB,EAAIN,KACiB,CAAC,GAAG,IAAI,IAAIpH,EAAM,IAAI,CAAApJ,MAAKA,EAAEwQ,CAAO,CAAC,EAAE,OAAO,OAAK,KAAK,IAAI,CAAC,CAAC,EAAE,KAAI,EAC5E,QAAQ,CAACO,GAAKrS,MAAM;AAAE,IAAAoS,EAASC,CAAG,IAAIN,EAAQ/R,IAAI+R,EAAQ,MAAM;AAAA,EAAG,CAAC;AAIjF,QAAMO,IAAQ,oBAAI,IAAG,GACfC,IAAS,CAAA;AAEf,aAAW,KAAK7H,GAAO;AACrB,UAAMJ,IAAQ,OAAO,EAAE0H,CAAQ,CAAC,GAC1B5T,IAAM,OAAO,EAAE6T,CAAM,CAAC,GACtBpH,IAAK,OAAO,EAAEqH,CAAK,CAAC,GACpBG,IAAMP,IAAW,EAAEA,CAAO,KAAK,aAAc,YAC7CU,IAAQV,IAAWM,EAASC,CAAG,KAAK,YAAa;AAEvD,IAAKC,EAAM,IAAID,CAAG,KAChBC,EAAM,IAAID,GAAK,EAAE,IAAI,CAAA,GAAI,IAAI,CAAA,GAAI,MAAM,CAAA,GAAI,KAAK,CAAA,GAAI,OAAAG,EAAK,CAAE;AAE7D,UAAMC,IAAQH,EAAM,IAAID,CAAG;AAE3B,IAAAI,EAAM,GAAG,KAAKrU,CAAG,GACjBqU,EAAM,GAAG,KAAKnI,CAAK,GACnBmI,EAAM,KAAK,KAAKrU,CAAG,GACnBqU,EAAM,IAAI,KAAK5H,CAAE;AAIjB,UAAMzI,IAASyI,IAAK,KAAK,KAAM,KACzB6H,IAASb,KAAa,KAAK,IAAIzT,CAAG,IAAI,KACtC8C,IAAK,KAAK,IAAIkB,CAAK,IAAIsQ,GACvBvR,IAAK,KAAK,IAAIiB,CAAK,IAAIsQ;AAE7B,IAAAH,EAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,IAAInU;AAAA,MAAK,IAAIkM;AAAA,MACb,IAAIlM,IAAM8C;AAAA,MAAI,IAAIoJ,IAAQnJ;AAAA,MAC1B,MAAM,EAAE,OAAAqR,GAAO,OAAO,EAAC;AAAA,IAC7B,CAAK;AAAA,EACH;AAEA,QAAMpB,IAAO,CAAA,GACPuB,IAAab,KAAWQ,EAAM,OAAO;AAE3C,aAAW,CAACD,GAAKI,CAAK,KAAKH,EAAM,QAAO;AACtC,IAAAlB,EAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,GAAGqB,EAAM;AAAA,MACT,GAAGA,EAAM;AAAA,MACT,MAAM;AAAA,MACN,MAAMJ,MAAQ,aAAa,OAAOA,CAAG,IAAI;AAAA,MACzC,QAAQ,EAAE,MAAM,GAAG,OAAOI,EAAM,MAAK;AAAA,MACrC,YAAYE,KAAcN,MAAQ;AAAA,MAClC,YAAYI,EAAM,KAAK,IAAI,CAAClR,GAAGvB,MAAM,CAACuB,GAAGkR,EAAM,IAAIzS,CAAC,CAAC,CAAC;AAAA,MACtD,eAAe;AAAA,IACrB,CAAK;AAmBH,SAAO,EAAE,MAAAoR,GAAM,QAhBA;AAAA,IACb,QAAAmB;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAE;AAAA,IACpC,OAAO;AAAA,MACL,OAAO;AAAA,MACP,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU,CAAC,KAAK,KAAK,KAAK,GAAG,IAAI,IAAI,EAAE;AAAA,IAC7C;AAAA,IACI,OAAO,EAAE,OAAO,aAAa,WAAW,WAAU;AAAA,IAClD,YAAY,CAAC,CAACI;AAAA,IACd,UAAUR,MAAa,SAAYA,IAAWS;AAAA,EAClD,EAEuB;AACvB;AAcO,SAASC,GAA2B5Z,GAAW;AAAA,EACpD,UAAA6Z,IAAW;AAAA,EACX,SAAAf,IAAUV;AAAA,EACV,SAAA1E,IAAU9T;AAAA,EACV,OAAA+T,IAAQ7T;AAAA,EACR,UAAAoZ,IAAW;AACb,IAAI,IAAI;AACN,QAAMY,IAAU9Z,EACb,OAAO,CAAAK,MAAMA,EAAGqT,CAAO,KAAK,QAAQrT,EAAGsT,CAAK,KAAK,QAAQ,OAAOtT,EAAGsT,CAAK,CAAC,IAAI,OAAOtT,EAAGqT,CAAO,CAAC,CAAC,EAChG,OAAO,CAAArT,MAAM;AACZ,UAAM0Z,IAAK1Z,EAAGwZ,CAAQ;AACtB,QAAIE,KAAM,KAAM,QAAO;AACvB,UAAM5T,IAAI,OAAO4T,CAAE,EAAE,KAAI;AACzB,WAAO5T,MAAM,MAAM,CAAC,qBAAqB,KAAKA,CAAC;AAAA,EACjD,CAAC,EACA,IAAI,CAAA9F,OAAO,EAAE,MAAM,OAAOA,EAAGqT,CAAO,CAAC,GAAG,IAAI,OAAOrT,EAAGsT,CAAK,CAAC,GAAG,OAAO,OAAOtT,EAAGwZ,CAAQ,CAAC,EAAE,KAAI,IAAK,EACpG,KAAK,CAAC3Z,GAAGC,MAAMD,EAAE,OAAOC,EAAE,IAAI;AAEjC,MAAI,CAAC2Z,EAAQ;AACX,WAAO,EAAE,MAAM,IAAI,QAAQ,CAAA,EAAE;AAG/B,QAAMR,IAAS,CAAA,GACTU,IAAQ,CAAA,GACRC,IAAQ,CAAA;AAEd,SAAAH,EAAQ,QAAQ,CAACI,GAAKtX,MAAQ;AAC5B,IAAA0W,EAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MAAK,MAAM;AAAA,MACjB,IAAI;AAAA,MAAG,IAAI;AAAA,MACX,IAAIY,EAAI;AAAA,MAAM,IAAIA,EAAI;AAAA,MACtB,WAAWpB,EAAQlW,IAAMkW,EAAQ,MAAM;AAAA,MACvC,MAAM,EAAE,OAAO,EAAC;AAAA,MAChB,OAAO;AAAA,IACb,CAAK,GACDkB,EAAM,KAAK,OAAOE,EAAI,OAAOA,EAAI,GAAG,GACpCD,EAAM,KAAKC,EAAI,KAAK;AAAA,EACtB,CAAC,GAsBM,EAAE,MApBI,CAAC;AAAA,IACZ,MAAM;AAAA,IACN,GAAG,MAAMD,EAAM,MAAM,EAAE,KAAK,GAAG;AAAA,IAC/B,GAAGD;AAAA,IACH,MAAM;AAAA,IACN,MAAMC;AAAA,IACN,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,EACf,CAAG,GAWc,QAAQxB,GATR;AAAA,IACb,QAAAa;AAAA,IACA,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,IAAO,YAAY,GAAI;AAAA,IACxD,OAAO,EAAE,OAAO,aAAa,WAAW,WAAU;AAAA,IAClD,YAAY;AAAA,IACZ,UAAUJ,MAAa,SAAYA,IAAWS;AAAA,EAClD,CAE2D,EAAC;AAC5D;AASA,SAASQ,GAAYC,GAAMC,GAAc;AACvC,MAAI,CAACD,EAAM,QAAO;AAClB,QAAME,IAAQ,OAAOF,CAAI,EAAE,KAAI,EAAG,MAAM,KAAK,GACvCtD,IAAQ,CAAA;AACd,MAAIH,IAAU;AACd,aAAW4D,KAAQD;AACjB,IAAI3D,KAAWA,EAAQ,SAAS,IAAI4D,EAAK,SAASF,KAChDvD,EAAM,KAAKH,CAAO,GAClBA,IAAU4D,KAEV5D,IAAUA,IAAU,GAAGA,CAAO,IAAI4D,CAAI,KAAKA;AAG/C,SAAI5D,KAASG,EAAM,KAAKH,CAAO,GACxBG,EAAM,KAAK,MAAM;AAC1B;AAqBO,SAAS0D,GAAoBxa,GAAW;AAAA,EAC7C,YAAAya,IAAa;AAAA,EACb,SAAA/G,IAAU9T;AAAA,EACV,OAAA+T,IAAQ7T;AAAA,EACR,SAAA4a,IAAU;AAAA,EACV,aAAAC,IAAc;AAAA,EACd,WAAAC,IAAY;AAAA,EACZ,cAAAP,IAAe;AAAA,EACf,UAAAnB,IAAW;AACb,IAAI,IAAI;AACN,QAAMY,IAAU9Z,EACb,OAAO,CAAAK,MAAMA,EAAGqT,CAAO,KAAK,QAAQrT,EAAGsT,CAAK,KAAK,QAAQ,OAAOtT,EAAGsT,CAAK,CAAC,IAAI,OAAOtT,EAAGqT,CAAO,CAAC,CAAC,EAChG,IAAI,CAAArT,MAAM;AACT,UAAMuW,IAAMvW,EAAGoa,CAAU,GACnBI,IAAWjE,KAAO,QAAQ,OAAOA,CAAG,EAAE,KAAI,MAAO,MAAM,OAAOA,CAAG,MAAM,SACzE,OAAOA,CAAG,EAAE,KAAI,IAChB;AACJ,WAAO,EAAE,MAAM,OAAOvW,EAAGqT,CAAO,CAAC,GAAG,IAAI,OAAOrT,EAAGsT,CAAK,CAAC,GAAG,SAAAkH,EAAO;AAAA,EACpE,CAAC,EACA,KAAK,CAAC3a,GAAGC,MAAMD,EAAE,OAAOC,EAAE,IAAI;AAEjC,MAAI,CAAC2Z,EAAQ;AACX,WAAO,EAAE,MAAM,IAAI,QAAQ,CAAA,EAAE;AAG/B,QAAMR,IAAS,CAAA,GACTwB,IAAS,CAAA,GACTC,IAAS,CAAA,GACTd,IAAQ,CAAA,GACRe,IAAS,CAAA;AAEf,aAAWd,KAAOJ,GAAS;AACzB,UAAMvI,IAAM,OAAO2I,EAAI,OAAOA,EAAI,KAC5Be,IAAa,CAAC,CAACf,EAAI;AAEzB,IAAAZ,EAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MAAK,MAAM;AAAA,MACjB,IAAI;AAAA,MAAG,IAAI;AAAA,MACX,IAAIY,EAAI;AAAA,MAAM,IAAIA,EAAI;AAAA,MACtB,WAAWe,IAAaP,IAAU;AAAA,MAClC,MAAM,EAAE,OAAOC,GAAa,OAAO,EAAC;AAAA,MACpC,OAAO;AAAA,IACb,CAAK,GAEGM,MACFH,EAAO,KAAK,GAAG,GACfC,EAAO,KAAKxJ,CAAG,GACf0I,EAAM,KAAKE,GAAYD,EAAI,SAASG,CAAY,CAAC,GACjDW,EAAO,KAAK,GAAGd,EAAI,KAAK,QAAQ,CAAC,CAAC,IAAIA,EAAI,GAAG,QAAQ,CAAC,CAAC,SAASC,GAAYD,EAAI,SAAS,EAAE,CAAC,EAAE;AAAA,EAElG;AAwBA,SAAO,EAAE,MAtBIY,EAAO,SAAS,CAAC;AAAA,IAC5B,MAAM;AAAA,IACN,GAAGA;AAAA,IACH,GAAGC;AAAA,IACH,MAAM;AAAA,IACN,MAAMd;AAAA,IACN,cAAc;AAAA,IACd,UAAU,EAAE,OAAOW,GAAW,MAAM,GAAE;AAAA,IACtC,WAAWI;AAAA,IACX,WAAW;AAAA,IACX,YAAY;AAAA,EAChB,CAAG,IAAI,CAAA,GAWU,QAAQvC,GATR;AAAA,IACb,QAAAa;AAAA,IACA,QAAQ;AAAA,IACR,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,IAAO,YAAY,GAAI;AAAA,IACxD,OAAO,EAAE,OAAO,aAAa,WAAW,WAAU;AAAA,IAClD,YAAY;AAAA,IACZ,UAAUJ,MAAa,SAAYA,IAAWS;AAAA,EAClD,CAE2D,EAAC;AAC5D;AAgBO,SAASuB,GAAqBnJ,GAAO;AAAA,EAC1C,YAAAoJ,IAAa;AAAA,EACb,MAAAC,IAAO;AAAA,EACP,MAAAC,IAAO;AACT,IAAI,IAAI;AACN,QAAMrQ,IAAI+G,EAAMqJ,CAAI,KAAK,OAAO,OAAOrJ,EAAMqJ,CAAI,CAAC,IAAI,MAChDnQ,IAAI8G,EAAMsJ,CAAI,KAAK,OAAO,OAAOtJ,EAAMsJ,CAAI,CAAC,IAAI,MAChDlW,IAAM4M,EAAMtN,CAAG,KAAK,OAAO,OAAOsN,EAAMtN,CAAG,CAAC,IAAI,MAChDmN,IAAKG,EAAMrN,CAAO,KAAK,OAAO,OAAOqN,EAAMrN,CAAO,CAAC,IAAI;AAE7D,MAAIsG,MAAM,QAAQC,MAAM,QAAQ9F,MAAQ,QAAQyM,MAAO,KAAM,QAAO;AAEpE,QAAM0J,KAAW1J,IAAK,KAAM,OAAO,KAC7B2J,IAAaD,IAAS,KAAK,KAAM,KACjCnS,IAASyI,IAAK,KAAK,KAAM,KAGzB4J,IAAML,IAAa,KAAK,IAAII,CAAS,GACrCE,IAAMN,IAAa,KAAK,IAAII,CAAS,GAGrCG,IAAUP,IAAa,OAAOhW,IAAM,KACpCwW,IAAMD,IAAU,KAAK,IAAIvS,CAAK,GAC9ByS,IAAMF,IAAU,KAAK,IAAIvS,CAAK;AAEpC,SAAO;AAAA,IACL,QAAAmS;AAAA,IACA,UAAUnW;AAAA,IACV,GAAA6F;AAAA,IACA,GAAAC;AAAA,IACA,UAAUD,IAAIwQ;AAAA,IACd,UAAUvQ,IAAIwQ;AAAA,IACd,UAAUzQ,IAAIwQ;AAAA,IACd,UAAUvQ,IAAIwQ;AAAA,IACd,QAAQzQ,IAAI2Q;AAAA,IACZ,QAAQ1Q,IAAI2Q;AAAA,EAChB;AACA;ACjZO,SAASC,GAAqB;AAAA,EACnC,QAAArc;AAAA,EACA,MAAAoC;AAAA,EACA,aAAAka;AAAA,EACA,UAAAhZ;AAAA,EACA,iBAAAiZ;AAAA,EACA,aAAAC;AAAA,EACA,QAAA5b;AAAA,EACA,aAAA6b;AACF,GAAG;AACD,MAAIA;AACF,WAAO,EAAE,MAAM,eAAe,MAAM,eAAeA,CAAW,GAAE;AAElE,QAAMna,IAAQga,KAAe,CAAA;AAC7B,MAAI,CAACtc;AACH,WAAIsC,EAAM,WAAW,IACZ,EAAE,MAAM,eAAe,MAAM,kBAAiB,IAEhD,EAAE,MAAM,eAAe,MAAM,gBAAe;AAErD,MAAI,CAACF;AACH,WAAO,EAAE,MAAM,eAAe,MAAM,WAAWpC,CAAM,IAAG;AAE1D,QAAM0c,IAAQH,KAAmB,CAAA;AACjC,SAAKjZ,IASD,EAHckZ,MAAgBG,OAGhB,EAFAH,MAAgBI,QACtBhc,KAAU,CAAA,GACc,WAAW,IACtC,EAAE,MAAM,eAAe,MAAM,iBAAiB0C,CAAQ,YAAYtD,CAAM,GAAE,IAE5E,EAAE,MAAM,QAAO,IAXhB0c,EAAM,WAAW,IACZ,EAAE,MAAM,eAAe,MAAM,oCAAoC1c,CAAM,GAAE,IAE3E,EAAE,MAAM,eAAe,MAAM,oBAAmB;AAS3D;AAUO,SAAS6c,GAAiBC,GAAYC,GAAS;AACpD,MAAI,OAAOA,KAAY,WAAY,QAAOA,EAAQD,CAAU;AAC5D,MAAI,SAAOC,KAAY,YAAY,CAACA,MAChC,EAAAD,KAAc,QAAQ,OAAOA,KAAe;AAChD,WAAOA,EAAWC,CAAO;AAC3B;AAUO,SAASC,GAAqBV,GAAaS,GAAS;AACzD,QAAMxR,IAAM,CAAA,GACN0R,IAAO,oBAAI,IAAG;AACpB,aAAWhZ,KAAKqY,KAAe,IAAI;AACjC,UAAMY,IAAIL,GAAiB5Y,GAAG8Y,CAAO;AACrC,IAAIG,KAAK,QAAQA,MAAM,MACnBD,EAAK,IAAIC,CAAC,MACdD,EAAK,IAAIC,CAAC,GACV3R,EAAI,KAAK2R,CAAC;AAAA,EACZ;AACA,SAAO3R;AACT;AAWO,SAAS4R,GAAmBb,GAAaS,GAASK,GAAY;AACnE,SAAIA,KAAc,QAAQA,MAAe,KAAWd,KAAe,CAAA,KAC3DA,KAAe,CAAA,GAAI,OAAO,CAACrY,MAAM4Y,GAAiB5Y,GAAG8Y,CAAO,MAAMK,CAAU;AACtF;AAYO,SAASC,GAAiC;AAAA,EAC/C,cAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA,qBAAAC;AACF,GAAG;AAED,SAAO;AAAA,IACL,MAAMF,MAAmB;AAAA,IACzB,UAAUC,MAAuB;AAAA,IACjC,WAAWC,MAAwB,OAJxBH,KAAgB,CAAA,GAIsB,SAAS;AAAA,EAC9D;AACA;AC9GA,MAAMI,KAA6B;AAMnC,SAASC,GAAiBnB,GAAaoB,GAAoB;;AACzD,QAAMN,IAAeO,GAAgBrB,CAAW;AAChD,SAAIc,EAAa,KAAK,CAACQ,MAAQA,EAAI,UAAUF,CAAkB,IAAUA,MAClE5c,IAAAsc,EAAa,CAAC,MAAd,gBAAAtc,EAAiB,UAAS0c;AACnC;AAEA,SAASK,GAAkB9Z,GAAG;AAC5B,QAAM7E,IAAQ,OAAO6E,KAAM,WAAWA,IAAIA,EAAE,QACtC0K,IAAQ,OAAO1K,KAAM,WAAWA,IAAKA,EAAE,SAASA,EAAE;AACxD,SAAO,CAAC7E,GAAOuP,CAAK;AACtB;AAEA,SAASqP,GAAqBC,GAAG;AAC/B,QAAM7e,IAAQ,OAAO6e,KAAM,WAAWA,IAAIA,EAAE,OACtCtP,IAAQ,OAAOsP,KAAM,WAAWA,IAAKA,EAAE,SAASA,EAAE;AACxD,SAAO,CAAC7e,GAAOuP,CAAK;AACtB;AAEA,SAASuP,GAAmB,EAAE,UAAAC,GAAU,aAAA7B,GAAa,gBAAA8B,GAAgB,gBAAAC,KAAkB;AACrF,QAAMjP,KAAO+O,KAAA,gBAAAA,EAAU,SAAQ;AAE/B,MAAI/O,MAAS,SAAS;AACpB,UAAMhQ,IAAQ+e,EAAS,SAAS,IAC1B7L,IAAO6L,EAAS,WAAW,CAAA,GAC3BxP,IAAQwP,EAAS,SAAS;AAChC,WACE,gBAAAG;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAAlf;AAAA,QACA,UAAU,CAACmf,MAAMJ,EAAS,YAAYA,EAAS,SAASI,EAAE,OAAO,KAAK;AAAA,QACtE,UAAUjM,EAAK,WAAW;AAAA,QAC1B,cAAY3D;AAAA,QAEX,UAAA;AAAA,UAAA2D,EAAK,WAAW,KAAK,gBAAAkM,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,KAAC;AAAA,UACvC,CAACpf,KAASkT,EAAK,SAAS,uBACtB,UAAA,EAAO,OAAM,IAAG,UAAQ,IAAC,QAAM,IAAE,oBAAU3D,EAAM,aAAa,IAAG;AAAA,UAEnE2D,EAAK,IAAI,CAAC2L,MAAM;AACf,kBAAM,CAACpc,GAAG4c,CAAC,IAAIT,GAAqBC,CAAC;AACrC,mBAAO,gBAAAO,EAAC,UAAA,EAAe,OAAO3c,GAAI,eAAdA,CAAgB;AAAA,UACtC,CAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,MAAIuN,MAAS,cAAc;AACzB,UAAM2N,IAAUoB,EAAS,SACnBf,IAAae,EAAS,cAAc,IACpCO,IAAaP,EAAS,cAAc,SACpCQ,IAAeR,EAAS,gBACzBnB,GAAqBV,GAAaS,CAAO,GACxC6B,IAAezB,GAAmBb,GAAaS,GAASK,CAAU;AACxE,WACE,gBAAAkB,EAAAO,IAAA,EACE,UAAA;AAAA,MAAA,gBAAAP;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAOlB;AAAA,UACP,UAAU,CAACmB,MAAMJ,EAAS,iBAAiBA,EAAS,cAAcI,EAAE,OAAO,KAAK;AAAA,UAChF,UAAUI,EAAa,WAAW;AAAA,UAClC,cAAYD;AAAA,UAEX,UAAA;AAAA,YAAAC,EAAa,WAAW,KAAK,gBAAAL,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA;AAAA,cAAA;AAAA,cAAII,EAAW,YAAA;AAAA,cAAc;AAAA,YAAA,GAAC;AAAA,YAC5E,CAACtB,KAAcuB,EAAa,SAAS,uBACnC,UAAA,EAAO,OAAM,IAAG,UAAQ,IAAC,QAAM,IAAE,oBAAUD,EAAW,aAAa,IAAG;AAAA,YAExEC,EAAa,IAAI,CAACzB,MAAM;AACvB,oBAAM,CAACrb,GAAG4c,CAAC,IAAIT,GAAqBd,CAAC;AACrC,qBAAO,gBAAAsB,EAAC,UAAA,EAAe,OAAO3c,GAAI,eAAdA,CAAgB;AAAA,YACtC,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAyc;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAOF;AAAA,UACP,UAAU,CAACG,MAAMF,KAAkBA,EAAe,EAAE,QAAQE,EAAE,OAAO,OAAO;AAAA,UAC5E,UAAUK,EAAa,WAAW;AAAA,UAClC,cAAW;AAAA,UAEV,UAAA;AAAA,YAAAA,EAAa,WAAW,KAAK,gBAAAJ,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,YAAQ;AAAA,YACtD,CAACJ,KAAkBQ,EAAa,SAAS,KACxC,gBAAAJ,EAAC,UAAA,EAAO,OAAM,IAAG,UAAQ,IAAC,QAAM,IAAC,UAAA,iBAAa;AAAA,YAE/CI,EAAa,IAAI,CAAC3a,MAAM;AACvB,oBAAM,CAACpC,GAAG4c,CAAC,IAAIV,GAAkB9Z,CAAC;AAClC,qBAAO,gBAAAua,EAAC,UAAA,EAAe,OAAO3c,GAAI,eAAdA,CAAgB;AAAA,YACtC,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,GACF;AAAA,EAEJ;AAGA,QAAMid,IAAUxC,EAAY,SAAS;AACrC,SACE,gBAAAgC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAOF;AAAA,MACP,UAAU,CAACG,MAAMF,KAAkBA,EAAe,EAAE,QAAQE,EAAE,OAAO,OAAO;AAAA,MAC5E,UAAU,CAACO;AAAA,MACX,cAAW;AAAA,MAEV,UAAA;AAAA,QAAA,CAACA,KAAW,gBAAAN,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,mBAAe;AAAA,QAC5C,CAACJ,KAAkBU,KAClB,gBAAAN,EAAC,UAAA,EAAO,OAAM,IAAG,UAAQ,IAAC,QAAM,IAAC,UAAA,gBAAA,CAAa;AAAA,QAE/ClC,EAAY,IAAI,CAACrY,MAAM;AACtB,gBAAM,CAACpC,GAAG,CAAC,IAAIkc,GAAkB9Z,CAAC;AAClC,iBAAO,gBAAAua,EAAC,UAAA,EAAe,OAAO3c,GAAI,eAAdA,CAAgB;AAAA,QACtC,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;AAmCA,SAASkd,GAAU;AAAA,EACjB,QAAA9c;AAAA,EACA,OAAA+c;AAAA,EACA,aAAA1C,IAAc,CAAA;AAAA,EACd,iBAAAC,IAAkB,CAAA;AAAA,EAClB,cAAA0C;AAAA,EACA,gBAAAZ;AAAA,EACA,UAAA3E;AAAA,EACA,gBAAA6D,IAAiB;AAAA,EACjB,oBAAAC,IAAqB;AAAA,EACrB,qBAAAC,IAAsB;AAAA,EACtB,cAAAyB;AACF,GAAG;AACD,QAAMC,IAAUC,GAAO,IAAI,GACrBC,IAAeD,GAAO,IAAI,GAC1Bhd,IAAO4c,KAAA,gBAAAA,EAAO,MACdpe,KAASoe,KAAA,gBAAAA,EAAO,WAAU,CAAA,GAC1B1b,KAAWrB,KAAA,gBAAAA,EAAQ,aAAY,IAC/Bqd,IAAOL,KAAA,gBAAAA,EAAe3b,IACtBC,KAAYtB,KAAA,gBAAAA,EAAQ,cAAayb,IACjCU,KAAiBnc,KAAA,gBAAAA,EAAQ,WAAU,IAGnCua,KAAcwC,KAAA,gBAAAA,EAAO,iBACrBA,KAAA,QAAAA,EAAO,YAAYrC,KAAmBqC,KAAA,QAAAA,EAAO,gBAAgBO,KAAsBC,KAEnFlC,IAAeO,GAAgBrB,CAAW,GAC1CiD,IAAqB9B,GAAiBnB,GAAajZ,CAAS,GAE5D,CAACkZ,GAAaiD,CAAc,IAAIC,EAAS,EAAE,GAC3C,CAACC,GAAUC,CAAW,IAAIF,EAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,GAE1DG,IAAYzD,GAAqB;AAAA,IACrC,QAAQ+B;AAAA,IACR,MAAAhc;AAAA,IACA,aAAAka;AAAA,IACA,UAAAhZ;AAAA,IACA,iBAAAiZ;AAAA,IACA,aAAAC;AAAA,IACA,QAAA5b;AAAA,IACA,aAAA6b;AAAA,EAAA,CACD,GACKsD,IAAgBD,EAAU,SAAS,SAEnCE,IAAa3C,GAAiC;AAAA,IAClD,cAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,oBAAAC;AAAA,IACA,qBAAAC;AAAA,EAAA,CACD,GACKwC,IAAwB1D,EAAgB,SAAS;AAEvD,SAAA2D,GAAU,MAAM;AACd,UAAMC,IAAOhB,EAAQ;AACrB,QAAI,CAACgB,KAAQ,OAAO,iBAAmB,IAAa;AAEpD,QAAIC,IAAQ;AACZ,UAAMC,IAAiB,MAAM;AAC3B,MAAID,0BAA4BA,CAAK,GACrCA,IAAQ,sBAAsB,MAAM;AAClC,cAAME,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAMH,EAAK,WAAW,CAAC,GAChDI,IAAS,KAAK,IAAI,GAAG,KAAK,MAAMJ,EAAK,YAAY,CAAC;AACxD,QAAAN,EAAY,CAACpY,MACXA,EAAK,UAAU6Y,KAAS7Y,EAAK,WAAW8Y,IAAS9Y,IAAO,EAAE,OAAA6Y,GAAO,QAAAC,EAAA,CAClE;AAAA,MACH,CAAC;AAAA,IACH;AAEA,IAAAF,EAAA;AACA,UAAMG,IAAW,IAAI,eAAeH,CAAc;AAClD,WAAAG,EAAS,QAAQL,CAAI,GAEd,MAAM;AACX,MAAIC,0BAA4BA,CAAK,GACrCI,EAAS,WAAA;AAAA,IACX;AAAA,EACF,GAAG,CAAA,CAAE,GAELN,GAAU,MAAM;AACd,QAAIJ,EAAU,SAAS,QAAS;AAChC,UAAMW,IAASpB,EAAa;AAE5B,QADI,CAACoB,KACDb,EAAS,SAAS,KAAKA,EAAS,UAAU,EAAG;AAEjD,UAAMc,IAAYlE,MAAgBG,IAC5BgE,IAAYnE,MAAgBI;AAElC,QAAIgE;AACJ,QAAI;AACF,MAAIF,IACFE,IAAW5F,GAAoBpa,GAAQ,EAAE,YAAY0C,GAAU,SAAS,QAAQ,OAAO,MAAM,IACpFqd,IACTC,IAAWzH,GAAmBvY,CAAM,IAEpCggB,IAAWC,GAAgB;AAAA,QACzB,QAAAjgB;AAAA,QACA,eAAe4b,MAAgB+C;AAAA,QAC/B,UAAAjc;AAAA,QACA,WAAWmc;AAAA,QACX,UAAA/F;AAAA,QACA,MAAA4F;AAAA,MAAA,CACD;AAAA,IAEL,SAASna,GAAK;AACZ,cAAQ,MAAM,oBAAoBA,CAAG,GACrCua,GAAeva,KAAA,gBAAAA,EAAK,YAAW,kBAAkB;AACjD;AAAA,IACF;AAEA,SAAI,EAACyb,KAAA,QAAAA,EAAU,SAAQA,EAAS,KAAK,WAAW,MAC1C,CAACF;AAAW;AAGlB,UAAMI,IAAa;AAAA,MACjB,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,wBAAwB,CAAC,YAAY,WAAW,aAAa;AAAA,IAAA,GAEzD5H,IAAS;AAAA,MACb,GAAG0H,EAAS;AAAA,MACZ,UAAU;AAAA,MACV,OAAOhB,EAAS;AAAA,MAChB,QAAQA,EAAS;AAAA,IAAA;AAGnB,QAAI;AACF,MAAAF,EAAe,EAAE,GACjBqB,GAAO,MAAMN,GAAQG,EAAS,MAAM1H,GAAQ4H,CAAU;AAAA,IACxD,SAAS3b,GAAK;AACZ,cAAQ,MAAM,qBAAqBA,CAAG,GACtCua,GAAeva,KAAA,gBAAAA,EAAK,YAAW,mBAAmB;AAAA,IACpD;AAEA,WAAO,MAAM;AACX,UAAIsb;AACF,YAAI;AACF,UAAAM,GAAO,MAAMN,CAAM;AAAA,QACrB,SAAStb,GAAK;AACZ,kBAAQ,KAAK,oBAAoBA,CAAG;AAAA,QACtC;AAAA,IAEJ;AAAA,EACF,GAAG;AAAA,IACD2a,EAAU;AAAA,IACV1d;AAAA,IACAkB;AAAA,IACAgc;AAAA,IACAG;AAAA,IACAjD;AAAA,IACA5b;AAAA,IACA8Y;AAAA,IACAkG,EAAS;AAAA,IACTA,EAAS;AAAA,EAAA,CACV,GAEDM,GAAU,MAAM;AACd,UAAMO,IAASpB,EAAa;AAC5B,QAAI,CAACoB,KAAU,OAAO,iBAAmB,IAAa;AACtD,UAAMO,IAAiB,IAAI,eAAe,MAAM;AAC9C,UAAI;AACF,YAAIP,KAAUA,EAAO,MAAM;AACzB,gBAAMH,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAMG,EAAO,WAAW,CAAC,GAClDF,IAAS,KAAK,IAAI,GAAG,KAAK,MAAME,EAAO,YAAY,CAAC;AAC1D,UAAIH,IAAQ,KAAKC,IAAS,KACxBQ,GAAO,SAASN,GAAQ,EAAE,OAAAH,GAAO,QAAAC,GAAQ,UAAU,IAAO;AAAA,QAE9D;AAAA,MACF,SAASpb,GAAK;AACZ,gBAAQ,KAAK,qBAAqBA,CAAG;AAAA,MACvC;AAAA,IACF,CAAC;AACD,WAAA6b,EAAe,QAAQP,CAAM,GACtB,MAAMO,EAAe,WAAA;AAAA,EAC9B,GAAG,CAAClB,EAAU,IAAI,CAAC,qBAGhB,OAAA,EAAI,WAAW,YAAYC,IAAgB,WAAW,EAAE,IACvD,UAAA;AAAA,IAAA,gBAAAzB,EAAC,UAAA,EAAO,WAAU,uBACf,UAAA;AAAA,MAAA0B,EAAW,QACV,gBAAAxB,EAAC,OAAA,EAAI,WAAU,cACZ,UAAAN,GAAmB;AAAA,QAClB,UAAUgB;AAAA,QACV,aAAA5C;AAAA,QACA,gBAAA8B;AAAA,QACA,gBAAAC;AAAA,MAAA,CACD,GACH;AAAA,OAEA2B,EAAW,YAAYA,EAAW,cAClC,gBAAA1B,EAAC,OAAA,EAAI,WAAU,wBACZ,UAAA;AAAA,QAAA0B,EAAW,YACV,gBAAA1B;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAOhb;AAAA,YACP,UAAU,CAACib,MAAMF,KAAkBA,EAAe,EAAE,UAAUE,EAAE,OAAO,OAAO;AAAA,YAC9E,UAAU,CAAC0B;AAAA,YACX,cAAW;AAAA,YAEV,UAAA;AAAA,cAAA,CAACA,KACA,gBAAAzB,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,KAAC;AAAA,cAEnB,CAAClb,KAAY2c,KACZ,gBAAAzB,EAAC,UAAA,EAAO,OAAM,IAAG,UAAQ,IAAC,QAAM,IAAC,UAAA,oBAAA,CAAiB;AAAA,cAEnDjC,EAAgB,IAAI,CAAC1T,wBACnB,UAAA,EAAe,OAAOA,GAAI,UAAAoY,GAAoBpY,GAAGoW,KAAA,gBAAAA,EAAepW,EAAE,EAAA,GAAtDA,CAAwD,CACtE;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGJmX,EAAW,aACV,gBAAAxB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAOiB;AAAA,YACP,UAAU,CAAClB,MAAMF,KAAkBA,EAAe,EAAE,WAAWE,EAAE,OAAO,OAAO;AAAA,YAC/E,cAAW;AAAA,YAEV,UAAAjB,EAAa,IAAI,CAACQ,MACjB,gBAAAU,EAAC,UAAA,EAAuB,OAAOV,EAAI,OAAQ,UAAAA,EAAI,MAAA,GAAlCA,EAAI,KAAoC,CACtD;AAAA,UAAA;AAAA,QAAA;AAAA,MACH,EAAA,CAEJ;AAAA,IAAA,GAEJ;AAAA,IACA,gBAAAU,EAAC,SAAI,WAAU,mBAAkB,KAAKW,GACnC,UAAAW,EAAU,SAAS,UAChB,gBAAAtB,EAAC,OAAA,EAAI,WAAU,gBAAe,KAAKa,GAAc,IACjD,gBAAAb,EAAC,SAAI,WAAU,eAAe,UAAAsB,EAAU,KAAA,CAAK,EAAA,CAEnD;AAAA,EAAA,GACF;AAEJ;ACpYA,SAASoB,GAAcC,GAASC,GAAO;AACrC,MAAI,EAACA,KAAA,QAAAA,EAAO,QAAQ,QAAOD;AAC3B,QAAMzO,IAAO,IAAI,IAAIyO,EAAQ,IAAI,CAACld,MAAM,CAACA,EAAE,MAAMA,EAAE,QAAQ,EAAE,GAAGA,EAAA,CAAG,CAAC,CAAC;AACrE,aAAWod,KAAMD,GAAO;AACtB,UAAMne,IAAKoe,EAAG,MAAMA,EAAG;AACvB,QAAKpe;AACL,UAAIyP,EAAK,IAAIzP,CAAE,GAAG;AAChB,cAAM4Q,IAAWnB,EAAK,IAAIzP,CAAE;AAC5B,QAAAyP,EAAK,IAAIzP,GAAI,EAAE,GAAG4Q,GAAU,QAAQ,CAAC,GAAIA,EAAS,UAAU,CAAA,GAAK,GAAIwN,EAAG,UAAU,CAAA,CAAG,GAAG;AAAA,MAC1F;AACE,QAAA3O,EAAK,IAAIzP,GAAIoe,CAAE;AAAA,EAEnB;AACA,SAAO,MAAM,KAAK3O,EAAK,OAAA,CAAQ;AACjC;AAQA,SAAS4O,GAAmBlf,GAAMkB,GAAU;AAC1C,MAAI,CAAClB,KAAQ,CAACkB,UAAiB,CAAA;AAC/B,QAAM2Z,wBAAW,IAAA,GACX1R,IAAM,CAAA;AACZ,aAAW1C,KAAKzG,EAAK,UAAU,CAAA,GAAI;AACjC,UAAMjC,IAAO,OAAO0I,EAAE,QAAQA,EAAE,aAAaA,EAAE,cAAcA,EAAE,UAAU,GACnExI,IAAK,OAAOwI,EAAE,MAAMA,EAAE,WAAWA,EAAE,YAAYA,EAAE,QAAQ;AAC/D,QAAI,CAAC,OAAO,SAAS1I,CAAI,KAAK,CAAC,OAAO,SAASE,CAAE,KAAKA,KAAMF,EAAM;AAClE,UAAMd,IAAM,GAAGc,CAAI,IAAIE,CAAE;AACzB,IAAI4c,EAAK,IAAI5d,CAAG,MAChB4d,EAAK,IAAI5d,CAAG,GACZkM,EAAI,KAAK,EAAE,MAAApL,GAAM,IAAAE,GAAI,CAACiD,CAAQ,GAAGuF,EAAEvF,CAAQ,KAAK,IAAI;AAAA,EACtD;AACA,SAAOiI;AACT;AAcA,SAAwBgW,GAAsB;AAAA,EAC5C,sBAAAC,IAAuB;AAAA,EACvB,YAAAC,IAAa;AAAA,EACb,YAAAC,IAAa,CAAA;AAAA,EACb,WAAA7d,IAAY;AACd,IAAI,IAAI;AACN,QAAM,CAACvB,GAAOqf,CAAQ,IAAIhC,EAAS,CAAA,CAAE,GAC/B,CAACte,GAASugB,CAAU,IAAIjC,EAAS,CAAA,CAAE,GACnC,CAACjb,GAAcmd,CAAe,IAAIlC,EAAS,CAAA,CAAE,GAC7C,CAACnc,GAAkBse,CAAmB,IAAInC,EAAS,CAAA,CAAE,GACrD,CAAClc,GAAcse,CAAe,IAAIpC,EAAS,CAAA,CAAE,GAC7C,CAAChb,GAAYqd,CAAa,IAAIrC,EAAS,CAAA,CAAE,GACzC,CAAC7b,GAAame,CAAc,IAAItC,EAAS,EAAE,GAC3C,CAAC/a,GAAcsd,CAAe,IAAIvC,EAAS,CAAA,CAAE,GAC7C,CAACle,GAAO0gB,CAAQ,IAAIxC,EAAS,EAAE,GAC/B,CAAC/b,GAAewe,CAAgB,IAAIzC,EAAS6B,KAAwB,EAAE,GACvE,CAACa,GAAcC,CAAe,IAAI3C,EAAS,CAAA,CAAE,GAC7C4C,IAAsBnD,GAAO,IAAI;AAkBvC,EAAAc,GAAU,MAAM;AACd,IAAI,CAACuB,KAAcc,EAAoB,YAAYd,MACnDc,EAAoB,UAAUd,GAC9Bld,GAAkBkd,CAAU,EACzB,KAAK,CAAC3e,MAAQ;AACb,UAAI,CAACA,EAAK;AACV,YAAM0f,IAAY,MAAM,KAAK,IAAI,IAAI1f,EAAI,IAAI,CAACmB,MAAM,CAACA,EAAE,QAAQA,CAAC,CAAC,CAAC,EAAE,QAAQ;AAC5E,MAAA2d,EAAWY,CAAS,GACpBN,EAAgBve,GAA4B;AAAA,QAC1C,SAAS6e,EAAU,IAAI,CAACve,MAAMA,EAAE,MAAM;AAAA,QACtC,eAAAL;AAAA,QACA,WAAAC;AAAA,QACA,aAAa;AAAA,QACb,kBAAAL;AAAA,QACA,cAAAC;AAAA,QACA,yBAAyB;AAAA,MAAA,CAC1B,CAAC;AAAA,IACJ,CAAC,EACA,MAAM,CAAC0B,MAAQ;AACd,cAAQ,KAAK,gCAAgCA,EAAI,OAAO;AAAA,IAC1D,CAAC;AAAA,EACL,GAAG,CAACsc,GAAY7d,GAAeC,GAAWL,GAAkBC,CAAY,CAAC,GAKzEyc,GAAU,MAAM;AACd,QAAI,EAACwB,KAAA,QAAAA,EAAY,QAAQ;AACzB,UAAM5e,IAAM4e,EACT,IAAI,CAACzd,OAAO,EAAE,QAAQA,EAAE,MAAMA,EAAE,SAAS,EACzC,OAAO,CAACA,MAAMA,EAAE,MAAM;AACzB,IAAA2d,EAAW,CAACna,MAAS;AACnB,YAAMoM,IAAW,IAAI,IAAIpM,EAAK,IAAI,CAACxD,MAAMA,EAAE,MAAM,CAAC,GAC5Cwe,IAAS3f,EAAI,OAAO,CAACmB,MAAM,CAAC4P,EAAS,IAAI5P,EAAE,MAAM,CAAC;AACxD,aAAOwe,EAAO,SAAS,CAAC,GAAGhb,GAAM,GAAGgb,CAAM,IAAIhb;AAAA,IAChD,CAAC;AAAA,EACH,GAAG,CAACia,CAAU,CAAC,GAEfxB,GAAU,MAAM;AACd,IAAAiC,EAAS,CAAC1a,MAAUA,KAAQA,EAAK,WAAW,UAAU,KAAKA,EAAK,SAAS,YAAY,IAAIA,IAAO,EAAG;AAAA,EACrG,GAAG,CAAC7C,CAAY,CAAC,GAEjBsb,GAAU,MAAM;AACd,QAAI,CAAC7e,EAAQ,QAAQ;AACnB,MAAA6gB,EAAgB,CAAA,CAAE;AAClB;AAAA,IACF;AACA,UAAMQ,IAAiB7f,GAAexB,EAAQ,IAAI,CAAC4C,MAAMA,EAAE,MAAM,GAAGL,CAAa;AACjF,IAAAse,EAAgB,CAACza,MACF,MAAM,KAAK,EAAE,QAAQ5D,EAAA,CAAW,EAAE,IAAI,CAACV,GAAGC,MAAQ;;AAC7D,YAAMyQ,IAAWpM,EAAKrE,CAAG,KAAK,CAAA,GACxBpD,IAASqB,EAAQ,KAAK,CAAC4C,OAAMA,GAAE,WAAW4P,EAAS,MAAM,IAAIA,EAAS,SAAS6O,EAAetf,CAAG,OAAKpC,IAAAK,EAAQ+B,CAAG,MAAX,gBAAApC,EAAc,WAAU,IAC9HsC,IAAWuQ,EAAS,YAAY/P,GAChCP,IAAYF,GAA2B;AAAA,QAC3C,UAAAC;AAAA,QACA,WAAWuQ,EAAS;AAAA,QACpB,kBAAArQ;AAAA,QACA,cAAAC;AAAA,QACA,yBAAyB;AAAA,MAAA,CAC1B;AACD,aAAO,EAAE,QAAAzD,GAAQ,UAAAsD,GAAU,WAAAC,EAAA;AAAA,IAC7B,CAAC,CAEF;AAAA,EACH,GAAG,CAAClC,GAASuC,GAAeE,GAAaN,GAAkBC,GAAcI,CAAS,CAAC,GAGnFqc,GAAU,MAAM;AACd,QAAI,CAACuB,EAAY;AAEjB,IADe7c,EAAa,IAAI,CAAC+d,MAAQA,EAAI,MAAM,EAAE,OAAO,OAAO,EAC5D,QAAQ,CAAC3iB,MAAW;AACzB,YAAM4iB,IAAUtgB,EAAM,KAAK,CAAC2B,OAAOA,EAAE,MAAMA,EAAE,YAAYjE,CAAM,GACzD6iB,IAAUR,EAAa,SAASriB,CAAM;AAC5C,MAAI4iB,KAAWC,MACfP,EAAgB,CAAC7a,MAAS,CAAC,GAAGA,GAAMzH,CAAM,CAAC,GAC3CwE,GAAcid,GAAYzhB,CAAM,EAC7B,KAAK,CAACoC,MAAS;AAEd,QADAkgB,EAAgB,CAAC7a,MAASA,EAAK,OAAO,CAACxE,MAAOA,MAAOjD,CAAM,CAAC,GACvDoC,KACLuf,EAAS,CAACla,MAAS;AACjB,gBAAMoG,IAASqT;AAAA,YACb,CAAC,GAAGzZ,EAAK,OAAO,CAACxD,OAAOA,EAAE,MAAMA,EAAE,YAAYjE,CAAM,GAAGoC,CAAI;AAAA,YAC3Dsf;AAAA,UAAA,GAEIhF,IAAQ1Y,GAAiB6J,CAAM;AACrC,iBAAAgU,EAAgBnF,EAAM,YAAY,GAClCoF,EAAoBpF,EAAM,gBAAgB,GAC1CqF,EAAgBrF,EAAM,YAAY,GAClCsF,EAActF,EAAM,UAAU,GAC1B,CAAC5Y,KAAe4Y,EAAM,gBACxBuF,EAAevF,EAAM,WAAW,GAChCwF,EAAgB,CAACY,MAAYA,EAAQ,IAAI,CAACH,OAAS;AAAA,YACjD,GAAGA;AAAA,YACH,UAAUA,EAAI,YAAYjG,EAAM;AAAA,YAChC,WAAWrZ,GAA2B;AAAA,cACpC,UAAUsf,EAAI,YAAYjG,EAAM;AAAA,cAChC,WAAWiG,EAAI;AAAA,cACf,kBAAkBjG,EAAM;AAAA,cACxB,cAAcA,EAAM;AAAA,cACpB,yBAAyB;AAAA,YAAA,CAC1B;AAAA,UAAA,EACD,CAAC,IAEE7O;AAAA,QACT,CAAC;AAAA,MACH,CAAC,EACA,MAAM,CAAC1I,MAAQ;AACd,gBAAQ,MAAMA,CAAG,GACjBmd,EAAgB,CAAC7a,MAASA,EAAK,OAAO,CAACxE,MAAOA,MAAOjD,CAAM,CAAC,GAC5DmiB,EAAShd,EAAI,WAAW,sBAAsBnF,CAAM,EAAE;AAAA,MACxD,CAAC;AAAA,IACL,CAAC;AAAA,EACH,GAAG,CAAC4E,GAAc6c,GAAYnf,GAAO+f,GAAcve,GAAa4d,CAAU,CAAC,GAG3ExB,GAAU,MAAM;AACd,IAAKwB,KAAA,QAAAA,EAAY,UACjBC,EAAS,CAACla,MAAS;AACjB,UAAI,CAACA,EAAK,QAAQ;AAEhB,cAAMiV,IAAQ1Y,GAAiB0d,CAAU;AACzC,eAAAG,EAAgBnF,EAAM,YAAY,GAClCoF,EAAoBpF,EAAM,gBAAgB,GAC1CqF,EAAgBrF,EAAM,YAAY,GAClCsF,EAActF,EAAM,UAAU,GAC1B,CAAC5Y,KAAe4Y,EAAM,eAAauF,EAAevF,EAAM,WAAW,GAChEgF;AAAA,MACT;AACA,YAAM7T,IAASqT,GAAczZ,GAAMia,CAAU,GACvChF,IAAQ1Y,GAAiB6J,CAAM;AACrC,aAAAgU,EAAgBnF,EAAM,YAAY,GAClCoF,EAAoBpF,EAAM,gBAAgB,GAC1CqF,EAAgBrF,EAAM,YAAY,GAClCsF,EAActF,EAAM,UAAU,GAC1B,CAAC5Y,KAAe4Y,EAAM,eAAauF,EAAevF,EAAM,WAAW,GAChE7O;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC6T,CAAU,CAAC;AAEf,QAAMnF,IAAkBwG;AAAA,IACtB,MAAM,CAAC,GAAGre,GAAc,GAAGlB,GAAkB,GAAGC,CAAY;AAAA,IAC5D,CAACiB,GAAclB,GAAkBC,CAAY;AAAA,EAAA,GAGzCuf,IAAqBD;AAAA,IACzB,MAAM1hB,EACH,IAAI,CAAC4C,OAAO,EAAE,QAAQA,EAAE,QAAQ,OAAOA,EAAE,SAAS,EAClD,KAAK,CAACvD,GAAGC,MAAMD,EAAE,MAAM,cAAcC,EAAE,KAAK,CAAC;AAAA,IAChD,CAACU,CAAO;AAAA,EAAA,GAGJ4hB,IAAcF,GAAQ,MAAM;AAChC,UAAMG,IAAW,CAAC,GAAGxe,GAAc,GAAGlB,GAAkB,GAAGC,CAAY;AACvE,WAAO,MAAM,KAAK,EAAE,QAAQI,EAAA,CAAW,EAAE,IAAI,CAACV,GAAGC,MAAQ;AACvD,YAAMuf,IAAM/d,EAAaxB,CAAG,KAAK,CAAA,GAC3BhB,IAAOE,EAAM,KAAK,CAAC2B,QAAOA,GAAE,MAAMA,GAAE,YAAY0e,EAAI,MAAM,KAAK,MAG/DQ,IAAsB/gB,IACxB8gB,EAAS,OAAO,CAACra,OAAMua,GAAYhhB,GAAMyG,EAAC,CAAC,IAC3Cqa;AAEJ,UAAI5f,IAAWqf,EAAI,YAAY7e;AAE/B,MAAI1B,KAAQ,CAAC+gB,EAAoB,SAAS7f,CAAQ,MAChDA,IAAW6f,EAAoB,CAAC,KAAK7f;AAGvC,YAAMod,IAAYjd,EAAa,SAASH,CAAQ,GAC1C+f,IAAgB,CAAC3C,KAAald,EAAiB,SAASF,CAAQ,GAChEqd,IAAY,CAACD,KAAa,CAAC2C,KAAiB/f,MAAa,OACzDkZ,KAAckE,IAAY,YAAYC,IAAY,YAAa0C,IAAgB,gBAAgB,WAE/F9f,KAAYod,IAAY,YAAYgC,EAAI,cAAcjC,IAAY,YAAa2C,IAAgB,gBAAgB,iBAC/GrjB,KAAS2iB,EAAI,WAAUvgB,KAAA,gBAAAA,EAAM,QAAMA,KAAA,gBAAAA,EAAM,WAAU,IAEnDxB,KAAS+f,KACVve,KAAA,gBAAAA,EAAM,WAAU,CAAA,IACjBse,IACEY,GAAmBlf,GAAMkB,CAAQ,IACjCggB,GAAoBlhB,GAAMkB,GAAU+f,CAAa;AAEvD,aAAO;AAAA,QACL,QAAQ,EAAE,QAAArjB,IAAQ,UAAAsD,GAAU,WAAAC,GAAA;AAAA,QAC5B,MAAAnB;AAAA,QACA,SAASigB,EAAa,SAASM,EAAI,MAAM;AAAA,QACzC,eAAAU;AAAA,QACA,WAAA3C;AAAA,QACA,WAAAC;AAAA,QACA,aAAAnE;AAAA,QACA,QAAA5b;AAAA,QACA,iBAAiBuiB;AAAA,QACjB,OAAOnjB;AAAA,MAAA;AAAA,IAEX,CAAC;AAAA,EACH,GAAG,CAAC4E,GAActC,GAAOwB,GAAaN,GAAkBC,GAAc4e,GAAcxe,GAAWa,CAAY,CAAC,GAEtG6e,IAAqB,CAACC,GAAOC,MAAU;AAC3C,IAAAvB,EAAgB,CAACza,MAAS;AACxB,YAAMyJ,IAAO,CAAC,GAAGzJ,CAAI,GAEfoG,IAAS,EAAE,GADJqD,EAAKsS,CAAK,KAAK,CAAA,GACF,GAAGC,EAAA;AAC7B,aAAIA,EAAM,aACR5V,EAAO,YAAYxK,GAA2B;AAAA,QAC5C,UAAUogB,EAAM;AAAA,QAChB,WAAW5V,EAAO;AAAA,QAClB,kBAAArK;AAAA,QACA,cAAAC;AAAA,QACA,yBAAyB;AAAA,MAAA,CAC1B,IAEHyN,EAAKsS,CAAK,IAAI3V,GACPqD;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAAzP;AAAA,IACA,eAAAmC;AAAA,IACA,kBAAAwe;AAAA,IACA,UAAAD;AAAA,IACA,WAAW9gB,EAAQ;AAAA,IACnB,cAAAqD;AAAA,IACA,kBAAAlB;AAAA,IACA,cAAAC;AAAA,IACA,YAAAkB;AAAA,IACA,iBAAA4X;AAAA,IACA,oBAAAyG;AAAA,IACA,aAAAC;AAAA,IACA,oBAAAM;AAAA,EAAA;AAEJ;ACvUA,SAASnV,GAAQ5O,GAAM;AACrB,SAAO,MAAM,QAAQA,CAAI,IAAIA,IAAO,CAAA;AACtC;AAQA,SAAS8F,GAASlG,GAAO;AACvB,QAAM,IAAI,OAAOA,CAAK;AACtB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAQA,SAASskB,GAAenR,IAAQ,IAAI;AAClC,SAAO;AAAA,IACL,GAAGA;AAAA,IACH,GAAGjN,GAASiN,EAAM,CAAC;AAAA,IACnB,GAAGjN,GAASiN,EAAM,CAAC;AAAA,IACnB,GAAGjN,GAASiN,EAAM,CAAC;AAAA,EACvB;AACA;AASO,SAASoR,GAAsBpW,IAAS,CAAA,GAAIqW,IAAS,CAAC,GAAG,CAAC,GAAGhe,IAAU,GAAG;AAC/E,QAAM,CAACie,GAAIC,CAAE,IAAIF,GACXja,IAAS,OAAO/D,CAAO,IAAI,KAAK,KAAM,KACtCme,IAAO,KAAK,IAAIpa,CAAK,GACrBqa,IAAO,KAAK,IAAIra,CAAK;AAE3B,SAAOyE,GAAQb,CAAM,EAClB,IAAImW,EAAc,EAClB,IAAI,CAACjlB,MAAQ;AACZ,QAAI,CAAC,OAAO,SAASA,EAAI,CAAC,KAAK,CAAC,OAAO,SAASA,EAAI,CAAC,EAAG,QAAO,EAAE,GAAGA,EAAG;AACvE,UAAMgK,IAAKhK,EAAI,IAAIolB,GACbnb,IAAKjK,EAAI,IAAIqlB;AACnB,WAAO;AAAA,MACL,GAAGrlB;AAAA,MACH,OAAQgK,IAAKub,IAAStb,IAAKqb;AAAA,MAC3B,QAAStb,IAAKsb,IAASrb,IAAKsb;AAAA,IACpC;AAAA,EACI,CAAC;AACL;AAUO,SAASC,GAAc1W,IAAS,IAAIqW,IAAS,CAAC,GAAG,CAAC,GAAGhe,IAAU,GAAG0a,IAAQ,IAAI;AACnF,QAAM4D,IAAYP,GAAsBpW,GAAQqW,GAAQhe,CAAO,GACzDue,IAAO,MAAM,OAAO7D,KAAS,CAAC;AACpC,SAAI,CAAC,OAAO,SAAS6D,CAAI,KAAKA,KAAQ,IAAUD,IACzCA,EAAU,OAAO,CAACzlB,MAAQ,OAAO,SAASA,EAAI,MAAM,KAAK,KAAK,IAAIA,EAAI,MAAM,KAAK0lB,CAAI;AAC9F;AASO,SAASC,GAAS7W,IAAS,CAAA,GAAI8W,IAAa,MAAMhL,IAAU,MAAM;AACvE,MAAI7Z,IAAO4O,GAAQb,CAAM,EAAE,IAAImW,EAAc;AAC7C,MAAI,MAAM,QAAQW,CAAU,KAAKA,EAAW,WAAW,GAAG;AACxD,UAAM,CAACC,GAAKC,CAAM,IAAIF;AACtB,IAAA7kB,IAAOA,EAAK,OAAO,CAACf,MAAQ,OAAO,SAASA,EAAI,CAAC,KAAKA,EAAI,KAAK,OAAO6lB,CAAG,KAAK7lB,EAAI,KAAK,OAAO8lB,CAAM,CAAC;AAAA,EACvG;AACA,SAAIlL,MACF7Z,IAAOA,EAAK,IAAI,CAACf,OAAS;AAAA,IACxB,GAAGA;AAAA,IACH,aAAaA,KAAA,gBAAAA,EAAM4a;AAAA,EACzB,EAAM,IAEG7Z;AACT;AAWO,SAASglB,GAAYjX,IAAS,CAAA,GAAIqW,IAAS,CAAC,GAAG,CAAC,GAAGhe,IAAU,GAAG0a,IAAQ,IAAIjH,IAAU,MAAM;AACjG,MAAIoL,IAAUR,GAAc1W,GAAQqW,GAAQhe,GAAS0a,CAAK;AAC1D,SAAIjH,MACFoL,IAAUA,EAAQ,IAAI,CAAChmB,OAAS;AAAA,IAC9B,GAAGA;AAAA,IACH,aAAaA,KAAA,gBAAAA,EAAM4a;AAAA,EACzB,EAAM,IAEGoL;AACT;AClHA,SAASrW,GAAQ5O,GAAM;AACrB,SAAO,MAAM,QAAQA,CAAI,IAAIA,IAAO,CAAA;AACtC;AAMA,SAASklB,GAAUjmB,IAAM,IAAI;AAC3B,SAAOA,EAAI,WAAWA,EAAI,UAAUA,EAAI;AAC1C;AAMA,SAAS6G,GAASlG,GAAOuD,IAAW,QAAW;AAC7C,QAAMwG,IAAI,OAAO/J,CAAK;AACtB,SAAO,OAAO,SAAS+J,CAAC,IAAIA,IAAIxG;AAClC;AAQO,SAASgiB,GAAiBpX,IAAS,IAAI8L,IAAU,MAAM;AAC5D,QAAM3S,IAAU,oBAAI,IAAG;AAEvB,EAAA0H,GAAQb,CAAM,EAAE,QAAQ,CAAC9O,MAAQ;AAC/B,UAAMuB,IAAS0kB,GAAUjmB,CAAG;AAC5B,QAA4BuB,KAAW,QAAQ,GAAGA,CAAM,GAAG,KAAI,MAAO,GAAI;AAC1E,UAAMX,IAAM,GAAGW,CAAM;AACrB,IAAK0G,EAAQ,IAAIrH,CAAG,KAAGqH,EAAQ,IAAIrH,GAAK,EAAE,GAC1CqH,EAAQ,IAAIrH,CAAG,EAAE,KAAKZ,CAAG;AAAA,EAC3B,CAAC;AAED,QAAMmmB,IAAW,CAAA;AACjB,SAAAle,EAAQ,QAAQ,CAAClH,GAAMQ,MAAW;AAChC,UAAMS,IAAS,CAAC,GAAGjB,CAAI,EAAE,KAAK,CAACkB,GAAGC,MAAM2E,GAAS5E,EAAE,IAAI,CAAC,IAAI4E,GAAS3E,EAAE,IAAI,CAAC,CAAC,GACvEkkB,IAAU;AAAA,MACd,SAAS7kB;AAAA,MACT,GAAGS,EAAO,IAAI,CAAChC,MAAQ6G,GAAS7G,EAAI,GAAG,CAAC,CAAC;AAAA,MACzC,GAAGgC,EAAO,IAAI,CAAChC,MAAQ6G,GAAS7G,EAAI,GAAG,CAAC,CAAC;AAAA,MACzC,GAAGgC,EAAO,IAAI,CAAChC,MAAQ6G,GAAS7G,EAAI,GAAG,CAAC,CAAC;AAAA,MACzC,OAAO;AAAA,IACb;AACI,IAAI4a,MACFwL,EAAQ,QAAQpkB,EAAO,IAAI,CAAChC,MAAQA,KAAA,gBAAAA,EAAM4a,EAAQ,IAEpDuL,EAAS,KAAKC,CAAO;AAAA,EACvB,CAAC,GAEMD;AACT;AASO,SAASE,GAAiBtkB,IAAY,CAAA,GAAIukB,IAAS,GAAG1L,IAAU,MAAM;AAC3E,SAAOjL,GAAQ5N,CAAS,EAAE,IAAI,CAAC/B,OAAS;AAAA,IACtC,SAASimB,GAAUjmB,CAAG;AAAA,IACtB,MAAMA,KAAA,gBAAAA,EAAK;AAAA,IACX,IAAIA,KAAA,gBAAAA,EAAK;AAAA,IACT,QAAAsmB;AAAA,IACA,OAAO1L,IAAU5a,KAAA,gBAAAA,EAAM4a,KAAW;AAAA,IAClC,OAAOA,IAAU5a,KAAA,gBAAAA,EAAM4a,KAAW;AAAA,EACtC,EAAI;AACJ;AAQO,SAAS2L,GAAyBxkB,IAAY,IAAI6Z,IAAW,MAAM;AACxE,SAAKA,IACEjM,GAAQ5N,CAAS,EACrB,OAAO,CAAC/B,MAAQ,OAAO,UAAU,eAAe,KAAKA,KAAO,CAAA,GAAI4b,CAAQ,CAAC,EACzE,IAAI,CAAC5b,OAAS;AAAA,IACb,SAASimB,GAAUjmB,CAAG;AAAA,IACtB,OAAOA,KAAA,gBAAAA,EAAM4b;AAAA,IACb,OAAO,OAAQ/U,GAAS7G,KAAA,gBAAAA,EAAK,MAAM,CAAC,IAAM6G,GAAS7G,KAAA,gBAAAA,EAAK,IAAI,CAAC;AAAA,EACnE,EAAM,IAPkB,CAAA;AAQxB;AClFA,SAASwmB,GAAmB;AAAA,EAC1B,aAAAC,IAAc;AAAA,EACd,aAAAC,IAAc,MAAM;AAAA,EAAC;AAAA,EACrB,YAAAC,IAAa,MAAM;AAAA,EAAC;AAAA,EACpB,YAAAC,IAAa,MAAM;AAAA,EAAC;AAAA,EACpB,OAAAC,IAAQ,MAAM;AAAA,EAAC;AAAA,EACf,gBAAAC,IAAiB;AAAA,EACjB,wBAAAC,IAAyB,MAAM;AAAA,EAAC;AAClC,GAAG;AACD,SACE,gBAAAlH,EAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,IAAA,gBAAAE,EAAC,YAAO,MAAK,UAAS,WAAU,gBAAe,SAAS4G,GAAY,UAAA,sBAAA,CAEpE;AAAA,IACA,gBAAA5G,EAAC,YAAO,MAAK,UAAS,WAAU,gBAAe,SAAS6G,GAAY,UAAA,YAAA,CAEpE;AAAA,IACA,gBAAA7G,EAAC,YAAO,MAAK,UAAS,WAAU,gBAAe,SAAS8G,GAAO,UAAA,eAAA,CAE/D;AAAA,IACA,gBAAA9G,EAAC,UAAA,EAAO,MAAK,UAAS,WAAU,gBAAe,SAAS2G,GACrD,UAAAD,MAAgB,UAAU,wBAAwB,uBAAA,CACrD;AAAA,IACA,gBAAA5G,EAAC,SAAA,EAAM,WAAU,iCACf,UAAA;AAAA,MAAA,gBAAAE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS+G;AAAA,UACT,UAAUC;AAAA,QAAA;AAAA,MAAA;AAAA,MACV;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;ACtBA,SAASC,GAAiB;AAAA,EACxB,YAAAC,IAAa,CAAA;AAAA,EACb,kBAAAC,IAAmB;AAAA,EACnB,kBAAAC,IAAmB,MAAM;AAAA,EAAC;AAAA,EAC1B,SAAAC,IAAU;AAAA,EACV,iBAAAC,IAAkB,MAAM;AAAA,EAAC;AAAA,EACzB,eAAAC,IAAgB;AAAA,EAChB,cAAAC,IAAe;AAAA,EACf,cAAAC,IAAe,MAAM;AAAA,EAAC;AACxB,GAAG;;AACD,SACE,gBAAA3H,EAAC,OAAA,EAAI,WAAU,aAEb,UAAA;AAAA,IAAA,gBAAAE,EAAC,SAAA,EAAM,WAAU,oBAAmB,SAAQ,sBAAqB,UAAA,YAEjE;AAAA,IACA,gBAAAF;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,WAAU;AAAA,QACV,OAAOqH;AAAA,QACP,UAAU,CAACpH,MAAMqH,EAAiBrH,EAAE,OAAO,KAAK;AAAA,QAE/C,UAAA;AAAA,UAAAmH,EAAW,WAAW,KACrB,gBAAAlH,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,qBAAiB;AAAA,UAEnCkH,EAAW,IAAI,CAAC7c,MACf,gBAAA2V,EAAC,YAAe,OAAO3V,GAAI,UAAAA,EAAA,GAAdA,CAAgB,CAC9B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAIFkd,KAAiBA,EAAc,SAAS,aACvC,gBAAAzH,EAAC,OAAA,EAAI,WAAU,oBACb,UAAA;AAAA,MAAA,gBAAAE,EAAC,QAAA,EAAK,WAAU,sDACb,YAAAxd,IAAA+kB,EAAc,QAAd,gBAAA/kB,EAAmB,QAAQ,OAAM,IAAA,CACpC;AAAA,MACA,gBAAAwd,EAAC,OAAA,EAAI,WAAU,uBAAA,CAAuB;AAAA,MACtC,gBAAAA,EAAC,UAAK,WAAU,sDACb,kBAAc,0BAAK,QAAQ,OAAM,IAAA,CACpC;AAAA,IAAA,GACF;AAAA,IAEDuH,KAAiBA,EAAc,SAAS,mCACtC,OAAA,EAAI,WAAU,yBACX,WAAAA,EAAc,cAAc,CAAA,GAAI,IAAI,CAACnM,GAAKrS,MAAM;AAChD,YAAM2e,IAAM,KAAK,MAAO3e,IAAI,KAAK,IAAIwe,EAAc,WAAW,QAAQ,CAAC,IAAK,GAAG;AAC/E,aACE,gBAAAvH;AAAA,QAAC;AAAA,QAAA;AAAA,UAAe,WAAU;AAAA,UACxB,OAAO,EAAE,YAAY,OAAO0H,CAAG,YAAA;AAAA,UAC9B,UAAAtM;AAAA,QAAA;AAAA,QAFQA;AAAA,MAAA;AAAA,IAKf,CAAC,EAAA,CACH;AAAA,IAIF,gBAAA0E,EAAC,SAAA,EAAM,WAAU,oBAAmB,SAAQ,qBAAoB,UAAA;AAAA,MAAA;AAAA,MACpD,KAAK,MAAMuH,IAAU,GAAG;AAAA,MAAE;AAAA,IAAA,GACtC;AAAA,IACA,gBAAArH;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,MAAK;AAAA,QACL,WAAU;AAAA,QACV,KAAI;AAAA,QACJ,KAAI;AAAA,QACJ,MAAK;AAAA,QACL,OAAOqH;AAAA,QACP,UAAU,CAACtH,MAAMuH,EAAgB,WAAWvH,EAAE,OAAO,KAAK,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,IAI5DyH,KACC,gBAAA1H,EAAC,OAAA,EAAI,WAAU,oBACb,UAAA;AAAA,MAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,QAAA,gBAAAE,EAAC,UAAK,UAAA,mBAAA,CAAgB;AAAA,QACtB,gBAAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAASyH;AAAA,YACT,cAAW;AAAA,YACZ,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAED,GACF;AAAA,wBACC,SAAA,EAAM,WAAU,0BACf,UAAA,gBAAAzH,EAAC,WACE,UAAA,OAAO,QAAQwH,CAAY,EAAE,IAAI,CAAC,CAAC3mB,GAAKD,CAAK,wBAC3C,MAAA,EACC,UAAA;AAAA,QAAA,gBAAAof,EAAC,QAAI,UAAAnf,EAAA,CAAI;AAAA,QACT,gBAAAmf,EAAC,QAAI,UAAApf,KAAU,OAA8B,MAAM,OAAOA,CAAK,EAAA,CAAE;AAAA,MAAA,KAF1DC,CAGT,CACD,EAAA,CACH,EAAA,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;ACzGY,MAAC8mB,KAA0B;AAAA,EACrC,EAAE,SAAS,GAAG,QAAQ,QAAO;AAAA,EAC7B,EAAE,SAAS,GAAG,QAAQ,SAAQ;AAAA,EAC9B,EAAE,SAAS,GAAG,QAAQ,OAAM;AAC9B,GAMaC,KAAwB;AAiB9B,SAASC,GAAkBC,GAAOC,GAAMC,IAAiBL,IAAyB;;AACvF,QAAMM,IAAUH,EAAM;AACtB,MAAI,CAACG,KAAW,OAAOA,KAAY,YAAY,MAAM,QAAQA,CAAO;AAClE,WAAOH,EAAM,aAAa;AAI5B,MAAII,MAAc1lB,IAAAwlB,EAAe,CAAC,MAAhB,gBAAAxlB,EAAmB,WAAU;AAC/C,aAAW2lB,KAAMH;AACf,IAAID,KAAQI,EAAG,YACbD,IAAcC,EAAG;AAIrB,SAAOF,EAAQC,CAAW,KAAKJ,EAAM,aAAa;AACpD;AASO,SAASM,GAAkBC,GAAQ;AACxC,SAAO,CAAC,GAAGA,CAAM,EAAE,KAAK,CAACnmB,GAAGC,OAAOD,EAAE,cAAc,MAAMC,EAAE,cAAc,EAAE;AAC7E;AAYO,SAASmmB,GAAiBD,GAAQ;AAEvC,QAAME,IAAO,CAAA;AACb,aAAWT,KAASO,GAAQ;AAC1B,UAAMxnB,IACJinB,EAAM,aAAa,QAAQA,EAAM,cAAc,KAC3C,OAAOA,EAAM,SAAS,IACtB;AACN,IAAKS,EAAK1nB,CAAG,MAAG0nB,EAAK1nB,CAAG,IAAI,CAAA,IAC5B0nB,EAAK1nB,CAAG,EAAE,KAAKinB,CAAK;AAAA,EACtB;AACA,SAAOS;AACT;AAaO,SAASC,GAAkBC,GAAUphB,GAAUqhB,IAAiB,IAAI;AACzE,QAAMC,IAAU,CAAA;AAChB,MAAIF,KAAYphB,KAAYqhB,KAAkB,EAAG,QAAOC;AAExD,QAAM1V,IAAQ,KAAK,KAAKwV,IAAWC,CAAc,IAAIA,GAE/CE,IAAUF,IAAiB;AACjC,WAASpe,IAAI2I,GAAO3I,KAAKjD,IAAWuhB,GAASte,KAAKoe,GAAgB;AAChE,UAAMG,IAAU,KAAK,MAAMve,IAAI,GAAG,IAAI;AACtC,IAAAqe,EAAQ,KAAK,EAAE,OAAOE,GAAS,OAAO,GAAGA,CAAO,MAAM;AAAA,EACxD;AACA,SAAOF;AACT;AASO,SAASG,GAAoBf,GAAM;AACxC,SAAIA,KAAQ,IAAU,IAClBA,KAAQ,IAAU,IAClBA,KAAQ,IAAU,IAClBA,KAAQ,IAAU,KACf;AACT;AAQO,SAASgB,GAAoB/D,GAAO;AACzC,SAAO,QAAQ,OAAOA,CAAK,EAAE,SAAS,GAAG,GAAG,CAAC;AAC/C;AA2BO,SAASgE,GACdxnB,GACAynB,GACAC,GACAC,GACAC,IAAW,eACXC,IAAcN,IACd;AACA,QAAMO,KAASJ,KAAgB,IAAI,QAAQ,OAAO,EAAE,GAC9CK,KAASJ,KAAgB,IAAI,QAAQ,OAAO,EAAE;AACpD,SAAOF,EAAM,IAAI,CAACO,GAAMxE,MAAU;AAChC,UAAMyE,IAAWD,EAAK,YAAYH,EAAYrE,CAAK,GAC7C0E,IAAWF,EAAK,YAAYJ;AAClC,WAAO;AAAA,MACL,SAAY5nB;AAAA,MACZ,YAAYgoB,EAAK;AAAA,MACjB,UAAYA,EAAK;AAAA,MACjB,WAAYE;AAAA,MACZ,WAAY,GAAGJ,CAAK,IAAIG,CAAQ;AAAA,MAChC,UAAU;AAAA,QACR,OAAO,GAAGH,CAAK,IAAIG,CAAQ;AAAA,QAC3B,MAAO,GAAGF,CAAI,IAAIE,CAAQ;AAAA,MAClC;AAAA,IACA;AAAA,EACE,CAAC;AACH;AAcO,SAASE,GAAsBC,GAAe7B,GAAM8B,IAAqBjC,IAAuB;AACrG,QAAMkC,IAAQ/B,IAAO;AACrB,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM6B,IAAgBC,IAAqBC,CAAK,CAAC;AAC3E;AC1MA,MAAMC,KAAY,GACZC,KAAc,MACdC,KAAY,MACZC,KAAY;AAqBX,SAASC,GAAe;AAAA,EAC7B,QAAA9B,IAAS,CAAA;AAAA,EACT,QAAA7mB,IAAS;AAAA,EACT,aAAA4oB,IAAc;AAAA,EACd,WAAWC;AAAA,EACX,mBAAAC;AACF,GAAG;AAID,QAAM,CAACC,GAAmBC,CAAoB,IAAIrJ,EAAS,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,EAAA,CAAG,GAC/EsJ,IAAYJ,KAAuBE,GACnCG,IAAe9J,GAAO6J,CAAS;AACrC,EAAAC,EAAa,UAAUD;AAIvB,QAAME,IAAeC;AAAA,IACnB,CAACC,MAAY;AACX,YAAMnY,IAAO,OAAOmY,KAAY,aAAaA,EAAQH,EAAa,OAAO,IAAIG;AAC7E,MAAIP,IACFA,EAAkB5X,CAAI,IAEtB8X,EAAqB9X,CAAI;AAAA,IAE7B;AAAA,IACA,CAAC4X,CAAiB;AAAA,EAAA,GAGd,CAACQ,GAAUC,CAAW,IAAI5J,EAAS,EAAK,GACxC6J,IAAapK,GAAO,IAAI,GAExBqK,IAAcrK,GAAO,IAAI,GAIzB3e,IAASsiB,GAAQ,MAAM6D,GAAkBC,CAAM,GAAG,CAACA,CAAM,CAAC,GAC1DngB,IAAUqc,GAAQ,MAAM+D,GAAiBrmB,CAAM,GAAG,CAACA,CAAM,CAAC,GAI1DipB,IAAW3G,GAAQ,MAAM;AAC7B,UAAM9F,wBAAW,IAAA;AACjB,eAAWpU,KAAKge,GAAQ;AACtB,YAAMxnB,IAAMwJ,EAAE,aAAa,QAAQA,EAAE,cAAc,KAAK,OAAOA,EAAE,SAAS,IAAI;AAC9E,MAAAoU,EAAK,IAAI5d,CAAG;AAAA,IACd;AACA,WAAO,CAAC,GAAG4d,CAAI;AAAA,EACjB,GAAG,CAAC4J,CAAM,CAAC,GAEL,EAAE,UAAAI,GAAU,UAAAphB,EAAA,IAAakd,GAAQ,MAAM;AAC3C,QAAI,CAACtiB,EAAO,OAAQ,QAAO,EAAE,UAAU,GAAG,UAAU,EAAA;AACpD,UAAMkpB,IAAQlpB,EAAO,IAAI,CAACoI,MAAMA,EAAE,YAAYA,EAAE,cAAc,CAAC;AAC/D,WAAO;AAAA,MACL,UAAUpI,EAAO,CAAC,EAAE,cAAc;AAAA,MAClC,UAAU,KAAK,IAAI,GAAGkpB,CAAK;AAAA,IAAA;AAAA,EAE/B,GAAG,CAAClpB,CAAM,CAAC,GAELmpB,IAAkBxD,KAAwBmC,KAAa,GAEvDsB,IAAc9G;AAAA,IAClB,MAAM,KAAK,IAAI,GAAG,KAAK,OAAOld,IAAWohB,KAAY2C,CAAc,CAAC;AAAA,IACpE,CAAC3C,GAAUphB,GAAU+jB,CAAc;AAAA,EAAA,GAG/BzC,IAAUpE,GAAQ,MAAM;AAC5B,UAAM5gB,IAAWmlB,GAAoBiB,EAAS;AAC9C,WAAOvB,GAAkBC,GAAUphB,GAAU1D,CAAQ;AAAA,EACvD,GAAG,CAAC8kB,GAAUphB,CAAQ,CAAC,GAEjBikB,IAAc,MAAMvB,KAAa,GAIjCwB,IAAUhH;AAAA,IACd,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM6F,IAAcK,EAAU,KAAK,CAAC,CAAC;AAAA,IACzE,CAACL,GAAaK,EAAU,KAAK;AAAA,EAAA,GAKzBe,IAAcZ,GAAY,CAAC7K,MAAM;AACrC,IAAAA,EAAE,eAAA;AACF,UAAM0L,IAAS1L,EAAE,SAAS,IAAIiK,KAAc,IAAIA,IAC1C0B,IAAOT,EAAY,QAAQ,sBAAA,GAC3BU,IAAK5L,EAAE,UAAU2L,EAAK,MACtBE,IAAK7L,EAAE,UAAU2L,EAAK;AAE5B,IAAAf,EAAa,CAAC1hB,MAAS;AACrB,YAAM4iB,IAAW,KAAK,IAAI5B,IAAW,KAAK,IAAIC,IAAWjhB,EAAK,QAAQwiB,CAAM,CAAC,GACvEK,IAAQD,IAAW5iB,EAAK;AAC9B,aAAO;AAAA,QACL,OAAO4iB;AAAA,QACP,IAAIF,KAAMA,IAAK1iB,EAAK,MAAM6iB;AAAA,QAC1B,IAAIF,KAAMA,IAAK3iB,EAAK,MAAM6iB;AAAA,MAAA;AAAA,IAE9B,CAAC;AAAA,EACH,GAAG,CAACnB,CAAY,CAAC;AAEjB,EAAAjJ,GAAU,MAAM;AACd,UAAMqK,IAAKd,EAAY;AACvB,QAAKc;AACL,aAAAA,EAAG,iBAAiB,SAASP,GAAa,EAAE,SAAS,IAAO,GACrD,MAAMO,EAAG,oBAAoB,SAASP,CAAW;AAAA,EAC1D,GAAG,CAACA,CAAW,CAAC;AAIhB,QAAMQ,IAAkBpB,GAAY,CAAC7K,MAAM;AACzC,IAAIA,EAAE,WAAW,MACjBA,EAAE,eAAA,GACFiL,EAAW,UAAU;AAAA,MACnB,GAAGjL,EAAE;AAAA,MACL,GAAGA,EAAE;AAAA,MACL,IAAI2K,EAAa,QAAQ;AAAA,MACzB,IAAIA,EAAa,QAAQ;AAAA,IAAA,GAE3BK,EAAY,EAAI;AAAA,EAClB,GAAG,CAAA,CAAE;AAEL,SAAArJ,GAAU,MAAM;AACd,UAAMuK,IAAkB,CAAClM,MAAM;AAC7B,UAAI,CAACiL,EAAW,QAAS;AAGzB,YAAM,EAAE,IAAAkB,GAAI,IAAAC,GAAI,GAAAnf,GAAG,EAAA,IAAMge,EAAW;AACpC,MAAAL,EAAa,CAAC1hB,OAAU;AAAA,QACtB,GAAGA;AAAA,QACH,IAAIijB,KAAMnM,EAAE,UAAU/S;AAAA,QACtB,IAAImf,KAAMpM,EAAE,UAAU;AAAA,MAAA,EACtB;AAAA,IACJ,GACMqM,IAAgB,MAAM;AAC1B,MAAApB,EAAW,UAAU,MACrBD,EAAY,EAAK;AAAA,IACnB;AACA,kBAAO,iBAAiB,aAAakB,CAAe,GACpD,OAAO,iBAAiB,WAAWG,CAAa,GACzC,MAAM;AACX,aAAO,oBAAoB,aAAaH,CAAe,GACvD,OAAO,oBAAoB,WAAWG,CAAa;AAAA,IACrD;AAAA,EACF,GAAG,CAACzB,CAAY,CAAC,GAKf,gBAAA7K,EAAC,OAAA,EAAI,WAAU,oBAEb,UAAA;AAAA,IAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,uBACZ,UAAA;AAAA,MAAAte,KAAU,gBAAAwe,EAAC,QAAA,EAAK,WAAU,sBAAsB,UAAAxe,GAAO;AAAA,MACxD,gBAAAse,EAAC,QAAA,EAAK,WAAU,yBACb,UAAA;AAAA,QAAA,KAAK,MAAM2K,EAAU,QAAQ,GAAG;AAAA,QAAE;AAAA,MAAA,GACrC;AAAA,MACA,gBAAAzK;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS,MAAM2K,EAAa,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG;AAAA,UACtD,cAAW;AAAA,UACX,OAAM;AAAA,UACP,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAED,GACF;AAAA,IAGCtC,EAAO,SAAS,KACf,gBAAAvI,EAAC,OAAA,EAAI,WAAU,0BACb,UAAA;AAAA,MAAA,gBAAAE,EAAC,OAAA,EAAI,WAAU,0BAAA,CAA0B;AAAA,MACxCkL,EAAS,IAAI,CAACnrB,MACb,gBAAAigB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,WAAU;AAAA,UACV,OAAO,EAAE,OAAOsL,EAAA;AAAA,UAEf,UAAAvrB;AAAA,QAAA;AAAA,QAJIA;AAAA,MAAA,CAMR;AAAA,IAAA,GACH;AAAA,IAIDsoB,EAAO,WAAW,IACjB,gBAAArI,EAAC,SAAI,WAAU,oBAAmB,mCAAqB,IAEvD,gBAAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW,oBAAoB8K,IAAW,iBAAiB,EAAE;AAAA,QAC7D,KAAKG;AAAA,QACL,aAAae;AAAA,QAEb,UAAA,gBAAAlM;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,QAAQuL;AAAA,cACR,WAAW,aAAaZ,EAAU,EAAE,OAAOA,EAAU,EAAE,aAAaA,EAAU,KAAK;AAAA,cACnF,iBAAiB;AAAA,YAAA;AAAA,YAInB,UAAA;AAAA,cAAA,gBAAAzK;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,EAAE,QAAQqL,EAAA;AAAA,kBAEhB,YAAQ,IAAI,CAAC,EAAE,OAAAhY,GAAO,OAAAlD,QACrB,gBAAA6P;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBAEC,WAAU;AAAA,sBACV,OAAO;AAAA,wBACL,KAAK,KAAK,OAAO3M,IAAQoV,KAAY2C,CAAc;AAAA,sBAAA;AAAA,sBAGpD,UAAAjb;AAAA,oBAAA;AAAA,oBANIkD;AAAA,kBAAA,CAQR;AAAA,gBAAA;AAAA,cAAA;AAAA,cAIF6X,EAAS,IAAI,CAACmB,MACb,gBAAArM;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEC,WAAU;AAAA,kBACV,OAAO,EAAE,QAAQqL,GAAa,OAAOC,EAAA;AAAA,kBAEpC,UAAApjB,EAAQmkB,CAAO,EAAE,IAAI,CAACvE,MAAU;AAC/B,0BAAMwE,IAAYxE,EAAM,cAAc,GAChCyE,IAAUzE,EAAM,YAAYwE,GAC5BxG,IAAM,KAAK;AAAA,uBACdwG,IAAY7D,KAAY2C;AAAA,oBAAA,GAErBrJ,IAAS,KAAK;AAAA,sBAClB;AAAA,sBACA,KAAK,OAAOwK,IAAUD,KAAalB,CAAc;AAAA,oBAAA,GAE7CoB,IAAM3E,GAAkBC,GAAOyD,CAAO;AAE5C,2BACE,gBAAAzL;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBAEC,WAAU;AAAA,wBACV,OAAO,EAAE,KAAAgG,GAAK,QAAA/D,GAAQ,OAAOuJ,EAAA;AAAA,wBAC7B,OAAO,GAAGgB,CAAS,IAAIC,CAAO;AAAA,wBAE7B,UAAA;AAAA,0BAAAC,IACC,gBAAAxM;AAAA,4BAAC;AAAA,4BAAA;AAAA,8BACC,KAAAwM;AAAA,8BACA,KAAK,QAAQF,CAAS,IAAIC,CAAO;AAAA,8BACjC,SAAQ;AAAA,4BAAA;AAAA,0BAAA,IAGV,gBAAAvM,EAAC,OAAA,EAAI,WAAU,sBAAA,CAAsB;AAAA,0BAEtC+B,KAAU,MACT,gBAAAjC,EAAC,QAAA,EAAK,WAAU,yBACb,UAAA;AAAA,4BAAAwM;AAAA,4BAAU;AAAA,4BAAEC;AAAA,4BAAQ;AAAA,0BAAA,EAAA,CACvB;AAAA,wBAAA;AAAA,sBAAA;AAAA,sBAjBG,GAAGzE,EAAM,WAAW,EAAE,IAAIwE,CAAS,IAAIC,CAAO,IAAIF,CAAO;AAAA,oBAAA;AAAA,kBAqBpE,CAAC;AAAA,gBAAA;AAAA,gBAvCIA;AAAA,cAAA,CAyCR;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EACF,GAEJ;AAEJ;AChJO,SAASI,GAAgB;AAAA,EAC9B,QAAAjrB,IAAS;AAAA,EACT,OAAAynB,IAAQ,CAAA;AAAA,EACR,cAAAC,IAAe;AAAA,EACf,aAAAC,IAAc;AAAA,EACd,UAAUuD,IAAkB;AAAA,EAC5B,aAAArD,IAAcN;AAAA,EACd,aAAAqB,IAAc;AAAA,EACd,WAAAK;AAAA,EACA,mBAAAH;AACF,GAAG;AACD,QAAMjC,IAAS9D;AAAA,IACb,MAAMyE,GAAgBxnB,GAAQynB,GAAOC,GAAcC,GAAauD,GAAiBrD,CAAW;AAAA,IAC5F,CAAC7nB,GAAQynB,GAAOC,GAAcC,GAAauD,GAAiBrD,CAAW;AAAA,EAAA;AAGzE,SACE,gBAAArJ;AAAA,IAACmK;AAAA,IAAA;AAAA,MACC,QAAA9B;AAAA,MACA,QAAA7mB;AAAA,MACA,aAAA4oB;AAAA,MACA,WAAAK;AAAA,MACA,mBAAAH;AAAA,IAAA;AAAA,EAAA;AAGN;AC3JO,SAASqC,GAAwBC,GAAO;AAC7C,QAAMzS,IAAO,OAAOyS,KAAU,WAAW,KAAK,MAAMA,CAAK,IAAIA;AAE7D,MAAIzS,EAAK,mBAAmB;AAC1B,UAAM,IAAI;AAAA,MACR,+BAA+B,KAAK,UAAUA,EAAK,cAAc,CAAC;AAAA,IACxE;AAGE,MAAI,CAAC,MAAM,QAAQA,EAAK,MAAM;AAC5B,UAAM,IAAI,MAAM,gCAAgC;AAGlD,QAAM0S,IAAS1S,EAAK,OAAO,IAAI,CAACvB,GAAK7P,MAAM;AACzC,QAAI6P,EAAI,MAAM,KAAM,OAAM,IAAI,MAAM,kBAAkB7P,CAAC,kCAAkC;AACzF,QAAI6P,EAAI,QAAQ,KAAM,OAAM,IAAI,MAAM,UAAUA,EAAI,EAAE,qCAAqC;AAC3F,QAAI,CAAC,MAAM,QAAQA,EAAI,QAAQ,EAAG,OAAM,IAAI,MAAM,UAAUA,EAAI,EAAE,yCAAyC;AAC3G,QAAI,CAAC,MAAM,QAAQA,EAAI,SAAS,EAAG,OAAM,IAAI,MAAM,UAAUA,EAAI,EAAE,0CAA0C;AAE7G,WAAO;AAAA,MACL,IAAIA,EAAI;AAAA,MACR,MAAMA,EAAI;AAAA,MACV,UAAUA,EAAI;AAAA,MACd,WAAWA,EAAI;AAAA,MACf,YAAYA,EAAI,cAAc,CAAA;AAAA,MAC9B,UAAUA,EAAI,YAAY,CAAA;AAAA,IAChC;AAAA,EACE,CAAC;AAED,SAAO;AAAA,IACL,gBAAgBuB,EAAK;AAAA,IACrB,OAAOA,EAAK,SAAS;AAAA,IACrB,QAAA0S;AAAA,EACJ;AACA;AAYO,SAASC,GAA0BC,GAAO;AAC/C,QAAMC,IAAW,IAAIC,EAAM,eAAc,GAGnCC,IAAe,IAAI,aAAaH,EAAM,SAAS,SAAS,CAAC;AAC/D,EAAAA,EAAM,SAAS,QAAQ,CAAC,CAAC/f,GAAGC,GAAGC,CAAC,GAAGnE,MAAM;AACvC,IAAAmkB,EAAankB,IAAI,CAAC,IAAIiE,GACtBkgB,EAAankB,IAAI,IAAI,CAAC,IAAIkE,GAC1BigB,EAAankB,IAAI,IAAI,CAAC,IAAImE;AAAA,EAC5B,CAAC,GACD8f,EAAS,aAAa,YAAY,IAAIC,EAAM,gBAAgBC,GAAc,CAAC,CAAC;AAG5E,QAAMC,IAAY,IAAI,YAAYJ,EAAM,UAAU,SAAS,CAAC;AAC5D,SAAAA,EAAM,UAAU,QAAQ,CAAC,CAAC7qB,GAAGC,GAAGuF,CAAC,GAAGqB,MAAM;AACxC,IAAAokB,EAAUpkB,IAAI,CAAC,IAAI7G,GACnBirB,EAAUpkB,IAAI,IAAI,CAAC,IAAI5G,GACvBgrB,EAAUpkB,IAAI,IAAI,CAAC,IAAIrB;AAAA,EACzB,CAAC,GACDslB,EAAS,SAAS,IAAIC,EAAM,gBAAgBE,GAAW,CAAC,CAAC,GAElDH;AACT;AAeO,SAASI,GAAsBC,GAAOC,GAAU9gB,IAAU,CAAA,GAAI;AACnE,QAAM,EAAE,gBAAA+gB,IAAiB,EAAG,IAAK/gB,GAC3BgP,IAAQ,IAAIyR,EAAM,MAAK;AAE7B,SAAAK,EAAS,OAAO,QAAQ,CAACP,MAAU;;AACjC,UAAMC,IAAWF,GAA0BC,CAAK,GAE1CxR,MAAQ/Y,IAAAuqB,EAAM,aAAN,gBAAAvqB,EAAgB,UAAS,WACjC6kB,MAAUzf,IAAAmlB,EAAM,aAAN,gBAAAnlB,EAAgB,YAAW2lB,GACrCC,IAAcnG,IAAU,GAExBoG,IAAW,IAAIR,EAAM,qBAAqB;AAAA,MAC9C,OAAO,IAAIA,EAAM,MAAM1R,CAAK;AAAA,MAC5B,SAAA8L;AAAA,MACA,aAAAmG;AAAA,MACA,MAAMP,EAAM;AAAA,MACZ,aAAa;AAAA,IACnB,CAAK,GAEKS,IAAO,IAAIT,EAAM,KAAKD,GAAUS,CAAQ;AAC9C,IAAAC,EAAK,WAAW;AAAA,MACd,IAAIX,EAAM;AAAA,MACV,YAAYA,EAAM;AAAA,IACxB;AAGI,UAAMY,IAAU,IAAIV,EAAM,cAAcD,GAAU,EAAE,GAC9CY,IAAU,IAAIX,EAAM,kBAAkB,EAAE,OAAO,WAAW,WAAW,GAAG,GACxEY,IAAY,IAAIZ,EAAM,aAAaU,GAASC,CAAO;AACzD,IAAAC,EAAU,UAAU,IACpBH,EAAK,IAAIG,CAAS,GAElBrS,EAAM,IAAIkS,CAAI;AAAA,EAChB,CAAC,GAEDL,EAAM,IAAI7R,CAAK,GACRA;AACT;"}
1
+ {"version":3,"file":"baselode.js","sources":["../src/data/keying.js","../src/data/assayFieldSets.js","../src/data/assayLoader.js","../src/data/csvRowUtils.js","../src/data/traceGridConfig.js","../src/data/assayDataLoader.js","../src/data/desurvey.js","../src/data/desurveyMethods.js","../src/data/drillholeLoader.js","../src/data/datasetLoader.js","../src/data/structuralLoader.js","../src/data/unifiedLoader.js","../src/data/intercepts.js","../src/data/intervals.js","../src/data/validateDrillholeDb.js","../src/data/geophysicsLoader.js","../src/data/lasLoader.js","../src/viz/structuralViz.js","../src/viz/tracePlotState.js","../src/viz/TracePlot.jsx","../src/viz/useDrillholeTraceGrid.jsx","../src/viz/view2d.js","../src/viz/view3dPayload.js","../src/viz/Baselode3DControls.jsx","../src/viz/BlockModelWidget.jsx","../src/viz/corePhotoViz.js","../src/viz/CorePhotoTable.jsx","../src/viz/CorePhotoViewer.jsx","../src/grade_blocks/gradeBlockLoader.js"],"sourcesContent":["/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { _COLUMN_LOOKUP, DEFAULT_COLUMN_MAP, HOLE_ID } from './datamodel.js';\n\n/**\n * Normalize a field name to lowercase with underscores\n * @param {string} name - The field name to normalize\n * @returns {string} - The normalized field name\n */\nexport function normalizeFieldName(name) {\n return (name || '').toString().trim().toLowerCase().replace(/\\s+/g, '_');\n}\n\n/**\n * Standardize column names in a row object using the baselode data model\n * @param {Object} row - The row object with arbitrary column names\n * @param {Object} [columnMap] - Optional column map to use (defaults to DEFAULT_COLUMN_MAP)\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Object} - The row object with standardized column names\n */\nexport function standardizeColumns(row, columnMap = null, sourceColumnMap = null) {\n const lookup = { ..._COLUMN_LOOKUP };\n \n // Add user-provided column mappings\n if (sourceColumnMap) {\n for (const [rawName, expectedName] of Object.entries(sourceColumnMap)) {\n if (rawName != null && expectedName != null) {\n const normalizedKey = normalizeFieldName(rawName);\n const normalizedValue = normalizeFieldName(expectedName);\n lookup[normalizedKey] = normalizedValue;\n }\n }\n }\n\n const renamed = {};\n for (const [col, value] of Object.entries(row)) {\n const key = normalizeFieldName(col);\n const mapped = lookup[key] || key;\n renamed[mapped] = value;\n }\n\n return renamed;\n}\n\n/**\n * Standardize an array of rows (e.g., from CSV parsing)\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map to use\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeRowArray(rows, columnMap = null, sourceColumnMap = null) {\n return rows.map(row => standardizeColumns(row, columnMap, sourceColumnMap));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport const ASSAY_NON_VALUE_FIELDS = new Set([\n 'hole_id',\n 'holeid',\n 'id',\n 'holeId',\n 'project_code',\n 'project',\n 'latitude',\n 'longitude',\n 'lat',\n 'lng',\n 'elevation',\n 'dip',\n 'azimuth',\n 'holetype',\n 'shape',\n 'anumber',\n 'collarid',\n 'companyholeid',\n 'company_hole_id',\n 'samp_from',\n 'samp_to',\n 'sample_from',\n 'sample_to',\n 'from',\n 'to',\n 'depth_from',\n 'depth_to',\n 'fromdepth',\n 'todepth',\n 'comment',\n 'z'\n]);","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, PROJECT_ID } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to use standardized column names\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Extract hole ID from a normalized row\n * @private\n * @param {Object} row - Normalized row object\n * @returns {{holeId: string}} Object containing hole ID\n */\nfunction extractIdFields(row) {\n const holeId = row[HOLE_ID];\n return { holeId };\n}\n\n/**\n * Extract and validate assay interval data from a row\n * @private\n * @param {Object} row - Normalized row object\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object|null} Interval object or null if invalid\n */\nfunction extractInterval(row, sourceColumnMap = null) {\n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n if (!holeId) return null;\n\n const project = row[PROJECT_ID] || row.project || row.project_code;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return null;\n\n return {\n holeId,\n project,\n from,\n to,\n ...row\n };\n}\n\n/**\n * Convert array of intervals to a hole object with points\n * @private\n * @param {string} holeId - Hole identifier\n * @param {Array<Object>} intervals - Array of interval objects\n * @returns {{id: string, project: string, points: Array<Object>}} Hole object\n */\nfunction intervalsToHole(holeId, intervals) {\n const sorted = intervals.sort((a, b) => a.from - b.from);\n const points = [];\n sorted.forEach((iv) => {\n const { from, to, project, ...rest } = iv;\n const pointData = {\n z: from,\n from,\n to,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n ...rest\n };\n points.push(pointData);\n points.push({ ...pointData, z: to });\n });\n return { id: holeId, project: sorted[0]?.project, points };\n}\n\n/**\n * Parse assay CSV to extract unique hole IDs (quick pass, no interval data)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<string>>} Array of unique hole IDs\n */\nexport function parseAssayHoleIds(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const holeIds = new Set();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const hid = row[HOLE_ID];\n if (hid !== undefined && `${hid}`.trim() !== '') {\n holeIds.add(`${hid}`.trim());\n }\n },\n complete: () => resolve(Array.from(holeIds)),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIds', error))\n });\n });\n}\n\n/**\n * Check if a row has at least one non-null assay value\n * @private\n * @param {Object} row - Normalized row object\n * @returns {boolean} True if row contains assay data\n */\nfunction hasAssayValue(row) {\n return Object.entries(row || {}).some(([k, v]) => {\n if (ASSAY_NON_VALUE_FIELDS.has(k)) return false;\n if (v === undefined || v === null) return false;\n if (typeof v === 'string' && v.trim() === '') return false;\n return true;\n });\n}\n\n/**\n * Parse assay CSV to extract hole IDs that have at least one assay value (quick pass)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<{holeId: string}>>} Array of objects with hole IDs that have assay data\n */\nexport function parseAssayHoleIdsWithAssays(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const byHole = new Map();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n if (!hasAssayValue(row)) return;\n const ids = extractIdFields(row);\n const hid = ids.holeId;\n if (hid !== undefined && `${hid}`.trim() !== '') {\n const key = `${hid}`.trim();\n if (!byHole.has(key)) {\n byHole.set(key, {\n holeId: key\n });\n }\n }\n },\n complete: () => resolve(Array.from(byHole.values())),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIdsWithAssays', error))\n });\n });\n}\n\n/**\n * Parse assay CSV for a single hole's intervals\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to extract\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object|null>} Hole object with intervals or null if not found\n */\nexport function parseAssayHole(file, holeId, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const wanted = `${holeId}`.trim();\n if (!wanted) {\n reject(withDataErrorContext('parseAssayHole', new Error('Missing hole id')));\n return;\n }\n const intervals = [];\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (`${interval.holeId}`.trim() !== wanted) return;\n intervals.push(interval);\n },\n complete: () => {\n if (!intervals.length) {\n resolve(null);\n return;\n }\n const hole = intervalsToHole(wanted, intervals);\n resolve(hole);\n },\n error: (error) => reject(withDataErrorContext('parseAssayHole', error))\n });\n });\n}\n\n/**\n * Parse complete assay CSV file with all holes and intervals (eager load)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<{holes: Array<Object>}>} Object containing array of all holes with intervals\n */\nexport function parseAssaysCSV(file, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow) => {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (!byHole.has(interval.holeId)) byHole.set(interval.holeId, []);\n byHole.get(interval.holeId).push(interval);\n });\n\n const holes = Array.from(byHole.entries()).map(([hid, intervals]) => intervalsToHole(hid, intervals));\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseAssaysCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { normalizeFieldName, standardizeColumns } from './keying.js';\n\n/**\n * Normalize a CSV row by converting all keys to lowercase with underscores\n * @deprecated Use standardizeColumns from keying.js instead for full data model support\n */\nexport function normalizeCsvRow(row = {}) {\n const normalized = {};\n Object.entries(row || {}).forEach(([key, value]) => {\n if (!key) return;\n normalized[normalizeFieldName(key)] = value;\n });\n return normalized;\n}\n\n/**\n * Pick the first present value from a list of keys in a normalized row\n * @param {Object} normalized - The normalized row object\n * @param {Array<string>} keys - Array of keys to check\n * @param {*} fallback - Fallback value if none found\n * @returns {*} - The first present value or fallback\n */\nexport function pickFirstPresent(normalized = {}, keys = [], fallback) {\n for (const key of keys) {\n const value = normalized[key];\n if (value !== undefined && value !== null && `${value}`.trim() !== '') {\n return value;\n }\n }\n return fallback;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default number of trace plots in a grid */\nexport const DEFAULT_TRACE_PLOT_COUNT = 4;\n\n/**\n * Reorder hole IDs to place a focused hole first\n * @param {Array<string>} ids - Array of hole IDs\n * @param {string} focusId - Hole ID to place first\n * @returns {Array<string>} Reordered array with focusId first if found\n */\nexport function reorderHoleIds(ids = [], focusId = '') {\n if (!ids.length) return [];\n if (!focusId) return ids;\n const matchIdx = ids.findIndex((id) => id === focusId);\n if (matchIdx === -1) return ids;\n const selected = ids[matchIdx];\n const rest = ids.filter((_, idx) => idx !== matchIdx);\n return [selected, ...rest];\n}\n\n/**\n * Determine appropriate chart type for a property.\n * Comment columns always use 'comment'; categorical → 'categorical'; numeric → requested or default.\n *\n * @param {Object} options - Configuration options\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Requested chart type\n * @param {Array<string>} options.categoricalProps - List of categorical property names\n * @param {Array<string>} options.commentProps - List of comment property names\n * @param {string} options.numericDefaultChartType - Default chart type for numeric properties\n * @returns {string} Coerced chart type ('comment', 'categorical', 'line', 'markers+line', etc.)\n */\nexport function coerceChartTypeForProperty({\n property = '',\n chartType = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n if (!property) return chartType || numericDefaultChartType;\n if (commentProps.includes(property)) return 'comment';\n if (categoricalProps.includes(property)) return 'categorical';\n if (property === 'dip') return 'tadpole';\n if (!chartType || chartType === 'categorical' || chartType === 'comment' || chartType === 'tadpole') return numericDefaultChartType;\n return chartType;\n}\n\n/**\n * Build trace plot configuration objects for a grid of hole IDs\n * @param {Object} options - Configuration options\n * @param {Array<string>} options.holeIds - Array of hole IDs\n * @param {string} options.focusedHoleId - Hole ID to focus (place first)\n * @param {number} options.plotCount - Number of plots in grid\n * @param {string} options.defaultProp - Default property to display\n * @param {Array<string>} options.categoricalProps - List of categorical properties\n * @param {Array<string>} options.commentProps - List of comment properties\n * @param {string} options.numericDefaultChartType - Default chart type for numeric props\n * @returns {Array<{holeId: string, property: string, chartType: string}>} Array of trace configurations\n */\nexport function buildTraceConfigsForHoleIds({\n holeIds = [],\n focusedHoleId = '',\n plotCount = DEFAULT_TRACE_PLOT_COUNT,\n defaultProp = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n const ordered = reorderHoleIds(holeIds, focusedHoleId);\n return Array.from({ length: plotCount }).map((_, idx) => {\n const holeId = ordered[idx] || holeIds[idx] || '';\n const chartType = coerceChartTypeForProperty({\n property: defaultProp,\n chartType: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType\n });\n return {\n holeId,\n property: defaultProp,\n chartType\n };\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { parseAssayHole, parseAssayHoleIdsWithAssays, parseAssaysCSV } from './assayLoader.js';\nimport { buildTraceConfigsForHoleIds, reorderHoleIds } from './traceGridConfig.js';\nimport { classifyColumns } from './columnMeta.js';\n\nexport { reorderHoleIds };\n\n/**\n * Derive property names from hole data using column display-type classification.\n *\n * @param {Array<Object>} holes - Array of hole objects with points containing assay data\n * @returns {{\n * numericProps: string[],\n * categoricalProps: string[],\n * commentProps: string[],\n * columnMeta: Object,\n * defaultProp: string\n * }} Property classification\n */\nexport function deriveAssayProps(holes = []) {\n const points = holes.flatMap((h) => h.points || []);\n const { numericCols, categoricalCols, commentCols, byType } = classifyColumns(points);\n\n const defaultProp = numericCols[0] || categoricalCols[0] || '';\n\n return {\n numericProps: numericCols,\n categoricalProps: categoricalCols,\n commentProps: commentCols,\n columnMeta: byType,\n defaultProp,\n };\n}\n\n/**\n * Load metadata (hole IDs) from an assay CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Array<{holeId: string}>>} Array of hole IDs with assay data\n */\nexport async function loadAssayMetadata(file, config = null) {\n const holeIds = await parseAssayHoleIdsWithAssays(file);\n return holeIds;\n}\n\n/**\n * Load assay intervals for a specific hole from CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to load\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Object|null>} Hole object with assay intervals or null if not found\n */\nexport async function loadAssayHole(file, holeId, config = null) {\n const hole = await parseAssayHole(file, holeId);\n return hole;\n}\n\n/**\n * Build complete assay state from hole data including property analysis and trace configs\n * @param {Array<Object>} holes - Array of hole objects with assay data\n * @param {string} focusedHoleId - Hole ID to focus on (will be first in trace configs)\n * @returns {Object|null} Assay state object with holes, properties, and trace configs\n */\nexport function buildAssayState(holes = [], focusedHoleId = '') {\n if (!holes.length) return null;\n const { numericProps, categoricalProps, commentProps, columnMeta, defaultProp } = deriveAssayProps(holes);\n const holeIds = holes.map((h) => h.id || h.holeId).filter(Boolean);\n const traceConfigs = buildTraceConfigsForHoleIds({\n holeIds,\n focusedHoleId,\n plotCount: 4,\n defaultProp,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'line'\n });\n return {\n holes,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n defaultProp,\n traceConfigs\n };\n}\n\n/**\n * Load and parse complete assay CSV file into state object\n * @param {File|Blob} file - Assay CSV file\n * @param {string} focusedHoleId - Hole ID to focus on\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object>} Complete assay state\n * @throws {Error} If no valid assay intervals found\n */\nexport async function loadAssayFile(file, focusedHoleId = '', sourceColumnMap = null) {\n const { holes } = await parseAssaysCSV(file, sourceColumnMap);\n const state = buildAssayState(holes, focusedHoleId);\n if (!state) throw new Error('No valid assay intervals found.');\n return state;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, LATITUDE, LONGITUDE, AZIMUTH, DIP, DEPTH, PROJECT_ID } from './datamodel.js';\n\n/**\n * Parse survey CSV file containing downhole survey measurements\n * Expected columns: hole_id, depth, azimuth, dip\n * @param {File|Blob} file - Survey CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<Object>>} Array of normalized survey rows\n */\nexport function parseSurveyCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = results.data\n .map((row) => normalizeRow(row, sourceColumnMap))\n .filter((row) => row[HOLE_ID] && Number.isFinite(row[DEPTH]) && Number.isFinite(row[DIP]) && Number.isFinite(row[AZIMUTH]));\n resolve(rows);\n },\n error: (err) => reject(withDataErrorContext('parseSurveyCSV', err))\n });\n });\n}\n\n/**\n * Normalize a survey row to standardized field names\n * @private\n * @param {Object} row - Raw survey row\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object} Normalized row with standardized field names\n */\nfunction normalizeRow(row, sourceColumnMap = null) {\n const norm = standardizeColumns(row, null, sourceColumnMap);\n\n const holeId = norm[HOLE_ID];\n const project = norm[PROJECT_ID] || norm.project || norm.project_code;\n const lat = toNumber(norm[LATITUDE]);\n const lng = toNumber(norm[LONGITUDE]);\n const surveyDepth = toNumber(norm[DEPTH]);\n const dip = toNumber(norm[DIP]);\n const azimuth = toNumber(norm[AZIMUTH]);\n const maxDepth = toNumber(norm.maxdepth);\n\n return {\n raw: norm,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n [LATITUDE]: lat,\n [LONGITUDE]: lng,\n [DEPTH]: surveyDepth,\n [DIP]: dip,\n [AZIMUTH]: azimuth,\n maxdepth: maxDepth,\n // Legacy field names for backwards compatibility\n project_code: project,\n latitude: lat,\n longitude: lng,\n surveydepth: surveyDepth\n };\n}\n\n/**\n * Convert value to number, returning undefined if not finite\n * @private\n * @param {*} v - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nconst toNumber = (v) => {\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/**\n * Desurvey drillhole traces using minimum curvature method\n * Converts survey measurements (azimuth, dip, depth) to 3D coordinates (x, y, z)\n * @param {Array<Object>} collars - Array of collar objects with location data\n * @param {Array<Object>} surveys - Array of survey measurement objects\n * @returns {Array<{id: string, project: string, points: Array<{x: number, y: number, z: number, md: number, azimuth: number, dip: number, lat: number, lng: number}>, collar: Object}>} Array of desurveyed holes with 3D points\n */\nexport function desurveyTraces(collars, surveys) {\n const collarByKey = new Map();\n collars.forEach((c) => {\n const holeId = (c[HOLE_ID] || c.holeId || c.id || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!collarByKey.has(key)) {\n collarByKey.set(key, c);\n }\n });\n\n const refLat = collars[0]?.lat ?? collars[0]?.[LATITUDE] ?? 0;\n const refLng = collars[0]?.lng ?? collars[0]?.[LONGITUDE] ?? 0;\n const refMetersPerDegLat = 111132;\n const refMetersPerDegLon = 111320 * Math.cos((refLat * Math.PI) / 180);\n\n const grouped = new Map();\n surveys.forEach((s) => {\n const holeId = (s[HOLE_ID] || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(s);\n });\n\n const holes = [];\n grouped.forEach((stations, key) => {\n const collar = collarByKey.get(key);\n if (!collar) return;\n const sorted = stations\n .filter((s) => Number.isFinite(s[DEPTH] ?? s.surveydepth))\n .sort((a, b) => (a[DEPTH] ?? a.surveydepth) - (b[DEPTH] ?? b.surveydepth));\n if (!sorted.length) return;\n\n const lat0 = collar.lat ?? collar[LATITUDE];\n const lng0 = collar.lng ?? collar[LONGITUDE];\n const metersPerDegLat = 111132;\n const metersPerDegLon = 111320 * Math.cos((lat0 * Math.PI) / 180);\n const baseX = (lng0 - refLng) * refMetersPerDegLon;\n const baseY = (lat0 - refLat) * refMetersPerDegLat;\n\n const points = [];\n let accX = 0;\n let accY = 0;\n let accZ = 0;\n\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const prev = sorted[i - 1];\n const currDepth = curr[DEPTH] ?? curr.surveydepth;\n const currAzimuth = curr[AZIMUTH] ?? curr.azimuth;\n const currDip = curr[DIP] ?? curr.dip;\n \n if (!prev) {\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: 0,\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n continue;\n }\n\n const prevDepth = prev[DEPTH] ?? prev.surveydepth;\n const prevAzimuth = prev[AZIMUTH] ?? prev.azimuth;\n const prevDip = prev[DIP] ?? prev.dip;\n \n const deltaMD = currDepth - prevDepth;\n if (deltaMD <= 0) continue;\n\n const inc1 = toInclination(prevDip);\n const inc2 = toInclination(currDip);\n const az1 = degToRad(prevAzimuth);\n const az2 = degToRad(currAzimuth);\n\n const beta = Math.acos(\n Math.sin(inc1) * Math.sin(inc2) * Math.cos(az1 - az2) +\n Math.cos(inc1) * Math.cos(inc2)\n );\n\n const rf = beta > 1e-6 ? (2 / beta) * Math.tan(beta / 2) : 1;\n\n const dx = 0.5 * deltaMD * (Math.sin(inc1) * Math.cos(az1) + Math.sin(inc2) * Math.cos(az2)) * rf;\n const dy = 0.5 * deltaMD * (Math.sin(inc1) * Math.sin(az1) + Math.sin(inc2) * Math.sin(az2)) * rf;\n const dzDown = 0.5 * deltaMD * (Math.cos(inc1) + Math.cos(inc2)) * rf; // down is positive\n\n accX += dx;\n accY += dy;\n accZ += dzDown;\n\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: -accZ, // render with z up; depth down\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n }\n\n // convert local meters back to approx lat/lon for map overlay if needed\n const pointsWithGeo = points.map((p) => ({\n ...p,\n lat: lat0 + (p.y / metersPerDegLat),\n lng: lng0 + (p.x / metersPerDegLon)\n }));\n\n holes.push({\n id: collar[HOLE_ID] || collar.holeId || key,\n project: collar[PROJECT_ID] || collar.project_id || collar.project || '',\n points: pointsWithGeo,\n collar\n });\n });\n\n return holes;\n}\n\n/**\n * Convert degrees to radians\n * @private\n * @param {number} d - Degrees\n * @returns {number} Radians\n */\nconst degToRad = (d) => (d * Math.PI) / 180;\n\n/**\n * Convert dip (negative downward from horizontal) to inclination from vertical\n * @private\n * @param {number} dipDeg - Dip in degrees (negative = downward)\n * @returns {number} Inclination in radians from vertical (0 = vertical down)\n */\nconst toInclination = (dipDeg) => {\n const val = Number(dipDeg);\n const incDeg = 90 + (Number.isFinite(val) ? val : 0); // dip -60 => inc 30\n const clamped = Math.min(180, Math.max(0, incDeg));\n return degToRad(clamped);\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { withDataErrorContext } from './dataErrorUtils.js';\n/**\n * Convert value to number with optional fallback\n * @private\n */function toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Normalize hole ID value to trimmed string\n * @private\n */\nfunction normalizeHoleIdValue(value) {\n if (value === undefined || value === null) return '';\n return `${value}`.trim();\n}\n\n/**\n * Canonicalize hole ID column across rows with varying column names\n * @private\n */\nfunction canonicalizeHoleIdRows(rows = [], holeIdCol = null) {\n const preferred = holeIdCol || 'hole_id';\n const candidates = [preferred, 'hole_id', 'holeId', 'id'];\n const resolved = candidates.find((col) => rows.some((row) => normalizeHoleIdValue(row?.[col])));\n if (!resolved) {\n throw withDataErrorContext('canonicalizeHoleIdRows', new Error(`hole id column '${preferred}' not found`));\n }\n return {\n aliasCol: resolved,\n rows: rows.map((row) => ({\n ...row,\n hole_id: normalizeHoleIdValue(row?.[resolved])\n }))\n };\n}\n\n/**\n * Convert degrees to radians\n * @private\n */\nfunction degToRad(angle) {\n return (Number(angle) * Math.PI) / 180;\n}\n\n/**\n * Calculate direction cosines from azimuth and dip\n * @private\n */\nfunction directionCosines(azimuth, dip) {\n const azRad = degToRad(azimuth);\n const dipRad = degToRad(dip);\n const ca = Math.cos(dipRad) * Math.sin(azRad);\n const cb = Math.cos(dipRad) * Math.cos(azRad);\n const cc = Math.sin(dipRad) * -1;\n return { ca, cb, cc };\n}\n\n/**\n * Calculate displacement vector for a survey segment using specified method\n * @private\n */\nfunction segmentDisplacement(deltaMd, az0, dip0, az1, dip1, method = 'minimum_curvature') {\n const dc0 = directionCosines(az0, dip0);\n const dc1 = directionCosines(az1, dip1);\n\n if (method === 'tangential') {\n return {\n dx: deltaMd * dc0.ca,\n dy: deltaMd * dc0.cb,\n dz: deltaMd * dc0.cc,\n azimuth: az0,\n dip: dip0\n };\n }\n\n if (method === 'balanced_tangential') {\n const azAvg = 0.5 * (az0 + az1);\n const dipAvg = 0.5 * (dip0 + dip1);\n const dcAvg = directionCosines(azAvg, dipAvg);\n return {\n dx: deltaMd * dcAvg.ca,\n dy: deltaMd * dcAvg.cb,\n dz: deltaMd * dcAvg.cc,\n azimuth: azAvg,\n dip: dipAvg\n };\n }\n\n const dot = (dc0.ca * dc1.ca) + (dc0.cb * dc1.cb) + (dc0.cc * dc1.cc);\n const dogleg = Math.acos(Math.max(-1, Math.min(1, dot)));\n const rf = dogleg > 1e-6 ? (2 * Math.tan(dogleg / 2)) / dogleg : 1;\n\n return {\n dx: 0.5 * deltaMd * (dc0.ca + dc1.ca) * rf,\n dy: 0.5 * deltaMd * (dc0.cb + dc1.cb) * rf,\n dz: 0.5 * deltaMd * (dc0.cc + dc1.cc) * rf,\n azimuth: az1,\n dip: dip1\n };\n}\n\nfunction desurvey(rowsCollars = [], rowsSurveys = [], options = {}) {\n const {\n step = 1,\n holeIdCol = null,\n method = 'minimum_curvature'\n } = options;\n\n const safeStep = Number.isFinite(Number(step)) && Number(step) > 0 ? Number(step) : 1;\n\n const collarsCanonical = canonicalizeHoleIdRows(rowsCollars, holeIdCol);\n const surveysCanonical = canonicalizeHoleIdRows(rowsSurveys, holeIdCol || collarsCanonical.aliasCol);\n\n if (!collarsCanonical.rows.length || !surveysCanonical.rows.length) return [];\n\n const collarsByHole = new Map();\n collarsCanonical.rows.forEach((row) => {\n if (!row.hole_id || collarsByHole.has(row.hole_id)) return;\n collarsByHole.set(row.hole_id, row);\n });\n\n const surveysByHole = new Map();\n surveysCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!surveysByHole.has(row.hole_id)) surveysByHole.set(row.hole_id, []);\n surveysByHole.get(row.hole_id).push(row);\n });\n\n const out = [];\n surveysByHole.forEach((stations, holeId) => {\n const collar = collarsByHole.get(holeId);\n if (!collar) return;\n\n const sorted = [...stations]\n .map((row) => ({\n ...row,\n from: toNumber(row.from),\n azimuth: toNumber(row.azimuth),\n dip: toNumber(row.dip)\n }))\n .filter((row) => Number.isFinite(row.from) && Number.isFinite(row.azimuth) && Number.isFinite(row.dip))\n .sort((a, b) => a.from - b.from);\n\n if (!sorted.length) return;\n\n let x = toNumber(collar.x, 0);\n let y = toNumber(collar.y, 0);\n let z = toNumber(collar.z, 0);\n let mdCursor = sorted[0].from;\n const azPrev = sorted[0].azimuth;\n const dipPrev = sorted[0].dip;\n\n const firstRecord = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: azPrev,\n dip: dipPrev\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n firstRecord[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(firstRecord);\n\n for (let idx = 0; idx < sorted.length - 1; idx += 1) {\n const s0 = sorted[idx];\n const s1 = sorted[idx + 1];\n const md0 = s0.from;\n const md1 = s1.from;\n const deltaMd = md1 - md0;\n if (deltaMd <= 0) continue;\n\n const segmentSteps = Math.max(1, Math.ceil(deltaMd / safeStep));\n const mdIncrement = deltaMd / segmentSteps;\n\n for (let stepIdx = 0; stepIdx < segmentSteps; stepIdx += 1) {\n mdCursor += mdIncrement;\n const weight = (mdCursor - md0) / deltaMd;\n const azInterp = s0.azimuth + weight * (s1.azimuth - s0.azimuth);\n const dipInterp = s0.dip + weight * (s1.dip - s0.dip);\n\n const disp = segmentDisplacement(mdIncrement, s0.azimuth, s0.dip, s1.azimuth, s1.dip, method);\n x += disp.dx;\n y += disp.dy;\n z += disp.dz;\n\n const record = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: method === 'minimum_curvature' ? azInterp : disp.azimuth,\n dip: method === 'minimum_curvature' ? dipInterp : disp.dip\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n record[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(record);\n }\n }\n });\n\n return out;\n}\n\n/**\n * Desurvey drillholes using minimum curvature method\n * @param {Array<Object>} collars - Collar data with hole_id, lat, lng\n * @param {Array<Object>} surveys - Survey data with hole_id, depth, azimuth, dip\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points with x, y, z, md coordinates\n */\nexport function minimumCurvatureDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'minimum_curvature' });\n}\n\n/**\n * Desurvey drillholes using tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function tangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'tangential' });\n}\n\n/**\n * Desurvey drillholes using balanced tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function balancedTangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'balanced_tangential' });\n}\n\n/**\n * Build desurveyed traces using minimum curvature method (alias for minimumCurvatureDesurvey)\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function buildTraces(collars, surveys, options = {}) {\n return minimumCurvatureDesurvey(collars, surveys, options);\n}\n\n/**\n * Find nearest trace point by measured depth\n * @private\n */\nfunction nearestByMeasuredDepth(traceRows, midMd) {\n if (!traceRows.length || !Number.isFinite(midMd)) return null;\n let best = null;\n let bestDist = Infinity;\n for (let i = 0; i < traceRows.length; i += 1) {\n const row = traceRows[i];\n const md = toNumber(row.md);\n if (!Number.isFinite(md)) continue;\n const dist = Math.abs(md - midMd);\n if (dist < bestDist) {\n bestDist = dist;\n best = row;\n }\n }\n return best;\n}\n\n/**\n * Attach 3D position data from desurveyed traces to assay intervals\n * Finds nearest trace point by mid-depth of each assay interval\n * @param {Array<Object>} assays - Assay data with hole_id, from, to\n * @param {Array<Object>} traces - Desurveyed trace data with hole_id, md, x, y, z\n * @param {Object} options - Options\n * @param {string} options.holeIdCol - Hole ID column name (default: 'hole_id')\n * @returns {Array<Object>} Assay rows enriched with x, y, z, md, azimuth, dip from nearest trace point\n */\nexport function attachAssayPositions(assays = [], traces = [], options = {}) {\n const holeIdCol = options.holeIdCol || 'hole_id';\n const assaysCanonical = canonicalizeHoleIdRows(assays, holeIdCol);\n const tracesCanonical = canonicalizeHoleIdRows(traces, holeIdCol);\n\n if (!assaysCanonical.rows.length || !tracesCanonical.rows.length) return [...assaysCanonical.rows];\n\n const tracesByHole = new Map();\n tracesCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!tracesByHole.has(row.hole_id)) tracesByHole.set(row.hole_id, []);\n tracesByHole.get(row.hole_id).push(row);\n });\n tracesByHole.forEach((rows, holeId) => {\n tracesByHole.set(holeId, [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0)));\n });\n\n return assaysCanonical.rows.map((assay) => {\n const from = toNumber(assay.from);\n const to = toNumber(assay.to);\n const midMd = Number.isFinite(from) && Number.isFinite(to) ? 0.5 * (from + to) : undefined;\n if (!assay.hole_id || !Number.isFinite(midMd)) return { ...assay };\n\n const nearest = nearestByMeasuredDepth(tracesByHole.get(assay.hole_id) || [], midMd);\n if (!nearest) return { ...assay };\n\n const merged = { ...assay };\n ['md', 'x', 'y', 'z', 'azimuth', 'dip'].forEach((key) => {\n if (nearest[key] === undefined) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = nearest[key];\n } else {\n merged[key] = nearest[key];\n }\n });\n return merged;\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, EASTING, NORTHING, ELEVATION } from './datamodel.js';\n\n/**\n * Parse drillholes CSV with desurveyed trace points\n * Expect CSV columns: hole_id, x (easting), y (northing), z (elevation), order (optional) plus any attributes per point\n * @param {File|Blob|string} file - CSV file or data\n * @param {Object} [sourceColumnMap] - Optional user-provided column mappings\n * @returns {Promise<{holes: Array}>} - Parsed drillhole data\n */\nexport function parseDrillholesCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow, idx) => {\n const row = standardizeColumns(rawRow, null, sourceColumnMap);\n \n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n const x = row[EASTING] ?? row.x;\n const y = row[NORTHING] ?? row.y;\n const z = row[ELEVATION] ?? row.z;\n const order = row.order ?? idx;\n\n if (!holeId || x === null || x === undefined || y === null || y === undefined || z === null || z === undefined) return;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({\n ...row,\n holeId,\n order,\n x: Number(x) ?? 0,\n y: Number(y) ?? 0,\n z: Number(z) ?? 0\n });\n });\n\n const holes = Array.from(byHole.entries()).map(([holeId, pts]) => ({\n id: holeId,\n points: pts\n .sort((a, b) => a.order - b.order)\n .map((p) => ({\n ...p,\n x: Number(p.x) || 0,\n y: Number(p.y) || 0,\n z: Number(p.z) || 0\n }))\n }));\n\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseDrillholesCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { standardizeColumns as standardizeRow } from './keying.js';\nimport {\n HOLE_ID,\n LATITUDE,\n LONGITUDE,\n ELEVATION,\n AZIMUTH,\n DIP,\n FROM,\n TO,\n MID,\n PROJECT_ID,\n EASTING,\n NORTHING,\n CRS,\n DEPTH,\n GEOLOGY_CODE,\n GEOLOGY_DESCRIPTION,\n BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY,\n BASELODE_DATA_MODEL_DRILL_GEOLOGY\n} from './datamodel.js';\n\n// Re-export for backwards compatibility\nexport { DEFAULT_COLUMN_MAP } from './datamodel.js';\n\n/**\n * Convert source to array\n * @private\n */\nfunction toArray(source) {\n if (!source) return [];\n if (Array.isArray(source)) return [...source];\n return [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Sort rows by multiple columns\n * @private\n */\nfunction sortByColumns(rows = [], columns = []) {\n const out = [...rows];\n out.sort((a, b) => {\n for (let i = 0; i < columns.length; i += 1) {\n const col = columns[i];\n const av = a?.[col];\n const bv = b?.[col];\n if (av === bv) continue;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return `${av}`.localeCompare(`${bv}`);\n }\n return 0;\n });\n return out;\n}\n\nfunction validateNoOverlappingIntervals(rows = [], label = 'Intervals') {\n if (!rows.length) return;\n const ordered = sortByColumns(rows, [HOLE_ID, FROM, TO]);\n const prevToByHole = new Map();\n\n ordered.forEach((row) => {\n const holeId = `${row?.[HOLE_ID] ?? ''}`.trim();\n const fromValue = Number(row?.[FROM]);\n const toValue = Number(row?.[TO]);\n if (!holeId || !Number.isFinite(fromValue) || !Number.isFinite(toValue)) return;\n\n const prevTo = prevToByHole.get(holeId);\n if (Number.isFinite(prevTo) && fromValue < prevTo) {\n throw withDataErrorContext(\n 'validateNoOverlappingIntervals',\n new Error(`${label} intervals overlap for hole '${holeId}': from=${fromValue} is less than previous to=${prevTo}`)\n );\n }\n prevToByHole.set(holeId, toValue);\n });\n}\n\n/**\n * Parse CSV data using PapaParse\n * @private\n */\nfunction parseCsv(source, papaParseConfig = {}) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n ...papaParseConfig,\n complete: (results) => resolve(Array.isArray(results?.data) ? results.data : []),\n error: (error) => reject(withDataErrorContext('loadTable(csv)', error))\n });\n });\n}\n\n/**\n * Standardize column names in an array of rows using the baselode data model\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map (unused, for backwards compatibility)\n * @param {Object} [sourceColumnMap] - Optional source column map for user-provided mappings\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeColumns(rows = [], columnMap = null, sourceColumnMap = null) {\n return rows.map((row) => standardizeRow(row, columnMap, sourceColumnMap));\n}\n\nexport async function loadTable(source, options = {}) {\n const {\n kind = 'csv',\n columnMap = null,\n sourceColumnMap = null,\n papaParseConfig = {}\n } = options;\n\n let rows;\n if (Array.isArray(source)) {\n rows = toArray(source);\n } else if (kind === 'csv') {\n rows = await parseCsv(source, papaParseConfig);\n } else if (kind === 'parquet' || kind === 'sql') {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind in JS runtime: ${kind}`));\n } else {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind: ${kind}`));\n }\n\n return standardizeColumns(rows, columnMap, sourceColumnMap);\n}\n\n/**\n * Load and validate drillhole collar data\n * Requires hole_id and coordinates (either lat/lng or easting/northing)\n * @param {File|Blob|Array<Object>|string} source - Collar data source\n * @param {Object} options - Loading options\n * @param {string} options.crs - Coordinate reference system (optional)\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated collar rows\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadCollars(source, options = {}) {\n const {\n crs = null,\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Check for hole_id\n const hasHoleId = standardized.some(row => HOLE_ID in row);\n if (!hasHoleId) {\n throw withDataErrorContext('loadCollars', new Error(`Collar table missing column: ${HOLE_ID}`));\n }\n\n // Check for required coordinate columns\n const hasXY = standardized.some(row => EASTING in row && NORTHING in row);\n const hasLatLon = standardized.some(row => LATITUDE in row && LONGITUDE in row);\n\n if (!hasXY && !hasLatLon) {\n throw withDataErrorContext('loadCollars', new Error('Collar table missing coordinate columns (need easting/northing or latitude/longitude)'));\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert coordinates to numbers\n if (LATITUDE in result) result[LATITUDE] = toNumber(result[LATITUDE]);\n if (LONGITUDE in result) result[LONGITUDE] = toNumber(result[LONGITUDE]);\n if (ELEVATION in result) result[ELEVATION] = toNumber(result[ELEVATION]);\n if (EASTING in result) result[EASTING] = toNumber(result[EASTING]);\n if (NORTHING in result) result[NORTHING] = toNumber(result[NORTHING]);\n\n // If datasource_hole_id is missing, copy from hole_id\n if (!('datasource_hole_id' in result) && HOLE_ID in result) {\n result.datasource_hole_id = result[HOLE_ID];\n }\n\n return result;\n });\n\n // Validate required fields\n const required = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (hasLatLon && (!Number.isFinite(row[LATITUDE]) || !Number.isFinite(row[LONGITUDE]))) {\n return false;\n }\n if (hasXY && !hasLatLon && (!Number.isFinite(row[EASTING]) || !Number.isFinite(row[NORTHING]))) {\n return false;\n }\n return true;\n });\n\n if (!required) {\n throw withDataErrorContext('loadCollars', new Error('Collar table has missing required values'));\n }\n\n return normalized;\n}\n\n/**\n * Load and validate drillhole survey data\n * Requires hole_id, depth, azimuth, and dip columns\n * @param {File|Blob|Array<Object>|string} source - Survey data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated survey rows, sorted by hole_id and depth\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadSurveys(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, DEPTH, AZIMUTH, DIP];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadSurveys', new Error(`Survey table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (DEPTH in result) result[DEPTH] = toNumber(result[DEPTH]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n if (AZIMUTH in result) result[AZIMUTH] = toNumber(result[AZIMUTH]);\n if (DIP in result) result[DIP] = toNumber(result[DIP]);\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[DEPTH])) return false;\n if (!Number.isFinite(row[AZIMUTH])) return false;\n if (!Number.isFinite(row[DIP])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadSurveys', new Error('Survey table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, DEPTH]);\n}\n\n/**\n * Load and validate assay/interval data\n * Requires hole_id, from, and to columns. Calculates mid depth automatically.\n * @param {File|Blob|Array<Object>|string} source - Assay data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated assay rows, sorted by hole_id, from, to\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadAssays(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadAssays', new Error(`Assay table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n \n // Calculate midpoint\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadAssays', new Error('Assay table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Load and validate geology interval data for categorical strip-log plotting.\n * Requires hole_id, from, to and at least one geology categorical field.\n * @param {File|Blob|Array<Object>|string} source - Geology data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @returns {Promise<Array<Object>>} Array of validated geology rows, sorted by hole_id, from, to\n */\nexport async function loadGeology(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n\n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some((row) => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n // Expand equal bounds to a 1mm interval (mirrors Python loader behaviour)\n if (result[TO] === result[FROM]) {\n result[FROM] = Math.round(result[FROM] * 1000) / 1000;\n result[TO] = result[FROM] + 0.001;\n }\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n\n const hasCode = result[GEOLOGY_CODE] !== undefined && result[GEOLOGY_CODE] !== null && `${result[GEOLOGY_CODE]}`.trim() !== '';\n const hasDescription = result[GEOLOGY_DESCRIPTION] !== undefined && result[GEOLOGY_DESCRIPTION] !== null && `${result[GEOLOGY_DESCRIPTION]}`.trim() !== '';\n if (!hasCode && hasDescription) {\n result[GEOLOGY_CODE] = result[GEOLOGY_DESCRIPTION];\n }\n if (hasCode && !hasDescription) {\n result[GEOLOGY_DESCRIPTION] = result[GEOLOGY_CODE];\n }\n\n return result;\n });\n\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadGeology', new Error('Geology table has missing or invalid interval values'));\n }\n\n const hasCategory = normalized.some((row) => {\n const code = row[GEOLOGY_CODE];\n const description = row[GEOLOGY_DESCRIPTION];\n return (code !== undefined && code !== null && `${code}`.trim() !== '') ||\n (description !== undefined && description !== null && `${description}`.trim() !== '');\n });\n\n if (!hasCategory) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing categorical columns: ${GEOLOGY_CODE} or ${GEOLOGY_DESCRIPTION}`));\n }\n\n validateNoOverlappingIntervals(normalized, 'Geology');\n\n if (!keepAll) {\n const keep = new Set(Object.keys(BASELODE_DATA_MODEL_DRILL_GEOLOGY));\n return sortByColumns(\n normalized.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => keep.has(k)))),\n [HOLE_ID, FROM, TO]\n );\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Join assay data to desurveyed trace data by matching key columns\n * Adds trace fields to assay rows, suffixing with '_trace' if field already exists\n * @param {Array<Object>} assays - Array of assay rows\n * @param {Array<Object>} traces - Array of trace/desurvey rows\n * @param {Object} options - Join options\n * @param {Array<string>} options.onCols - Columns to join on (default: [hole_id])\n * @returns {Array<Object>} Assay rows enriched with trace data\n */\nexport function joinAssaysToTraces(assays = [], traces = [], options = {}) {\n const onCols = Array.isArray(options.onCols) && options.onCols.length ? options.onCols : [HOLE_ID];\n if (!traces.length) return [...assays];\n\n const keyOf = (row) => onCols.map((col) => `${row?.[col] ?? ''}`).join('|');\n const tracesByKey = new Map();\n traces.forEach((trace) => {\n tracesByKey.set(keyOf(trace), trace);\n });\n\n return assays.map((assay) => {\n const trace = tracesByKey.get(keyOf(assay));\n if (!trace) return { ...assay };\n const merged = { ...assay };\n Object.entries(trace).forEach(([key, value]) => {\n if (onCols.includes(key)) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = value;\n } else {\n merged[key] = value;\n }\n });\n return merged;\n });\n}\n\n/**\n * Filter rows by project ID\n * @param {Array<Object>} rows - Array of rows with project_id field\n * @param {string|null} projectId - Project ID to filter by (null returns all rows)\n * @returns {Array<Object>} Filtered rows\n */\nexport function filterByProject(rows = [], projectId = null) {\n if (projectId === null || projectId === undefined) return [...rows];\n if (!rows.length) return [];\n \n // Check if rows have project_id column\n const hasProjectId = rows.some(row => PROJECT_ID in row);\n if (!hasProjectId) return [...rows];\n \n return rows.filter((row) => row?.[PROJECT_ID] === projectId);\n}\n\n/**\n * Coerce specified columns to numeric (finite number or undefined)\n * @param {Array<Object>} rows - Array of rows\n * @param {Array<string>} columns - Column names to coerce to numeric\n * @returns {Array<Object>} Rows with coerced numeric columns\n */\nexport function coerceNumeric(rows = [], columns = []) {\n return rows.map((row) => {\n const next = { ...row };\n columns.forEach((column) => {\n if (!(column in next)) return;\n const n = toNumber(next[column]);\n next[column] = n;\n });\n return next;\n });\n}\n\n/**\n * Assemble a complete drillhole dataset from component tables\n * @param {Object} components - Dataset components\n * @param {Array<Object>} components.collars - Collar data\n * @param {Array<Object>} components.surveys - Survey data\n * @param {Array<Object>} components.assays - Assay data\n * @param {Array<Object>} components.geology - Geology interval data\n * @param {Array<Object>} components.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, geology: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n geology = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\n geology: toArray(geology),\n structures: toArray(structures),\n metadata: metadata || {}\n };\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, DEPTH, DIP, AZIMUTH } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to standardized column names.\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Determine if a set of rows represents point or interval data.\n * @private\n * @param {Array<Object>} rows - Normalized rows\n * @returns {'point'|'interval'|null}\n */\nfunction detectSchema(rows) {\n if (!rows.length) return null;\n const first = rows[0];\n const hasInterval = FROM in first && TO in first;\n const hasPoint = DEPTH in first && !hasInterval;\n if (hasInterval) return 'interval';\n if (hasPoint) return 'point';\n return null;\n}\n\n/**\n * Coerce a value to a finite number or null.\n * @private\n */\nfunction toNumber(v) {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Normalize a single structural point row.\n * @private\n */\nfunction extractStructuralPoint(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const depth = toNumber(row[DEPTH]);\n if (depth === null) return null;\n\n return {\n [HOLE_ID]: holeId,\n [DEPTH]: depth,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Normalize a single structural interval row.\n * @private\n */\nfunction extractStructuralInterval(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const from = toNumber(row[FROM]);\n const to = toNumber(row[TO]);\n if (from === null || to === null || to <= from) return null;\n\n const mid = 0.5 * (from + to);\n return {\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n mid,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n classification: row.classification != null ? `${row.classification}` : null,\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Validate an array of structural point rows.\n * Returns an object with valid rows and error details.\n *\n * @param {Array<Object>} rows - Normalized structural point rows\n * @returns {{ valid: Array<Object>, errors: Array<{row: Object, message: string}> }}\n */\nexport function validateStructuralPoints(rows) {\n const valid = [];\n const errors = [];\n\n for (const row of rows) {\n const messages = [];\n const dip = toNumber(row[DIP]);\n const az = toNumber(row[AZIMUTH]);\n\n if (dip !== null && (dip < 0 || dip > 90)) {\n messages.push(`dip ${dip} out of range [0, 90]`);\n }\n if (az !== null && (az < 0 || az >= 360)) {\n messages.push(`azimuth ${az} out of range [0, 360)`);\n }\n if (messages.length) {\n errors.push({ row, message: messages.join('; ') });\n } else {\n valid.push(row);\n }\n }\n\n return { valid, errors };\n}\n\n/**\n * Parse a structural points CSV (point schema: hole_id, depth, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural point objects\n */\nexport function parseStructuralPointsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const opts = {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const point = extractStructuralPoint(row);\n if (point) rows.push(point);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralPointsCSV', error)),\n };\n\n if (typeof source === 'string' && !source.startsWith('data:') && source.includes('\\n')) {\n Papa.parse(source, opts);\n } else {\n Papa.parse(source, opts);\n }\n });\n}\n\n/**\n * Parse a structural intervals CSV (interval schema: hole_id, from, to, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural interval objects\n */\nexport function parseStructuralIntervalsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractStructuralInterval(row);\n if (interval) rows.push(interval);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralIntervalsCSV', error)),\n });\n });\n}\n\n/**\n * Group a flat array of structural rows into hole objects keyed by hole_id.\n *\n * The resulting hole objects use the same shape as assay holes\n * ({ holeId, points }) so they can be merged into useDrillholeTraceGrid.\n *\n * @param {Array<Object>} rows - Flat array of structural rows (already normalized)\n * @param {string} [holeIdCol='hole_id'] - Column name containing the hole identifier\n * @returns {Array<{ holeId: string, points: Array<Object> }>}\n */\nexport function groupRowsByHole(rows, holeIdCol = HOLE_ID) {\n const byId = new Map();\n for (const row of rows) {\n const id = row[holeIdCol] != null ? String(row[holeIdCol]).trim() : '';\n if (!id) continue;\n if (!byId.has(id)) byId.set(id, { holeId: id, points: [] });\n byId.get(id).points.push(row);\n }\n return Array.from(byId.values());\n}\n\n/**\n * Parse a structural CSV, auto-detecting point vs interval schema.\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<{ schema: 'point'|'interval', rows: Array<Object> }>}\n */\nexport function parseStructuralCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = results.data.map(r => normalizeRow(r, sourceColumnMap));\n const schema = detectSchema(normalized);\n\n if (!schema) {\n reject(withDataErrorContext('parseStructuralCSV',\n new Error(\"Structural CSV requires either 'depth' (point) or 'from'/'to' (interval) columns\")));\n return;\n }\n\n const rows = [];\n for (const row of normalized) {\n const parsed = schema === 'interval'\n ? extractStructuralInterval(row)\n : extractStructuralPoint(row);\n if (parsed) rows.push(parsed);\n }\n resolve({ schema, rows });\n },\n error: (error) => reject(withDataErrorContext('parseStructuralCSV', error)),\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, FROM, TO, MID, DEPTH, DIP, AZIMUTH } from './datamodel.js';\nimport { parseStructuralCSV, groupRowsByHole } from './structuralLoader.js';\n\n\n/**\n * Parse assay CSV text into an array of hole objects.\n *\n * Each interval row gets a pre-computed `mid` field (= (from + to) / 2) stored\n * under both the `mid` and `depth` keys so it can serve as the unified y-axis\n * depth value when rendered alongside structural point data.\n *\n * @param {string} csvText - Assay CSV content as a text string\n * @returns {Promise<Array<{holeId: string, points: Array<Object>}>>}\n */\nexport function parseAssayCsvTextToHoles(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // Strip structural-domain columns that some assay exports (e.g. GSWA) include\n // as per-interval hole-orientation values — these are borehole survey attributes,\n // not structural plane measurements, and must not appear as selectable properties\n // alongside true structural data.\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rowWithoutStructural } = row;\n const point = {\n ...rowWithoutStructural,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid, // unified depth field for y-axis rendering\n _source: 'assay',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n const holes = Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n }));\n resolve(holes);\n },\n });\n });\n}\n\n/**\n * Parse geology CSV text into an array of hole objects, keyed by hole_id.\n *\n * @param {string} csvText - Geology CSV content as a text string\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport function parseGeologyCsvText(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = (row[HOLE_ID] ?? '').toString().trim();\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rest } = row;\n const point = {\n ...rest,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid,\n _source: 'geology',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n resolve({\n holes: Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n })),\n });\n },\n });\n });\n}\n\n/**\n * Build a unified drillhole dataset by merging assay intervals and structural\n * point/interval measurements from their respective CSV text sources.\n *\n * The result is a flat, holeId-keyed collection of hole objects. Each hole's\n * `points` array contains rows from both sources:\n * - Assay rows carry all geochemical columns; `depth` is set to the interval\n * midpoint ((from + to) / 2) pre-computed at load time.\n * - Structural rows carry dip/azimuth/description columns; `depth` comes\n * directly from the structural CSV (point schema) or the interval midpoint\n * (interval schema).\n * - Rows from each source are tagged with `_source: 'assay'|'structural'`.\n *\n * The returned holes can be passed directly to `useDrillholeTraceGrid` as\n * `extraHoles` (without a `sourceFile`) since all data is eager-loaded.\n *\n * @param {Object} options\n * @param {string} [options.assayCsv] - Assay CSV text\n * @param {string} [options.structuralCsv] - Structural CSV text\n * @param {string} [options.geologyCsv] - Geology CSV text\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv, geologyCsv } = {}) {\n const [assayHoles, structuralHoles, geologyHoles] = await Promise.all([\n assayCsv ? parseAssayCsvTextToHoles(assayCsv) : Promise.resolve([]),\n structuralCsv\n ? parseStructuralCSV(structuralCsv).then(({ rows }) =>\n groupRowsByHole(rows.map((r) => ({ ...r, _source: 'structural' })))\n )\n : Promise.resolve([]),\n geologyCsv ? parseGeologyCsvText(geologyCsv).then(({ holes }) => holes) : Promise.resolve([]),\n ]);\n\n // Merge holes from all sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of [...structuralHoles, ...geologyHoles]) {\n const id = sh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...existing.points, ...(sh.points || [])] });\n } else {\n byId.set(id, sh);\n }\n }\n\n return { holes: Array.from(byId.values()) };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Calculate significant intercepts from assay interval data.\n *\n * A significant intercept is a contiguous downhole run of assay intervals where\n * every interval meets or exceeds {@link minGrade}, and the total run length is\n * at least {@link minLength}.\n *\n * @param {Array<Object>} intervals - Assay interval rows, each containing at\n * minimum the hole ID, from-depth, to-depth, and assay field columns.\n * @param {string} assayField - Name of the assay value property on each row\n * (e.g. `\"CU_PCT\"` or `\"AU_PPM\"`).\n * @param {number} minGrade - Minimum grade threshold. Intervals below this\n * value are excluded.\n * @param {number} minLength - Minimum contiguous downhole length required to\n * report an intercept.\n * @param {Object} [options={}]\n * @param {string} [options.fromCol=\"from\"] - Property name for the from-depth.\n * @param {string} [options.toCol=\"to\"] - Property name for the to-depth.\n * @param {string} [options.holeCol=\"hole_id\"] - Property name for the hole ID.\n * @returns {Array<Object>} One object per significant intercept with properties:\n * `hole_id`, `assay_field`, `from`, `to`, `length`, `avg_grade`,\n * `n_samples`, `label`.\n */\nexport function significantIntercepts(intervals, assayField, minGrade, minLength, options = {}) {\n const fromCol = options.fromCol || 'from';\n const toCol = options.toCol || 'to';\n const holeCol = options.holeCol || 'hole_id';\n\n if (!intervals || !intervals.length) return [];\n\n // Group by hole ID\n const byHole = {};\n for (const row of intervals) {\n const holeId = row[holeCol];\n if (holeId == null) continue;\n if (!byHole[holeId]) byHole[holeId] = [];\n byHole[holeId].push(row);\n }\n\n const results = [];\n\n for (const [holeId, holeIntervals] of Object.entries(byHole)) {\n // Sort by from depth\n const sorted = [...holeIntervals].sort((a, b) => Number(a[fromCol]) - Number(b[fromCol]));\n\n // Filter to above-threshold intervals with finite grades\n const qualifying = sorted.filter((row) => {\n const grade = Number(row[assayField]);\n return Number.isFinite(grade) && grade >= minGrade;\n });\n\n if (!qualifying.length) continue;\n\n // Group qualifying intervals into contiguous runs\n const runs = [];\n let currentRun = [];\n let prevTo = null;\n\n for (const row of qualifying) {\n const f = Number(row[fromCol]);\n const t = Number(row[toCol]);\n\n if (prevTo === null || Math.abs(f - prevTo) > 1e-6) {\n if (currentRun.length) runs.push(currentRun);\n currentRun = [row];\n } else {\n currentRun.push(row);\n }\n prevTo = t;\n }\n if (currentRun.length) runs.push(currentRun);\n\n for (const run of runs) {\n const totalFrom = Number(run[0][fromCol]);\n const totalTo = Number(run[run.length - 1][toCol]);\n const totalLength = totalTo - totalFrom;\n\n if (totalLength < minLength) continue;\n\n let weightedSum = 0;\n let totalWeight = 0;\n for (const row of run) {\n const grade = Number(row[assayField]);\n const len = Number(row[toCol]) - Number(row[fromCol]);\n weightedSum += grade * len;\n totalWeight += len;\n }\n const avgGrade = weightedSum / totalWeight;\n const nSamples = run.length;\n const label = `${totalLength.toFixed(1)} m @ ${avgGrade.toFixed(2)} ${assayField}`;\n\n results.push({\n [holeCol]: holeId,\n assay_field: assayField,\n [fromCol]: totalFrom,\n [toCol]: totalTo,\n length: totalLength,\n avg_grade: avgGrade,\n n_samples: nSamples,\n label,\n });\n }\n }\n\n return results;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * From-to interval algebra primitives.\n *\n * Pure functions on arrays of interval rows. All rows are plain objects\n * keyed by `HOLE_ID`, `FROM`, `TO` plus any additional value columns.\n * Functions never mutate inputs.\n */\n\nimport { HOLE_ID, FROM, TO } from './datamodel.js';\n\nfunction groupByHole(rows, holeCol) {\n const grouped = new Map();\n for (const row of rows) {\n const hole = row[holeCol];\n if (hole == null) continue;\n if (!grouped.has(hole)) grouped.set(hole, []);\n grouped.get(hole).push(row);\n }\n return grouped;\n}\n\n/**\n * Length of each interval row (`to - from`).\n *\n * @returns {number[]}\n */\nexport function intervalLength(rows, { fromCol = FROM, toCol = TO } = {}) {\n return rows.map((row) => Number(row[toCol]) - Number(row[fromCol]));\n}\n\n/**\n * Midpoint depth of each interval row.\n *\n * @returns {number[]}\n */\nexport function fromToMidpoints(rows, { fromCol = FROM, toCol = TO } = {}) {\n return rows.map((row) => (Number(row[fromCol]) + Number(row[toCol])) / 2);\n}\n\n/**\n * Uncovered downhole ranges between consecutive intervals, per hole.\n *\n * @param {number} [options.minGap=0] - Minimum gap length to report.\n * @returns {Array<Object>} `{hole_id, from, to, length}` per gap.\n */\nexport function detectGaps(\n rows,\n { fromCol = FROM, toCol = TO, holeCol = HOLE_ID, minGap = 0 } = {},\n) {\n if (!rows || !rows.length) return [];\n\n const result = [];\n for (const [hole, group] of groupByHole(rows, holeCol)) {\n const sorted = [...group].sort(\n (left, right) => Number(left[fromCol]) - Number(right[fromCol]),\n );\n let prevTo = null;\n for (const row of sorted) {\n const fromDepth = Number(row[fromCol]);\n const toDepth = Number(row[toCol]);\n if (prevTo !== null && fromDepth - prevTo > minGap) {\n result.push({\n [holeCol]: hole,\n [fromCol]: prevTo,\n [toCol]: fromDepth,\n length: fromDepth - prevTo,\n });\n }\n if (prevTo === null || toDepth > prevTo) prevTo = toDepth;\n }\n }\n return result;\n}\n\n/**\n * Pairwise overlapping intervals, per hole.\n *\n * Indices are positional into the input `rows` array.\n *\n * @returns {Array<Object>} `{hole_id, from, to, length, first_index, second_index}`.\n */\nexport function detectOverlaps(\n rows,\n { fromCol = FROM, toCol = TO, holeCol = HOLE_ID } = {},\n) {\n if (!rows || !rows.length) return [];\n\n const indexed = rows.map((row, originalIndex) => ({ row, originalIndex }));\n const byHole = new Map();\n for (const entry of indexed) {\n const hole = entry.row[holeCol];\n if (hole == null) continue;\n if (!byHole.has(hole)) byHole.set(hole, []);\n byHole.get(hole).push(entry);\n }\n\n const result = [];\n for (const [hole, group] of byHole) {\n const sorted = [...group].sort(\n (left, right) => Number(left.row[fromCol]) - Number(right.row[fromCol]),\n );\n for (let firstIdx = 0; firstIdx < sorted.length; firstIdx += 1) {\n const firstFrom = Number(sorted[firstIdx].row[fromCol]);\n const firstTo = Number(sorted[firstIdx].row[toCol]);\n for (let secondIdx = firstIdx + 1; secondIdx < sorted.length; secondIdx += 1) {\n const secondFrom = Number(sorted[secondIdx].row[fromCol]);\n if (secondFrom >= firstTo) break;\n const secondTo = Number(sorted[secondIdx].row[toCol]);\n const overlapFrom = Math.max(firstFrom, secondFrom);\n const overlapTo = Math.min(firstTo, secondTo);\n if (overlapTo > overlapFrom) {\n result.push({\n [holeCol]: hole,\n [fromCol]: overlapFrom,\n [toCol]: overlapTo,\n length: overlapTo - overlapFrom,\n first_index: sorted[firstIdx].originalIndex,\n second_index: sorted[secondIdx].originalIndex,\n });\n }\n }\n }\n }\n return result;\n}\n\nfunction normalizeDepthsArg(depths, holeCol) {\n if (depths == null) return { byHole: new Map(), all: [] };\n if (Array.isArray(depths)) {\n const firstEntry = depths[0];\n const isRowArray = depths.length\n && firstEntry\n && typeof firstEntry === 'object'\n && firstEntry[holeCol] != null\n && firstEntry.depth != null;\n if (isRowArray) {\n const byHole = new Map();\n for (const row of depths) {\n const hole = row && row[holeCol];\n const depth = row && row.depth;\n if (hole == null || depth == null) continue;\n if (!byHole.has(hole)) byHole.set(hole, []);\n byHole.get(hole).push(Number(depth));\n }\n return { byHole, all: [] };\n }\n return { byHole: new Map(), all: depths.map(Number) };\n }\n if (typeof depths === 'number') {\n return { byHole: new Map(), all: [Number(depths)] };\n }\n if (depths && typeof depths === 'object') {\n if (depths[holeCol] != null && depths.depth != null) {\n const byHole = new Map();\n byHole.set(depths[holeCol], [Number(depths.depth)]);\n return { byHole, all: [] };\n }\n const byHole = new Map();\n for (const [hole, value] of Object.entries(depths)) {\n const list = Array.isArray(value) ? value : [value];\n byHole.set(hole, list.map(Number));\n }\n return { byHole, all: [] };\n }\n return { byHole: new Map(), all: [] };\n}\n\n/**\n * Split intervals at boundary depths.\n *\n * Any depth strictly inside `(from, to)` is introduced as a new boundary;\n * the row is replaced by the resulting sub-intervals. All other columns\n * are inherited unchanged.\n *\n * @param {(number|number[]|Object|Array<Object>)} depths - One of:\n * a scalar applied to every hole; an array of numbers applied to every\n * hole; `{hole_id: [d1, d2, ...]}` keyed by hole; or an array of\n * `{hole_id, depth}` rows.\n * @returns {Array<Object>}\n */\nexport function splitAt(\n rows,\n depths,\n { fromCol = FROM, toCol = TO, holeCol = HOLE_ID } = {},\n) {\n if (!rows || !rows.length) return [];\n\n const { byHole, all } = normalizeDepthsArg(depths, holeCol);\n\n const out = [];\n for (const row of rows) {\n const hole = row[holeCol];\n const fromDepth = Number(row[fromCol]);\n const toDepth = Number(row[toCol]);\n const candidate = byHole.has(hole) ? byHole.get(hole) : all;\n const inner = [...new Set(\n candidate\n .map(Number)\n .filter((depth) => depth > fromDepth && depth < toDepth),\n )].sort((left, right) => left - right);\n if (!inner.length) {\n out.push({ ...row });\n continue;\n }\n const boundaries = [fromDepth, ...inner, toDepth];\n for (let boundaryIdx = 0; boundaryIdx < boundaries.length - 1; boundaryIdx += 1) {\n out.push({\n ...row,\n [fromCol]: boundaries[boundaryIdx],\n [toCol]: boundaries[boundaryIdx + 1],\n });\n }\n }\n return out;\n}\n\n/**\n * Clip intervals to a downhole depth window.\n *\n * Intervals entirely outside the window are dropped; straddling intervals\n * have their from/to pulled to the boundary. All other columns preserved.\n *\n * @returns {Array<Object>}\n */\nexport function clip(rows, fromDepth, toDepth, { fromCol = FROM, toCol = TO } = {}) {\n if (!rows || !rows.length) return [];\n\n const out = [];\n for (const row of rows) {\n let segFrom = Number(row[fromCol]);\n let segTo = Number(row[toCol]);\n if (fromDepth != null) {\n if (segTo <= fromDepth) continue;\n if (segFrom < fromDepth) segFrom = fromDepth;\n }\n if (toDepth != null) {\n if (segFrom >= toDepth) continue;\n if (segTo > toDepth) segTo = toDepth;\n }\n out.push({ ...row, [fromCol]: segFrom, [toCol]: segTo });\n }\n return out;\n}\n\nfunction rowContaining(group, depth, fromCol, toCol) {\n for (const row of group) {\n if (Number(row[fromCol]) <= depth && Number(row[toCol]) > depth) return row;\n }\n return null;\n}\n\n/**\n * Left-join interval tables onto a common from-to support.\n *\n * Per hole, the first (\"left\") table anchors the support; boundary depths\n * from later tables that fall inside the left's coverage are introduced as\n * new sub-interval boundaries. Each output row carries `<name>_<col>`\n * values looked up at the sub-interval midpoint. Where a later table has\n * no row covering the midpoint, the column is `null`.\n *\n * @param {Object<string, Array<Object>>} tables - Ordered object literal:\n * the first key is the left table.\n * @returns {Array<Object>} Rows with `hole_id`, `from`, `to`, and\n * prefixed columns from every input table.\n */\nexport function mergeTables(\n tables,\n { fromCol = FROM, toCol = TO, holeCol = HOLE_ID } = {},\n) {\n if (!tables) return [];\n\n const tableNames = Object.keys(tables);\n if (!tableNames.length) return [];\n\n const leftName = tableNames[0];\n const left = tables[leftName] || [];\n if (!left.length) return [];\n\n const leftByHole = groupByHole(left, holeCol);\n const otherByHole = {};\n for (const otherName of tableNames.slice(1)) {\n otherByHole[otherName] = groupByHole(tables[otherName] || [], holeCol);\n }\n\n const out = [];\n for (const [hole, leftRowsForHole] of leftByHole) {\n const boundaries = new Set();\n let leftMin = Infinity;\n let leftMax = -Infinity;\n for (const row of leftRowsForHole) {\n const fromDepth = Number(row[fromCol]);\n const toDepth = Number(row[toCol]);\n boundaries.add(fromDepth);\n boundaries.add(toDepth);\n if (fromDepth < leftMin) leftMin = fromDepth;\n if (toDepth > leftMax) leftMax = toDepth;\n }\n for (const otherName of tableNames.slice(1)) {\n const otherRowsForHole = otherByHole[otherName].get(hole) || [];\n for (const row of otherRowsForHole) {\n const otherFrom = Number(row[fromCol]);\n const otherTo = Number(row[toCol]);\n if (otherTo < leftMin || otherFrom > leftMax) continue;\n if (otherFrom > leftMin) boundaries.add(otherFrom);\n if (otherTo < leftMax) boundaries.add(otherTo);\n }\n }\n const ordered = [...boundaries].sort((first, second) => first - second);\n\n for (let boundaryIdx = 0; boundaryIdx < ordered.length - 1; boundaryIdx += 1) {\n const segFrom = ordered[boundaryIdx];\n const segTo = ordered[boundaryIdx + 1];\n const midpoint = (segFrom + segTo) / 2;\n const leftRow = rowContaining(leftRowsForHole, midpoint, fromCol, toCol);\n if (!leftRow) continue;\n const outRow = { [holeCol]: hole, [fromCol]: segFrom, [toCol]: segTo };\n for (const col of Object.keys(leftRow)) {\n if (col === holeCol || col === fromCol || col === toCol) continue;\n outRow[`${leftName}_${col}`] = leftRow[col];\n }\n for (const otherName of tableNames.slice(1)) {\n const otherRowsForHole = otherByHole[otherName].get(hole) || [];\n const otherRow = rowContaining(otherRowsForHole, midpoint, fromCol, toCol);\n const sample = (tables[otherName] && tables[otherName][0]) || {};\n const otherCols = Object.keys(sample).filter(\n (col) => col !== holeCol && col !== fromCol && col !== toCol,\n );\n for (const col of otherCols) {\n outRow[`${otherName}_${col}`] = otherRow ? otherRow[col] : null;\n }\n }\n out.push(outRow);\n }\n }\n return out;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Comprehensive drillhole-database validator.\n *\n * Mirrors the Python `baselode.drill.validate.validate_drillhole_db`\n * shape: returns `{summary, issues}` where each issue carries a `check`\n * name, `severity` (`error` / `warning` / `info`), the affected\n * `hole_id` / `table` / `row_index`, a human-readable `message`, and a\n * `fix` recipe when one is available.\n */\n\nimport { HOLE_ID, FROM, TO, DEPTH, AZIMUTH, DIP, MAX_DEPTH } from './datamodel.js';\nimport { detectGaps, detectOverlaps } from './intervals.js';\n\nexport const SEVERITY_ERROR = 'error';\nexport const SEVERITY_WARNING = 'warning';\nexport const SEVERITY_INFO = 'info';\n\nconst BDL_PATTERN = /^\\s*<\\s*(-?\\d+(?:\\.\\d+)?(?:[eE][-+]?\\d+)?)\\s*$/;\n\nfunction makeIssue({ check, severity, message, holeId = null, table = null, rowIndex = null, fix = null }) {\n return { check, severity, hole_id: holeId, table, row_index: rowIndex, message, fix };\n}\n\nfunction buildMaxDepthLookup(collar, holeCol, maxDepthCol) {\n const lookup = new Map();\n for (const row of collar || []) {\n const holeId = row && row[holeCol];\n const value = row && row[maxDepthCol];\n if (holeId == null || value == null || Number.isNaN(Number(value))) continue;\n lookup.set(holeId, Number(value));\n }\n return lookup;\n}\n\nfunction groupBy(rows, key) {\n const grouped = new Map();\n for (const row of rows || []) {\n const value = row && row[key];\n if (value == null) continue;\n if (!grouped.has(value)) grouped.set(value, []);\n grouped.get(value).push(row);\n }\n return grouped;\n}\n\nfunction checkDuplicateHoleIds(collar, holeCol) {\n if (!collar || !collar.length) return [];\n const counts = new Map();\n for (const row of collar) {\n const holeId = row && row[holeCol];\n if (holeId == null) continue;\n counts.set(holeId, (counts.get(holeId) || 0) + 1);\n }\n const issues = [];\n for (const [holeId, count] of counts) {\n if (count > 1) {\n issues.push(makeIssue({\n check: 'duplicate_hole_ids',\n severity: SEVERITY_ERROR,\n holeId: String(holeId),\n table: 'collar',\n message: `Hole '${holeId}' appears ${count} times in the collar table`,\n fix: 'Remove or merge duplicate collar rows so each hole_id is unique',\n }));\n }\n }\n return issues;\n}\n\nfunction checkSingleStationSurveys(survey, holeCol) {\n if (!survey || !survey.length) return [];\n const grouped = groupBy(survey, holeCol);\n const issues = [];\n for (const [holeId, group] of grouped) {\n if (group.length === 1) {\n const rowIndex = survey.indexOf(group[0]);\n issues.push(makeIssue({\n check: 'single_station_surveys',\n severity: SEVERITY_WARNING,\n holeId: String(holeId),\n table: 'survey',\n rowIndex,\n message: `Hole '${holeId}' has only one survey station; desurvey will fail`,\n fix: 'Call fixSingleStationSurveys(survey, collar) to add a synthetic station',\n }));\n }\n }\n return issues;\n}\n\nfunction checkAzimuthRange(survey, holeCol, azimuthCol, allowFullCircle = false) {\n if (!survey || !survey.length) return [];\n const intervalText = allowFullCircle ? '[0, 360]' : '[0, 360)';\n const issues = [];\n survey.forEach((row, rowIndex) => {\n const value = row && row[azimuthCol];\n if (value == null || Number.isNaN(Number(value))) return;\n const numeric = Number(value);\n const outOfRange = numeric < 0 || (allowFullCircle ? numeric > 360 : numeric >= 360);\n if (outOfRange) {\n issues.push(makeIssue({\n check: 'azimuth_range',\n severity: SEVERITY_ERROR,\n holeId: row[holeCol] != null ? String(row[holeCol]) : null,\n table: 'survey',\n rowIndex,\n message: `Azimuth ${numeric} outside ${intervalText}`,\n fix: 'Call normalizeAzimuth(survey) to wrap into [0, 360) or correct the source value',\n }));\n }\n });\n return issues;\n}\n\nfunction checkDipRange(survey, holeCol, dipCol) {\n if (!survey || !survey.length) return [];\n const issues = [];\n survey.forEach((row, rowIndex) => {\n const value = row && row[dipCol];\n if (value == null || Number.isNaN(Number(value))) return;\n const numeric = Number(value);\n if (numeric < -90 || numeric > 90) {\n issues.push(makeIssue({\n check: 'dip_range',\n severity: SEVERITY_ERROR,\n holeId: row[holeCol] != null ? String(row[holeCol]) : null,\n table: 'survey',\n rowIndex,\n message: `Dip ${numeric} outside [-90, 90]`,\n fix: 'Correct the source dip value',\n }));\n }\n });\n return issues;\n}\n\nfunction checkOrphanIntervals(table, tableName, collarHoleIds, holeCol) {\n if (!table || !table.length) return [];\n const issues = [];\n table.forEach((row, rowIndex) => {\n const holeId = row && row[holeCol];\n if (holeId == null) return;\n if (!collarHoleIds.has(holeId)) {\n issues.push(makeIssue({\n check: 'orphan_intervals',\n severity: SEVERITY_ERROR,\n holeId: String(holeId),\n table: tableName,\n rowIndex,\n message: `Hole '${holeId}' in '${tableName}' is not present in the collar table`,\n fix: 'Call dropOrphanIntervals(table, collar) to remove these rows, or add the hole to the collar table',\n }));\n }\n });\n return issues;\n}\n\nfunction checkNegativeLengths(table, tableName, holeCol, fromCol, toCol) {\n if (!table || !table.length) return [];\n const issues = [];\n table.forEach((row, rowIndex) => {\n const fromDepth = row && row[fromCol];\n const toDepth = row && row[toCol];\n if (fromDepth == null || toDepth == null) return;\n if (Number(toDepth) <= Number(fromDepth)) {\n issues.push(makeIssue({\n check: 'negative_lengths',\n severity: SEVERITY_ERROR,\n holeId: row[holeCol] != null ? String(row[holeCol]) : null,\n table: tableName,\n rowIndex,\n message: `Interval from=${fromDepth} to=${toDepth} has zero or negative length`,\n fix: 'Call swapInvertedIntervals(table) to fix data-entry typos where to<from; zero-length rows (to===from) require manual review',\n }));\n }\n });\n return issues;\n}\n\nfunction checkIntervalsBeyondMaxDepth(table, tableName, maxDepthLookup, holeCol, toCol) {\n if (!maxDepthLookup.size || !table || !table.length) return [];\n const issues = [];\n table.forEach((row, rowIndex) => {\n const holeId = row && row[holeCol];\n if (holeId == null) return;\n const maxDepth = maxDepthLookup.get(holeId);\n if (maxDepth == null) return;\n const toDepth = row && row[toCol];\n if (toDepth == null) return;\n if (Number(toDepth) > maxDepth) {\n issues.push(makeIssue({\n check: 'intervals_beyond_max_depth',\n severity: SEVERITY_WARNING,\n holeId: String(holeId),\n table: tableName,\n rowIndex,\n message: `Interval to=${toDepth} exceeds collar max_depth=${maxDepth} for '${holeId}'`,\n fix: 'Extend collar max_depth or clip the interval',\n }));\n }\n });\n return issues;\n}\n\nfunction checkIntervalGaps(table, tableName, holeCol, fromCol, toCol) {\n if (!table || !table.length) return [];\n return detectGaps(table, { holeCol, fromCol, toCol }).map((gap) => makeIssue({\n check: 'interval_gaps',\n severity: SEVERITY_INFO,\n holeId: String(gap[holeCol]),\n table: tableName,\n message: `Gap from ${gap[fromCol]} to ${gap[toCol]} (${gap.length.toFixed(3)} m) in '${tableName}'`,\n fix: 'Re-sample the interval or document the gap',\n }));\n}\n\nfunction checkIntervalOverlaps(table, tableName, holeCol, fromCol, toCol) {\n if (!table || !table.length) return [];\n return detectOverlaps(table, { holeCol, fromCol, toCol }).map((overlap) => makeIssue({\n check: 'interval_overlaps',\n severity: SEVERITY_WARNING,\n holeId: String(overlap[holeCol]),\n table: tableName,\n rowIndex: overlap.first_index,\n message: (\n `Overlap from ${overlap[fromCol]} to ${overlap[toCol]} (${overlap.length.toFixed(3)} m) `\n + `between rows ${overlap.first_index} and ${overlap.second_index}`\n ),\n fix: 'Merge overlapping intervals or correct the from/to depths',\n }));\n}\n\nfunction checkBelowDetectionLimit(table, tableName, holeCol, fromCol, toCol) {\n if (!table || !table.length) return [];\n const reserved = new Set([holeCol, fromCol, toCol]);\n const issues = [];\n table.forEach((row, rowIndex) => {\n if (!row) return;\n for (const [col, value] of Object.entries(row)) {\n if (reserved.has(col)) continue;\n if (typeof value !== 'string') continue;\n if (!BDL_PATTERN.test(value)) continue;\n issues.push(makeIssue({\n check: 'below_detection_limit',\n severity: SEVERITY_INFO,\n holeId: row[holeCol] != null ? String(row[holeCol]) : null,\n table: tableName,\n rowIndex,\n message: `Column '${col}' contains below-detection sentinel '${value}'`,\n fix: 'Call replaceBelowDetectionLimit(rows, {columns: [...]}) to substitute MDL/2',\n }));\n }\n });\n return issues;\n}\n\n/**\n * Run the full drillhole-database validation suite.\n *\n * @param {Object} db - `{ collar, survey, intervalTables }`\n * @returns {{ summary: Object, issues: Array<Object> }}\n */\nexport function validateDrillholeDb(\n { collar = [], survey = [], intervalTables = null } = {},\n {\n holeCol = HOLE_ID,\n depthCol = DEPTH,\n azimuthCol = AZIMUTH,\n dipCol = DIP,\n fromCol = FROM,\n toCol = TO,\n maxDepthCol = MAX_DEPTH,\n allowFullCircle = false,\n } = {},\n) {\n const issues = [];\n issues.push(...checkDuplicateHoleIds(collar, holeCol));\n issues.push(...checkSingleStationSurveys(survey, holeCol));\n issues.push(...checkAzimuthRange(survey, holeCol, azimuthCol, allowFullCircle));\n issues.push(...checkDipRange(survey, holeCol, dipCol));\n\n if (intervalTables) {\n const collarHoleIds = new Set(\n (collar || []).map((row) => row && row[holeCol]).filter((value) => value != null),\n );\n const maxDepthLookup = buildMaxDepthLookup(collar, holeCol, maxDepthCol);\n for (const [tableName, table] of Object.entries(intervalTables)) {\n issues.push(...checkOrphanIntervals(table, tableName, collarHoleIds, holeCol));\n issues.push(...checkNegativeLengths(table, tableName, holeCol, fromCol, toCol));\n issues.push(...checkIntervalsBeyondMaxDepth(table, tableName, maxDepthLookup, holeCol, toCol));\n issues.push(...checkIntervalGaps(table, tableName, holeCol, fromCol, toCol));\n issues.push(...checkIntervalOverlaps(table, tableName, holeCol, fromCol, toCol));\n issues.push(...checkBelowDetectionLimit(table, tableName, holeCol, fromCol, toCol));\n }\n }\n\n const summary = {\n [SEVERITY_ERROR]: issues.filter((issue) => issue.severity === SEVERITY_ERROR).length,\n [SEVERITY_WARNING]: issues.filter((issue) => issue.severity === SEVERITY_WARNING).length,\n [SEVERITY_INFO]: issues.filter((issue) => issue.severity === SEVERITY_INFO).length,\n };\n return { summary, issues };\n}\n\n/**\n * Synthesize a second survey station for any hole with only one.\n *\n * Mirrors `baselode.drill.validate.fix_single_station_surveys` — appends\n * a duplicate row at `collar.max_depth` (when available, otherwise\n * `depth + 1.0`) preserving azimuth/dip.\n */\nexport function fixSingleStationSurveys(\n survey,\n collar,\n { holeCol = HOLE_ID, depthCol = DEPTH, maxDepthCol = MAX_DEPTH } = {},\n) {\n if (!survey || !survey.length) return [];\n\n const maxDepthLookup = collar ? buildMaxDepthLookup(collar, holeCol, maxDepthCol) : new Map();\n const grouped = groupBy(survey, holeCol);\n const additions = [];\n for (const [holeId, group] of grouped) {\n if (group.length !== 1) continue;\n const original = group[0];\n const originalDepth = Number(original[depthCol]);\n let anchorDepth = maxDepthLookup.get(holeId);\n if (anchorDepth == null || anchorDepth <= originalDepth) {\n anchorDepth = originalDepth + 1.0;\n }\n additions.push({ ...original, [depthCol]: anchorDepth });\n }\n\n const extended = [...survey, ...additions];\n return extended.sort((first, second) => {\n const firstHole = String(first[holeCol] ?? '');\n const secondHole = String(second[holeCol] ?? '');\n if (firstHole !== secondHole) return firstHole < secondHole ? -1 : 1;\n return Number(first[depthCol]) - Number(second[depthCol]);\n });\n}\n\n/**\n * Drop interval rows whose `hole_id` is not in the collar table.\n *\n * Complement of the `orphan_intervals` validation check.\n *\n * @returns {Array<Object>} new rows containing only those whose hole_id is in collar\n */\nexport function dropOrphanIntervals(table, collar, { holeCol = HOLE_ID } = {}) {\n if (!table || !table.length) return [];\n if (!collar || !collar.length) return [];\n const validHoleIds = new Set(\n collar.map((row) => row && row[holeCol]).filter((value) => value != null),\n );\n return table.filter((row) => row && validHoleIds.has(row[holeCol]));\n}\n\n/**\n * Swap `from` and `to` where the values are inverted.\n *\n * Fixes the common data-entry typo where `to < from`. Rows where\n * `to === from` are genuinely malformed (zero-length intervals) and are\n * left untouched. All other columns preserved.\n *\n * @returns {Array<Object>} new rows with inverted intervals corrected\n */\nexport function swapInvertedIntervals(table, { fromCol = FROM, toCol = TO } = {}) {\n if (!table || !table.length) return [];\n return table.map((row) => {\n if (!row) return row;\n const fromDepth = Number(row[fromCol]);\n const toDepth = Number(row[toCol]);\n if (Number.isFinite(fromDepth) && Number.isFinite(toDepth) && toDepth < fromDepth) {\n return { ...row, [fromCol]: toDepth, [toCol]: fromDepth };\n }\n return { ...row };\n });\n}\n\n/**\n * Wrap survey azimuths into `[0, 360)`.\n *\n * Folds `360` to `0`, brings negatives like `-30` to `330`, and is\n * idempotent for already-valid values. Non-numeric or `null` values are\n * left untouched.\n *\n * @returns {Array<Object>} new rows with the azimuth column normalized\n */\nexport function normalizeAzimuth(survey, { azimuthCol = AZIMUTH } = {}) {\n if (!survey || !survey.length) return [];\n return survey.map((row) => {\n if (!row) return row;\n const value = row[azimuthCol];\n if (value == null) return { ...row };\n const numeric = Number(value);\n if (Number.isNaN(numeric)) return { ...row };\n const wrapped = ((numeric % 360) + 360) % 360;\n return { ...row, [azimuthCol]: wrapped };\n });\n}\n\n/**\n * Replace `<MDL` strings with `MDL * sentinelFactor`.\n *\n * Mirrors `baselode.drill.validate.replace_below_detection_limit`.\n */\nexport function replaceBelowDetectionLimit(rows, { columns = null, sentinelFactor = 0.5 } = {}) {\n if (!rows || !rows.length) return [];\n const targetColumns = columns ? new Set(columns) : null;\n return rows.map((row) => {\n if (!row) return row;\n const out = { ...row };\n for (const [col, value] of Object.entries(row)) {\n if (targetColumns && !targetColumns.has(col)) continue;\n if (typeof value !== 'string') continue;\n const match = BDL_PATTERN.exec(value);\n if (match === null) continue;\n out[col] = Number(match[1]) * sentinelFactor;\n }\n return out;\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, DEPTH, FROM, TO, MID, GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Column name variants for depth in geophysics point logs.\n * These are tried (after normalisation) when the standard 'depth' column is absent.\n * @private\n */\nconst DEPTH_ALIASES = ['depth', 'md', 'measured_depth', 'dept', 'z'];\n\n/**\n * Resolve the depth value from a standardised geophysics row.\n *\n * Supports both point-based (single depth column) and interval-based (from/to)\n * schemas. For interval rows the midpoint is returned. The standard DEPTH\n * column is tried first; common aliases are checked as a fallback.\n *\n * @private\n * @param {object} row - Standardised row object\n * @returns {number|null}\n */\nfunction resolveDepth(row) {\n // Point-based: explicit depth column\n if (row[DEPTH] !== undefined) {\n const v = Number(row[DEPTH]);\n if (Number.isFinite(v)) return v;\n }\n for (const alias of DEPTH_ALIASES) {\n if (row[alias] !== undefined) {\n const v = Number(row[alias]);\n if (Number.isFinite(v)) return v;\n }\n }\n // Interval-based: compute midpoint from from/to\n const f = Number(row[FROM]);\n const t = Number(row[TO]);\n if (Number.isFinite(f) && Number.isFinite(t) && t >= f) return (f + t) / 2;\n return null;\n}\n\n/**\n * Discover the numeric value columns in a geophysics file.\n *\n * All columns that are not hole_id or a recognised depth alias are considered\n * candidate value columns. Only columns whose values parse as finite numbers\n * for the majority (>50 %) of rows are retained, so comment or flag columns\n * are dropped automatically.\n *\n * @private\n * @param {object[]} rows - Standardised rows\n * @returns {string[]} Ordered list of numeric value column names\n */\nfunction detectValueColumns(rows) {\n if (rows.length === 0) return [];\n const skipSet = new Set([HOLE_ID, DEPTH, FROM, TO, MID, ...DEPTH_ALIASES]);\n const candidates = Object.keys(rows[0]).filter((c) => !skipSet.has(c));\n\n return candidates.filter((col) => {\n let numericCount = 0;\n for (const row of rows) {\n const v = Number(row[col]);\n if (Number.isFinite(v)) numericCount++;\n }\n return numericCount / rows.length > 0.5;\n });\n}\n\n/**\n * Parse a geophysics point-log CSV into per-hole arrays of depths and values.\n *\n * Unlike assay intervals (from/to), geophysics logs contain one row per sample\n * depth — typically at a fixed spacing (e.g. 0.1 m for gamma probes). This\n * loader handles the higher sample density gracefully; there is no upper limit\n * on the number of rows.\n *\n * ### Column detection\n * - **hole_id** — standard variants recognised by `standardizeColumns`.\n * - **depth** — standard `depth` plus common aliases: `md`, `measured_depth`,\n * `dept`, `z`.\n * - **value columns** — every other numeric column is treated as a geophysics\n * channel (gamma, resistivity, SP, etc.). Column names are preserved as-is\n * after normalisation so callers can identify individual channels.\n *\n * ### Return value\n * Returns an array of **hole objects**, one per unique hole_id:\n * ```js\n * [\n * {\n * holeId: 'GSWA001',\n * channels: {\n * gamma: { depths: [0.0, 0.1, 0.2, ...], values: [45, 47, 52, ...] },\n * sp: { depths: [0.0, 0.1, 0.2, ...], values: [-12, -11, -9, ...] },\n * }\n * },\n * ...\n * ]\n * ```\n * The `channels` object keys match the normalised CSV column names. Each\n * channel only includes rows where both depth and value are finite numbers.\n *\n * @param {string} csvText - Raw CSV text\n * @param {object} [columnMap] - Optional `{ rawName: standardName }` overrides\n * @returns {Array<{holeId: string, channels: object}>}\n */\nexport function parseGeophysicsCSV(csvText, columnMap = null) {\n const result = Papa.parse(csvText, {\n header: true,\n skipEmptyLines: true,\n dynamicTyping: false,\n });\n\n const rawRows = result.data || [];\n if (rawRows.length === 0) return [];\n\n const rows = rawRows.map((r) => standardizeColumns(r, null, columnMap));\n\n // Discover value columns from the full dataset\n const valueCols = detectValueColumns(rows);\n\n // Group rows by hole\n const byHole = new Map();\n for (const row of rows) {\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n\n const depth = resolveDepth(row);\n if (depth === null || depth < 0) continue;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({ depth, row });\n }\n\n const holes = [];\n for (const [holeId, samples] of byHole) {\n // Sort by depth (ascending)\n samples.sort((a, b) => a.depth - b.depth);\n\n const channels = {};\n for (const col of valueCols) {\n const depths = [];\n const values = [];\n for (const { depth, row } of samples) {\n const v = Number(row[col]);\n if (Number.isFinite(v) && v !== GEOPHYSICS_NULL_SENTINEL) {\n depths.push(depth);\n values.push(v);\n }\n }\n if (depths.length >= 2) {\n channels[col] = { depths, values };\n }\n }\n\n if (Object.keys(channels).length > 0) {\n holes.push({ holeId, channels });\n }\n }\n\n return holes;\n}\n\n/**\n * Convert the output of `parseGeophysicsCSV` into the strip log format\n * expected by `Baselode3DScene.setStripLogs`.\n *\n * A single channel from each hole becomes one strip log entry. If a hole has\n * multiple channels, pass `channel` to select which one (defaults to the first\n * channel found).\n *\n * @param {Array<{holeId: string, channels: object}>} geophysicsHoles\n * Output of `parseGeophysicsCSV`.\n * @param {string} [channel]\n * Channel name to use. When omitted, the first available channel per hole\n * is used.\n * @param {object} [options]\n * Strip log display options forwarded to each entry's `options` field.\n * See `normalizeStripLogOptions` for available keys.\n * @returns {Array<{holeId: string, depths: number[], values: number[], options: object}>}\n */\nexport function geophysicsToStripLogs(geophysicsHoles, channel = null, options = {}) {\n const stripLogs = [];\n for (const { holeId, channels } of geophysicsHoles) {\n const key = channel && channels[channel] ? channel : Object.keys(channels)[0];\n if (!key) continue;\n const { depths, values } = channels[key];\n stripLogs.push({ holeId, depths, values, options });\n }\n return stripLogs;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Parse a single LAS header mnemonic line.\n *\n * LAS header lines have the format:\n * MNEM.UNIT value : description\n *\n * @private\n * @param {string} line\n * @returns {{ mnem: string, unit: string, value: string, description: string } | null}\n */\nfunction parseMnemonicLine(line) {\n const dotIdx = line.indexOf('.');\n if (dotIdx < 0) return null;\n\n const mnem = line.slice(0, dotIdx).trim().toUpperCase();\n const afterDot = line.slice(dotIdx + 1);\n\n // Unit is the first non-whitespace token after the dot\n const unitMatch = afterDot.match(/^(\\S*)\\s*(.*)/s);\n const unit = unitMatch ? unitMatch[1] : '';\n const valueAndDesc = unitMatch ? unitMatch[2] : afterDot;\n\n // Description follows the last colon\n const lastColon = valueAndDesc.lastIndexOf(':');\n const value = lastColon >= 0 ? valueAndDesc.slice(0, lastColon).trim() : valueAndDesc.trim();\n const description = lastColon >= 0 ? valueAndDesc.slice(lastColon + 1).trim() : '';\n\n return { mnem, unit, value, description };\n}\n\n/**\n * Split a LAS text into named sections.\n *\n * @private\n * @param {string} lasText\n * @returns {Array<{ type: string, lines: string[] }>}\n */\nfunction splitSections(lasText) {\n const sections = [];\n let current = null;\n\n for (const raw of lasText.split(/\\r?\\n/)) {\n const line = raw.trimEnd();\n if (line.startsWith('~')) {\n // Section type is the first letter after ~, uppercased\n const type = line.slice(1, 2).toUpperCase();\n current = { type, lines: [] };\n sections.push(current);\n } else if (current) {\n current.lines.push(line);\n }\n }\n\n return sections;\n}\n\n/**\n * Parse a LAS header section into a map of mnemonic → parsed entry.\n *\n * @private\n * @param {string[] | undefined} lines\n * @returns {Record<string, { mnem: string, unit: string, value: string, description: string }>}\n */\nfunction parseHeaderSection(lines = []) {\n const map = {};\n for (const line of lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) map[parsed.mnem] = parsed;\n }\n return map;\n}\n\n/**\n * Parse a LAS 1.2 or 2.0 file into the same per-hole channel format returned\n * by {@link parseGeophysicsCSV}.\n *\n * LAS (Log ASCII Standard) is the industry-standard format for wireline log\n * data. Measurements are point-based — one row per sample depth — so the\n * returned channel objects use a ``depth`` axis rather than from/to intervals.\n *\n * ### LAS → baselode mapping\n *\n * | LAS section | Field | Baselode field |\n * |-------------|--------------|------------------|\n * | `~WELL` | WELL/UWI/API | `holeId` |\n * | `~WELL` | NULL | null sentinel |\n * | `~CURVE` | first curve | depth axis |\n * | `~CURVE` | other curves | channel names |\n * | `~A` | data rows | depths + values |\n *\n * ### Return value\n *\n * Returns an array with a single element (LAS files describe one hole):\n * ```js\n * [\n * {\n * holeId: 'MKC435',\n * units: { gamma: 'GAPI', density: 'G/CC' },\n * channels: {\n * gamma: { depths: [0.1, 0.2, ...], values: [45, 52, ...] },\n * density: { depths: [1.1, 1.2, ...], values: [2.3, 2.4, ...] },\n * }\n * }\n * ]\n * ```\n *\n * The `units` object contains the LAS unit string for each channel (lowercased\n * mnemonic key). Channels with fewer than 2 valid samples are omitted.\n *\n * ### Limitations\n *\n * `WRAP YES` (multi-line data rows) is not supported; a console warning is\n * emitted and parsing continues on a best-effort basis.\n *\n * @param {string} lasText - Raw LAS file text\n * @param {object} [options]\n * @param {string} [options.holeId] - Override the hole identifier\n * @param {number} [options.nullSentinel] - Override the null sentinel value\n * @returns {Array<{ holeId: string, units: object, channels: object }>}\n */\nexport function parseLasFile(lasText, options = {}) {\n const sections = splitSections(lasText);\n\n const versionSection = sections.find((s) => s.type === 'V');\n const wellSection = sections.find((s) => s.type === 'W');\n const curveSection = sections.find((s) => s.type === 'C');\n const dataSection = sections.find((s) => s.type === 'A');\n\n if (!dataSection) return [];\n\n const version = parseHeaderSection(versionSection?.lines);\n const well = parseHeaderSection(wellSection?.lines);\n const curveEntries = [];\n for (const line of (curveSection?.lines ?? [])) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) curveEntries.push(parsed);\n }\n\n // Warn on unsupported WRAP YES\n const wrap = version.WRAP?.value?.trim().toUpperCase() ?? 'NO';\n if (wrap === 'YES') {\n console.warn(\n 'parseLasFile: WRAP YES is not supported. Data rows may be read incorrectly.'\n );\n }\n\n // Resolve hole_id\n const holeId =\n options.holeId ??\n (well.WELL?.value?.trim() || null) ??\n (well.UWI?.value?.trim() || null) ??\n (well.API?.value?.trim() || null) ??\n 'unknown';\n\n // Resolve null sentinel (LAS file value takes precedence over default)\n let nullSentinel = GEOPHYSICS_NULL_SENTINEL;\n if (well.NULL?.value) {\n const v = Number(well.NULL.value);\n if (Number.isFinite(v)) nullSentinel = v;\n }\n if (options.nullSentinel !== undefined) {\n nullSentinel = options.nullSentinel;\n }\n\n if (curveEntries.length === 0) return [];\n\n // First curve is always depth\n const valueCurves = curveEntries.slice(1); // skip depth curve\n\n // Collect units for each value channel\n const units = {};\n for (const { mnem, unit } of valueCurves) {\n units[mnem.toLowerCase()] = unit;\n }\n\n // Accumulate channel data\n const channelData = {};\n for (const { mnem } of valueCurves) {\n channelData[mnem.toLowerCase()] = { depths: [], values: [] };\n }\n\n for (const line of dataSection.lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const tokens = trimmed.split(/\\s+/);\n if (tokens.length < 2) continue;\n\n const depth = Number(tokens[0]);\n if (!Number.isFinite(depth) || depth === nullSentinel) continue;\n\n for (let i = 0; i < valueCurves.length; i++) {\n const key = valueCurves[i].mnem.toLowerCase();\n const v = Number(tokens[i + 1]);\n if (Number.isFinite(v) && v !== nullSentinel) {\n channelData[key].depths.push(depth);\n channelData[key].values.push(v);\n }\n }\n }\n\n // Drop channels with fewer than 2 valid samples\n const channels = {};\n for (const [key, data] of Object.entries(channelData)) {\n if (data.depths.length >= 2) channels[key] = data;\n }\n\n if (Object.keys(channels).length === 0) return [];\n\n return [{ holeId, units, channels }];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Plotly visualization builders for structural measurements.\n *\n * Provides:\n * - buildTadpoleConfig: 1D strip log tadpole plot (dip head + azimuth tail)\n * - buildStructuralStripConfig: categorical interval strip log\n * - buildStrikeDipSymbol: 2D map strike/dip symbol geometry\n */\n\nimport { AZIMUTH, DEPTH, DIP, FROM, TO } from '../data/datamodel.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 36 };\nconst STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\nconst STRIPLOG_AXIS_TITLE_FONT_SIZE = 11;\nconst STRIPLOG_XAXIS_TITLE_STANDOFF = 6;\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.xaxis && layout.xaxis.title) || {}),\n font: {\n ...(((layout.xaxis && layout.xaxis.title && layout.xaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n standoff: (layout.xaxis && layout.xaxis.title && layout.xaxis.title.standoff)\n ?? STRIPLOG_XAXIS_TITLE_STANDOFF,\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.yaxis && layout.yaxis.title) || {}),\n font: {\n ...(((layout.yaxis && layout.yaxis.title && layout.yaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n };\n}\n\n/**\n * Build a Plotly tadpole log config for structural point measurements.\n *\n * Each measurement renders as a circle (head) at its depth with a tail\n * pointing toward the dip direction. Tail length is proportional to dip magnitude.\n *\n * @param {Array<Object>} points - Structural point rows\n * @param {Object} opts\n * @param {number} [opts.tailScale=0.3] - Controls tail length relative to dip magnitude\n * @param {string|null} [opts.colorBy=null] - Column name to color heads by (e.g. 'defect')\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.depthCol='depth'] - Column for measured depth\n * @param {string} [opts.dipCol='dip'] - Column for dip angle\n * @param {string} [opts.azCol='azimuth'] - Column for dip direction\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildTadpoleConfig(points, {\n tailScale = 5,\n colorBy = null,\n palette = DEFAULT_PALETTE,\n depthCol = DEPTH,\n dipCol = DIP,\n azCol = AZIMUTH,\n template = undefined,\n} = {}) {\n const valid = points.filter(p =>\n p[depthCol] != null && p[dipCol] != null && p[azCol] != null\n );\n\n if (!valid.length) {\n return { data: [], layout: {} };\n }\n\n // Build color map for categories\n const colorMap = {};\n if (colorBy) {\n const categories = [...new Set(valid.map(p => p[colorBy]).filter(v => v != null))].sort();\n categories.forEach((cat, i) => { colorMap[cat] = palette[i % palette.length]; });\n }\n\n // Group by category for legend traces\n const byCat = new Map();\n const shapes = [];\n\n for (const p of valid) {\n const depth = Number(p[depthCol]);\n const dip = Number(p[dipCol]);\n const az = Number(p[azCol]);\n const cat = colorBy ? (p[colorBy] ?? '_default') : '_default';\n const color = colorBy ? (colorMap[cat] ?? '#0f172a') : '#0f172a';\n\n if (!byCat.has(cat)) {\n byCat.set(cat, { xs: [], ys: [], dips: [], azs: [], color });\n }\n const group = byCat.get(cat);\n // Head positioned at x=dip (degrees)\n group.xs.push(dip);\n group.ys.push(depth);\n group.dips.push(dip);\n group.azs.push(az);\n\n // Tail: starts at (dip, depth), direction encodes azimuth.\n // Length scales with dip magnitude (in degree units on the x-axis).\n const azRad = (az * Math.PI) / 180;\n const length = tailScale * (Math.abs(dip) / 90);\n const dx = Math.sin(azRad) * length; // x-component (degrees)\n const dy = Math.cos(azRad) * length; // y-component (degrees, visual only)\n\n shapes.push({\n type: 'line',\n x0: dip, y0: depth,\n x1: dip + dx, y1: depth + dy,\n line: { color, width: 2 },\n });\n }\n\n const data = [];\n const showLegend = colorBy && byCat.size > 1;\n\n for (const [cat, group] of byCat.entries()) {\n data.push({\n type: 'scatter',\n x: group.xs,\n y: group.ys,\n mode: 'markers',\n name: cat !== '_default' ? String(cat) : undefined,\n marker: { size: 8, color: group.color },\n showlegend: showLegend && cat !== '_default',\n customdata: group.dips.map((d, i) => [d, group.azs[i]]),\n hovertemplate: 'Depth: %{y}<br>Dip: %{customdata[0]}<br>Az: %{customdata[1]}<extra></extra>',\n });\n }\n\n const layout = {\n shapes,\n height: 400,\n margin: { l: 40, r: 10, t: 10, b: 40 },\n xaxis: {\n title: 'Dip (°)',\n autorange: true,\n fixedrange: true,\n zeroline: true,\n tickvals: [-90, -60, -30, 0, 30, 60, 90],\n },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: !!showLegend,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout };\n}\n\n/**\n * Build a Plotly categorical strip log config for structural interval measurements.\n *\n * @param {Array<Object>} intervals - Structural interval rows\n * @param {Object} opts\n * @param {string} [opts.labelCol='defect'] - Column for interval label/color\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildStructuralStripConfig(intervals, {\n labelCol = 'structure_type',\n palette = DEFAULT_PALETTE,\n fromCol = FROM,\n toCol = TO,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .filter(iv => {\n const lv = iv[labelCol];\n if (lv == null) return false;\n const s = String(lv).trim();\n return s !== '' && !/^(nan|null|none)$/i.test(s);\n })\n .map(iv => ({ from: Number(iv[fromCol]), to: Number(iv[toCol]), label: String(iv[labelCol]).trim() }))\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textY = [];\n const texts = [];\n\n records.forEach((rec, idx) => {\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: palette[idx % palette.length],\n line: { width: 0 },\n layer: 'below',\n });\n textY.push(0.5 * (rec.from + rec.to));\n texts.push(rec.label);\n });\n\n const data = [{\n type: 'scatter',\n x: Array(texts.length).fill(0.5),\n y: textY,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n }];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Word-wrap text at word boundaries, inserting Plotly HTML line breaks.\n * @private\n * @param {string} text\n * @param {number} charsPerLine\n * @returns {string}\n */\nfunction wrapComment(text, charsPerLine) {\n if (!text) return '';\n const words = String(text).trim().split(/\\s+/);\n const lines = [];\n let current = '';\n for (const word of words) {\n if (current && current.length + 1 + word.length > charsPerLine) {\n lines.push(current);\n current = word;\n } else {\n current = current ? `${current} ${word}` : word;\n }\n }\n if (current) lines.push(current);\n return lines.join('<br>');\n}\n\n/**\n * Build a Plotly comments log config — depth intervals with text annotations overlaid.\n *\n * Each interval is drawn as a lightly shaded rectangle spanning its from/to depth.\n * Non-empty comments are word-wrapped and centered inside the rectangle.\n * Intervals with no comment show a thin border only.\n *\n * @param {Array<Object>} intervals - Interval rows (must have from, to, and a comment column)\n * @param {Object} opts\n * @param {string} [opts.commentCol='comments'] - Column containing comment text\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {string} [opts.bgColor='#f1f5f9'] - Fill color for intervals with a comment\n * @param {string} [opts.borderColor='#cbd5e1'] - Rectangle border color\n * @param {string} [opts.textColor='#1e293b'] - Comment text color\n * @param {number} [opts.charsPerLine=18] - Characters before word-wrapping\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildCommentsConfig(intervals, {\n commentCol = 'comments',\n fromCol = FROM,\n toCol = TO,\n bgColor = '#f1f5f9',\n borderColor = '#cbd5e1',\n textColor = '#1e293b',\n charsPerLine = 18,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .map(iv => {\n const raw = iv[commentCol];\n const comment = (raw != null && String(raw).trim() !== '' && String(raw) !== 'null')\n ? String(raw).trim()\n : '';\n return { from: Number(iv[fromCol]), to: Number(iv[toCol]), comment };\n })\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textXs = [];\n const textYs = [];\n const texts = [];\n const hovers = [];\n\n for (const rec of records) {\n const mid = 0.5 * (rec.from + rec.to);\n const hasComment = !!rec.comment;\n\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: hasComment ? bgColor : 'rgba(0,0,0,0)',\n line: { color: borderColor, width: 1 },\n layer: 'below',\n });\n\n if (hasComment) {\n textXs.push(0.5);\n textYs.push(mid);\n texts.push(wrapComment(rec.comment, charsPerLine));\n hovers.push(`${rec.from.toFixed(3)}–${rec.to.toFixed(3)} m<br>${wrapComment(rec.comment, 40)}`);\n }\n }\n\n const data = textXs.length ? [{\n type: 'scatter',\n x: textXs,\n y: textYs,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n textfont: { color: textColor, size: 10 },\n hovertext: hovers,\n hoverinfo: 'text',\n showlegend: false,\n }] : [];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Compute 2D map strike/dip symbol geometry for a single structural measurement.\n *\n * Returns the geometry needed to draw a strike line and dip tick on a map.\n *\n * @param {Object} point - Structural measurement with x, y, dip, azimuth\n * @param {Object} opts\n * @param {number} [opts.symbolSize=10] - Strike line half-length in map units\n * @param {string} [opts.xCol='easting'] - X coordinate column\n * @param {string} [opts.yCol='northing'] - Y coordinate column\n * @returns {{ strike: number, dipValue: number, x: number, y: number,\n * strikeX0: number, strikeY0: number, strikeX1: number, strikeY1: number,\n * tickX1: number, tickY1: number } | null}\n */\nexport function buildStrikeDipSymbol(point, {\n symbolSize = 10,\n xCol = 'easting',\n yCol = 'northing',\n} = {}) {\n const x = point[xCol] != null ? Number(point[xCol]) : null;\n const y = point[yCol] != null ? Number(point[yCol]) : null;\n const dip = point[DIP] != null ? Number(point[DIP]) : null;\n const az = point[AZIMUTH] != null ? Number(point[AZIMUTH]) : null;\n\n if (x === null || y === null || dip === null || az === null) return null;\n\n const strike = ((az - 90) + 360) % 360;\n const strikeRad = (strike * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n\n // Strike line half-endpoints\n const dxS = symbolSize * Math.sin(strikeRad);\n const dyS = symbolSize * Math.cos(strikeRad);\n\n // Dip tick from center (length scaled by dip magnitude)\n const tickLen = symbolSize * 0.4 * (dip / 90);\n const dxD = tickLen * Math.sin(azRad);\n const dyD = tickLen * Math.cos(azRad);\n\n return {\n strike,\n dipValue: dip,\n x,\n y,\n strikeX0: x - dxS,\n strikeY0: y - dyS,\n strikeX1: x + dxS,\n strikeY1: y + dyS,\n tickX1: x + dxD,\n tickY1: y + dyD,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { DISPLAY_COMMENT, DISPLAY_TADPOLE } from '../data/columnMeta.js';\n\n/**\n * Decide what the TracePlot body should display for a given state.\n * Returns either { kind: 'chart' } when a chart should render, or\n * { kind: 'placeholder', text } with the message to show.\n *\n * Pure — does not touch the DOM or Plotly — so callers and tests can\n * verify the state matrix without rendering.\n *\n * @param {Object} state\n * @param {string} state.holeId - The selected hole id (config.holeId)\n * @param {Object|null} state.hole - The resolved hole (graph.hole)\n * @param {Array} state.holeOptions - Available holes\n * @param {string} state.property - The selected property (config.property)\n * @param {Array} state.propertyOptions - Available properties\n * @param {string} state.displayType - DISPLAY_* constant\n * @param {Array} state.points - Plot points\n * @param {string} [state.renderError] - Local plot-build error, if any\n * @returns {{ kind: 'chart' } | { kind: 'placeholder', text: string }}\n */\nexport function resolveTracePlotBody({\n holeId,\n hole,\n holeOptions,\n property,\n propertyOptions,\n displayType,\n points,\n renderError,\n}) {\n if (renderError) {\n return { kind: 'placeholder', text: `Plot error: ${renderError}` };\n }\n const holes = holeOptions || [];\n if (!holeId) {\n if (holes.length === 0) {\n return { kind: 'placeholder', text: 'No holes loaded' };\n }\n return { kind: 'placeholder', text: 'Select a hole' };\n }\n if (!hole) {\n return { kind: 'placeholder', text: `Loading ${holeId}…` };\n }\n const props = propertyOptions || [];\n if (!property) {\n if (props.length === 0) {\n return { kind: 'placeholder', text: `No properties available for hole ${holeId}` };\n }\n return { kind: 'placeholder', text: 'Select a property' };\n }\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n const pts = points || [];\n if (!isComment && !isTadpole && pts.length === 0) {\n return { kind: 'placeholder', text: `No values for ${property} in hole ${holeId}` };\n }\n return { kind: 'chart' };\n}\n\n/**\n * Read a group value off a hole option using either a string key\n * (looked up on the option object) or a function.\n *\n * @param {string|Object} holeOption\n * @param {string|Function} groupBy\n * @returns {*} the group value, or undefined if it can't be derived\n */\nexport function deriveGroupValue(holeOption, groupBy) {\n if (typeof groupBy === 'function') return groupBy(holeOption);\n if (typeof groupBy !== 'string' || !groupBy) return undefined;\n if (holeOption == null || typeof holeOption !== 'object') return undefined;\n return holeOption[groupBy];\n}\n\n/**\n * Unique, non-empty group values from a list of hole options, in\n * first-seen order.\n *\n * @param {Array} holeOptions\n * @param {string|Function} groupBy\n * @returns {Array} unique group values\n */\nexport function groupValuesFromHoles(holeOptions, groupBy) {\n const out = [];\n const seen = new Set();\n for (const h of holeOptions || []) {\n const g = deriveGroupValue(h, groupBy);\n if (g == null || g === '') continue;\n if (seen.has(g)) continue;\n seen.add(g);\n out.push(g);\n }\n return out;\n}\n\n/**\n * Filter hole options to those whose group matches groupValue.\n * Returns the full list when groupValue is empty.\n *\n * @param {Array} holeOptions\n * @param {string|Function} groupBy\n * @param {*} groupValue\n * @returns {Array}\n */\nexport function filterHolesByGroup(holeOptions, groupBy, groupValue) {\n if (groupValue == null || groupValue === '') return holeOptions || [];\n return (holeOptions || []).filter((h) => deriveGroupValue(h, groupBy) === groupValue);\n}\n\n/**\n * Decide which control selects render for a given configuration.\n *\n * @param {Object} args\n * @param {Array} args.chartOptions\n * @param {boolean} args.showHoleSelect\n * @param {boolean} args.showPropertySelect\n * @param {boolean} args.showChartTypeSelect\n * @returns {{ hole: boolean, property: boolean, chartType: boolean }}\n */\nexport function resolveTracePlotSelectVisibility({\n chartOptions,\n showHoleSelect,\n showPropertySelect,\n showChartTypeSelect,\n}) {\n const opts = chartOptions || [];\n return {\n hole: showHoleSelect !== false,\n property: showPropertySelect !== false,\n chartType: showChartTypeSelect !== false && opts.length > 1,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useRef, useState } from 'react';\nimport Plotly from 'plotly.js-dist-min';\nimport { buildPlotConfig } from './drillholeViz.js';\nimport { formatPropertyLabel } from '../data/propertyLabels.js';\nimport { buildCommentsConfig, buildTadpoleConfig } from './structuralViz.js';\nimport { getChartOptions, DISPLAY_COMMENT, DISPLAY_CATEGORICAL, DISPLAY_NUMERIC, DISPLAY_TADPOLE } from '../data/columnMeta.js';\nimport {\n resolveTracePlotBody,\n resolveTracePlotSelectVisibility,\n deriveGroupValue,\n groupValuesFromHoles,\n filterHolesByGroup,\n} from './tracePlotState.js';\nimport './TracePlot.css';\n\nexport {\n resolveTracePlotBody,\n resolveTracePlotSelectVisibility,\n deriveGroupValue,\n groupValuesFromHoles,\n filterHolesByGroup,\n};\n\nconst DEFAULT_NUMERIC_CHART_TYPE = 'markers+line';\n\n/**\n * Resolve chart type from available options.\n * @private\n */\nfunction resolveChartType(displayType, requestedChartType) {\n const chartOptions = getChartOptions(displayType);\n if (chartOptions.some((opt) => opt.value === requestedChartType)) return requestedChartType;\n return chartOptions[0]?.value || DEFAULT_NUMERIC_CHART_TYPE;\n}\n\nfunction holeOptionAsTuple(h) {\n const value = typeof h === 'string' ? h : h.holeId;\n const label = typeof h === 'string' ? h : (h.label || h.holeId);\n return [value, label];\n}\n\nfunction genericOptionAsTuple(o) {\n const value = typeof o === 'string' ? o : o.value;\n const label = typeof o === 'string' ? o : (o.label ?? o.value);\n return [value, label];\n}\n\nfunction renderHoleSelector({ selector, holeOptions, selectedHoleId, onConfigChange }) {\n const kind = selector?.kind || 'hole';\n\n if (kind === 'field') {\n const value = selector.value ?? '';\n const opts = selector.options || [];\n const label = selector.label || 'Selection';\n return (\n <select\n className=\"plot-select plot-select--field\"\n value={value}\n onChange={(e) => selector.onChange && selector.onChange(e.target.value)}\n disabled={opts.length === 0}\n aria-label={label}\n >\n {opts.length === 0 && <option value=\"\">—</option>}\n {!value && opts.length > 0 && (\n <option value=\"\" disabled hidden>{`Select ${label.toLowerCase()}`}</option>\n )}\n {opts.map((o) => {\n const [v, l] = genericOptionAsTuple(o);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n );\n }\n\n if (kind === 'group+hole') {\n const groupBy = selector.groupBy;\n const groupValue = selector.groupValue ?? '';\n const groupLabel = selector.groupLabel || 'Group';\n const groupOptions = selector.groupOptions\n || groupValuesFromHoles(holeOptions, groupBy);\n const visibleHoles = filterHolesByGroup(holeOptions, groupBy, groupValue);\n return (\n <>\n <select\n className=\"plot-select plot-select--group\"\n value={groupValue}\n onChange={(e) => selector.onGroupChange && selector.onGroupChange(e.target.value)}\n disabled={groupOptions.length === 0}\n aria-label={groupLabel}\n >\n {groupOptions.length === 0 && <option value=\"\">No {groupLabel.toLowerCase()}s</option>}\n {!groupValue && groupOptions.length > 0 && (\n <option value=\"\" disabled hidden>{`Select ${groupLabel.toLowerCase()}`}</option>\n )}\n {groupOptions.map((g) => {\n const [v, l] = genericOptionAsTuple(g);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n <select\n className=\"plot-select plot-select--hole\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n disabled={visibleHoles.length === 0}\n aria-label=\"Hole\"\n >\n {visibleHoles.length === 0 && <option value=\"\">No holes</option>}\n {!selectedHoleId && visibleHoles.length > 0 && (\n <option value=\"\" disabled hidden>Select a hole</option>\n )}\n {visibleHoles.map((h) => {\n const [v, l] = holeOptionAsTuple(h);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n </>\n );\n }\n\n // kind === 'hole' (default)\n const enabled = holeOptions.length > 0;\n return (\n <select\n className=\"plot-select plot-select--hole\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n disabled={!enabled}\n aria-label=\"Hole\"\n >\n {!enabled && <option value=\"\">No holes loaded</option>}\n {!selectedHoleId && enabled && (\n <option value=\"\" disabled hidden>Select a hole</option>\n )}\n {holeOptions.map((h) => {\n const [v, l] = holeOptionAsTuple(h);\n return <option key={v} value={v}>{l}</option>;\n })}\n </select>\n );\n}\n\n/**\n * Plotly-based trace plot component for drillhole data.\n *\n * Hole / property / chart-type selects render in every state — including\n * the empty, loading, no-data and error states — so the user can always\n * change selection. The body switches between the Plotly chart and a\n * placeholder message; the controls do not move.\n *\n * @param {Object} props\n * @param {Object} props.config - Plot configuration {holeId, property, chartType}\n * @param {Object} props.graph - Graph data {hole, points, displayType, isCategorical, isComment, loading}\n * @param {Array} props.holeOptions - Available holes for dropdown\n * @param {Array} props.propertyOptions - Available properties for dropdown\n * @param {Function} props.onConfigChange - Handler for configuration changes\n * @param {Object} [props.propertyMeta] - Optional per-property metadata map\n * (`{ [property]: { label?, unit?, sourceAttribute? } }`). When the selected\n * property has an entry, its unit / source attribute are folded into the\n * axis title, hover tooltip and property dropdown label — e.g. \"Au (ppm)\".\n * The bare property key is still used for selection and `onConfigChange`.\n * @param {Object} [props.template] - Plotly template to apply. Defaults to the Baselode template.\n * @param {boolean} [props.showHoleSelect=true] - Render the hole selector area.\n * @param {boolean} [props.showPropertySelect=true] - Render the property select.\n * @param {boolean} [props.showChartTypeSelect=true] - Render the chart-type select (when >1 option).\n * @param {Object} [props.holeSelector] - Shape of the hole selector area.\n * Defaults to `{ kind: 'hole' }` (single hole dropdown).\n * - `{ kind: 'hole' }` — one dropdown over `holeOptions`, value = `config.holeId`.\n * - `{ kind: 'group+hole', groupBy, groupValue, onGroupChange, groupLabel?, groupOptions? }` —\n * a group dropdown plus a hole dropdown filtered to that group. `groupBy` is\n * either a string key on each hole option or a `(holeOption) => value` function.\n * - `{ kind: 'field', value, options, onChange, label? }` — a single dropdown\n * bound to an arbitrary field, fully controlled by the caller.\n * @returns {JSX.Element}\n */\nfunction TracePlot({\n config,\n graph,\n holeOptions = [],\n propertyOptions = [],\n propertyMeta,\n onConfigChange,\n template,\n showHoleSelect = true,\n showPropertySelect = true,\n showChartTypeSelect = true,\n holeSelector,\n}) {\n const bodyRef = useRef(null);\n const containerRef = useRef(null);\n const hole = graph?.hole;\n const points = graph?.points || [];\n const property = config?.property || '';\n const meta = propertyMeta?.[property];\n const chartType = config?.chartType || DEFAULT_NUMERIC_CHART_TYPE;\n const selectedHoleId = config?.holeId || '';\n\n // Derive display type from graph metadata (set by useDrillholeTraceGrid)\n const displayType = graph?.displayType\n || (graph?.isComment ? DISPLAY_COMMENT : (graph?.isCategorical ? DISPLAY_CATEGORICAL : DISPLAY_NUMERIC));\n\n const chartOptions = getChartOptions(displayType);\n const effectiveChartType = resolveChartType(displayType, chartType);\n\n const [renderError, setRenderError] = useState('');\n const [plotSize, setPlotSize] = useState({ width: 0, height: 0 });\n\n const bodyState = resolveTracePlotBody({\n holeId: selectedHoleId,\n hole,\n holeOptions,\n property,\n propertyOptions,\n displayType,\n points,\n renderError,\n });\n const isPlaceholder = bodyState.kind !== 'chart';\n\n const visibility = resolveTracePlotSelectVisibility({\n chartOptions,\n showHoleSelect,\n showPropertySelect,\n showChartTypeSelect,\n });\n const propertySelectEnabled = propertyOptions.length > 0;\n\n useEffect(() => {\n const body = bodyRef.current;\n if (!body || typeof ResizeObserver === 'undefined') return undefined;\n\n let frame = 0;\n const updatePlotSize = () => {\n if (frame) cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const width = Math.max(0, Math.floor(body.clientWidth));\n const height = Math.max(0, Math.floor(body.clientHeight));\n setPlotSize((prev) => (\n prev.width === width && prev.height === height ? prev : { width, height }\n ));\n });\n };\n\n updatePlotSize();\n const observer = new ResizeObserver(updatePlotSize);\n observer.observe(body);\n\n return () => {\n if (frame) cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, []);\n\n useEffect(() => {\n if (bodyState.kind !== 'chart') return;\n const target = containerRef.current;\n if (!target) return;\n if (plotSize.width <= 0 || plotSize.height <= 0) return;\n\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n\n let plotData;\n try {\n if (isComment) {\n plotData = buildCommentsConfig(points, { commentCol: property, fromCol: 'from', toCol: 'to' });\n } else if (isTadpole) {\n plotData = buildTadpoleConfig(points);\n } else {\n plotData = buildPlotConfig({\n points,\n isCategorical: displayType === DISPLAY_CATEGORICAL,\n property,\n chartType: effectiveChartType,\n template,\n meta,\n });\n }\n } catch (err) {\n console.error('Plot build error', err);\n setRenderError(err?.message || 'Plot build error');\n return;\n }\n\n if (!plotData?.data || plotData.data.length === 0) {\n if (!isComment) return;\n }\n\n const plotConfig = {\n displayModeBar: true,\n responsive: false,\n modeBarButtonsToRemove: ['select2d', 'lasso2d', 'autoScale2d']\n };\n const layout = {\n ...plotData.layout,\n autosize: false,\n width: plotSize.width,\n height: plotSize.height,\n };\n\n try {\n setRenderError('');\n Plotly.react(target, plotData.data, layout, plotConfig);\n } catch (err) {\n console.error('Plot render error', err);\n setRenderError(err?.message || 'Plot render error');\n }\n\n return () => {\n if (target) {\n try {\n Plotly.purge(target);\n } catch (err) {\n console.warn('Plot purge error', err);\n }\n }\n };\n }, [\n bodyState.kind,\n hole,\n property,\n meta,\n effectiveChartType,\n displayType,\n points,\n template,\n plotSize.width,\n plotSize.height,\n ]);\n\n useEffect(() => {\n const target = containerRef.current;\n if (!target || typeof ResizeObserver === 'undefined') return undefined;\n const resizeObserver = new ResizeObserver(() => {\n try {\n if (target && target.data) {\n const width = Math.max(0, Math.floor(target.clientWidth));\n const height = Math.max(0, Math.floor(target.clientHeight));\n if (width > 0 && height > 0) {\n Plotly.relayout(target, { width, height, autosize: false });\n }\n }\n } catch (err) {\n console.warn('Plot resize error', err);\n }\n });\n resizeObserver.observe(target);\n return () => resizeObserver.disconnect();\n }, [bodyState.kind]);\n\n return (\n <div className={`plot-card${isPlaceholder ? ' empty' : ''}`}>\n <header className=\"plot-card__controls\">\n {visibility.hole && (\n <div className=\"plot-title\">\n {renderHoleSelector({\n selector: holeSelector,\n holeOptions,\n selectedHoleId,\n onConfigChange,\n })}\n </div>\n )}\n {(visibility.property || visibility.chartType) && (\n <div className=\"plot-controls column\">\n {visibility.property && (\n <select\n className=\"plot-select plot-select--property\"\n value={property}\n onChange={(e) => onConfigChange && onConfigChange({ property: e.target.value })}\n disabled={!propertySelectEnabled}\n aria-label=\"Property\"\n >\n {!propertySelectEnabled && (\n <option value=\"\">—</option>\n )}\n {!property && propertySelectEnabled && (\n <option value=\"\" disabled hidden>Select a property</option>\n )}\n {propertyOptions.map((p) => (\n <option key={p} value={p}>{formatPropertyLabel(p, propertyMeta?.[p])}</option>\n ))}\n </select>\n )}\n {visibility.chartType && (\n <select\n className=\"plot-select plot-select--chart-type\"\n value={effectiveChartType}\n onChange={(e) => onConfigChange && onConfigChange({ chartType: e.target.value })}\n aria-label=\"Chart type\"\n >\n {chartOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n )}\n </header>\n <div className=\"plot-card__body\" ref={bodyRef}>\n {bodyState.kind === 'chart'\n ? <div className=\"plotly-chart\" ref={containerRef} />\n : <div className=\"placeholder\">{bodyState.text}</div>\n }\n </div>\n </div>\n );\n}\n\nexport default TracePlot;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n deriveAssayProps,\n loadAssayHole,\n loadAssayMetadata\n} from '../data/assayDataLoader.js';\nimport {\n buildTraceConfigsForHoleIds,\n coerceChartTypeForProperty,\n reorderHoleIds\n} from '../data/traceGridConfig.js';\nimport { buildIntervalPoints, holeHasData } from './drillholeViz.js';\n\n/**\n * Merge two arrays of hole objects by holeId.\n * For holes that appear in both arrays, their points are concatenated.\n * @private\n */\nfunction mergeHoleSets(primary, extra) {\n if (!extra?.length) return primary;\n const byId = new Map(primary.map((h) => [h.id || h.holeId, { ...h }]));\n for (const eh of extra) {\n const id = eh.id || eh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...(existing.points || []), ...(eh.points || [])] });\n } else {\n byId.set(id, eh);\n }\n }\n return Array.from(byId.values());\n}\n\n/**\n * Build comment-type interval points from a hole.\n * Unlike buildIntervalPoints, this keeps intervals with empty/null comment values\n * so that buildCommentsConfig can draw the interval border rectangles.\n * @private\n */\nfunction buildCommentPoints(hole, property) {\n if (!hole || !property) return [];\n const seen = new Set();\n const out = [];\n for (const p of hole.points || []) {\n const from = Number(p.from ?? p.samp_from ?? p.depth_from ?? p.from_depth);\n const to = Number(p.to ?? p.samp_to ?? p.depth_to ?? p.to_depth);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const key = `${from}-${to}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ from, to, [property]: p[property] ?? '' });\n }\n return out;\n}\n\n/**\n * React hook for managing a grid of drillhole trace plots.\n * Handles loading assay data, optional extra hole data (e.g. structural intervals),\n * column metadata classification, and trace config coordination.\n *\n * @param {Object} options\n * @param {string} options.initialFocusedHoleId - Initial focused hole ID\n * @param {File|Blob|null} options.sourceFile - Assay data CSV file\n * @param {Array<Object>} options.extraHoles - Pre-parsed extra hole data (e.g. structural)\n * @param {number} options.plotCount - Number of plots in grid (default: 4)\n * @returns {Object} Hook state and actions\n */\nexport default function useDrillholeTraceGrid({\n initialFocusedHoleId = '',\n sourceFile = null,\n extraHoles = [],\n plotCount = 4\n} = {}) {\n const [holes, setHoles] = useState([]);\n const [holeIds, setHoleIds] = useState([]);\n const [numericProps, setNumericProps] = useState([]);\n const [categoricalProps, setCategoricalProps] = useState([]);\n const [commentProps, setCommentProps] = useState([]);\n const [columnMeta, setColumnMeta] = useState({});\n const [defaultProp, setDefaultProp] = useState('');\n const [traceConfigs, setTraceConfigs] = useState([]);\n const [error, setError] = useState('');\n const [focusedHoleId, setFocusedHoleId] = useState(initialFocusedHoleId || '');\n const [loadingHoles, setLoadingHoles] = useState([]);\n const loadedSourceFileRef = useRef(null);\n\n const applyAssayState = (state) => {\n if (!state) return;\n setError('');\n setHoles(state.holes || []);\n setHoleIds((state.holes || []).map((h) => ({\n holeId: h.id || h.holeId\n })).filter((h) => h.holeId));\n setNumericProps(state.numericProps || []);\n setCategoricalProps(state.categoricalProps || []);\n setCommentProps(state.commentProps || []);\n setColumnMeta(state.columnMeta || {});\n setDefaultProp(state.defaultProp || '');\n setTraceConfigs(state.traceConfigs || []);\n };\n\n // Load metadata (hole IDs) from assay CSV on first mount\n useEffect(() => {\n if (!sourceFile || loadedSourceFileRef.current === sourceFile) return;\n loadedSourceFileRef.current = sourceFile;\n loadAssayMetadata(sourceFile)\n .then((ids) => {\n if (!ids) return;\n const uniqueIds = Array.from(new Map(ids.map((h) => [h.holeId, h])).values());\n setHoleIds(uniqueIds);\n setTraceConfigs(buildTraceConfigsForHoleIds({\n holeIds: uniqueIds.map((h) => h.holeId),\n focusedHoleId,\n plotCount,\n defaultProp: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n }));\n })\n .catch((err) => {\n console.info('Assay metadata load skipped:', err.message);\n });\n }, [sourceFile, focusedHoleId, plotCount, categoricalProps, commentProps]);\n\n // Inject extra holes (structural etc.) into holeIds — always, regardless of whether\n // an assay sourceFile is also present. This ensures structural-only holes appear in\n // the dropdown even when assay data is loaded from a separate file.\n useEffect(() => {\n if (!extraHoles?.length) return;\n const ids = extraHoles\n .map((h) => ({ holeId: h.id || h.holeId }))\n .filter((h) => h.holeId);\n setHoleIds((prev) => {\n const existing = new Set(prev.map((h) => h.holeId));\n const newIds = ids.filter((h) => !existing.has(h.holeId));\n return newIds.length ? [...prev, ...newIds] : prev;\n });\n }, [extraHoles]);\n\n useEffect(() => {\n setError((prev) => (prev && prev.startsWith('Loading ') && prev.includes(' for hole ') ? prev : ''));\n }, [traceConfigs]);\n\n useEffect(() => {\n if (!holeIds.length) {\n setTraceConfigs([]);\n return;\n }\n const orderedHoleIds = reorderHoleIds(holeIds.map((h) => h.holeId), focusedHoleId);\n setTraceConfigs((prev) => {\n const next = Array.from({ length: plotCount }).map((_, idx) => {\n const existing = prev[idx] || {};\n const holeId = holeIds.some((h) => h.holeId === existing.holeId) ? existing.holeId : orderedHoleIds[idx] || holeIds[idx]?.holeId || '';\n const property = existing.property || defaultProp;\n const chartType = coerceChartTypeForProperty({\n property,\n chartType: existing.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n return { holeId, property, chartType };\n });\n return next;\n });\n }, [holeIds, focusedHoleId, defaultProp, categoricalProps, commentProps, plotCount]);\n\n // Load assay hole data on demand as trace configs change\n useEffect(() => {\n if (!sourceFile) return;\n const needed = traceConfigs.map((cfg) => cfg.holeId).filter(Boolean);\n needed.forEach((holeId) => {\n const already = holes.some((h) => (h.id || h.holeId) === holeId);\n const loading = loadingHoles.includes(holeId);\n if (already || loading) return;\n setLoadingHoles((prev) => [...prev, holeId]);\n loadAssayHole(sourceFile, holeId)\n .then((hole) => {\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n if (!hole) return;\n setHoles((prev) => {\n const merged = mergeHoleSets(\n [...prev.filter((h) => (h.id || h.holeId) !== holeId), hole],\n extraHoles\n );\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) {\n setDefaultProp(props.defaultProp);\n setTraceConfigs((configs) => configs.map((cfg) => ({\n ...cfg,\n property: cfg.property || props.defaultProp,\n chartType: coerceChartTypeForProperty({\n property: cfg.property || props.defaultProp,\n chartType: cfg.chartType,\n categoricalProps: props.categoricalProps,\n commentProps: props.commentProps,\n numericDefaultChartType: 'markers+line'\n })\n })));\n }\n return merged;\n });\n })\n .catch((err) => {\n console.error(err);\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n setError(err.message || `Error loading hole ${holeId}`);\n });\n });\n }, [traceConfigs, sourceFile, holes, loadingHoles, defaultProp, extraHoles]);\n\n // Merge extra holes whenever they change (and assay holes are present)\n useEffect(() => {\n if (!extraHoles?.length) return;\n setHoles((prev) => {\n if (!prev.length) {\n // No assay data yet — seed from extra holes only\n const props = deriveAssayProps(extraHoles);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return extraHoles;\n }\n const merged = mergeHoleSets(prev, extraHoles);\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return merged;\n });\n }, [extraHoles]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const propertyOptions = useMemo(\n () => [...numericProps, ...categoricalProps, ...commentProps],\n [numericProps, categoricalProps, commentProps]\n );\n\n const labeledHoleOptions = useMemo(\n () => holeIds\n .map((h) => ({ holeId: h.holeId, label: h.holeId }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n [holeIds]\n );\n\n const traceGraphs = useMemo(() => {\n const allProps = [...numericProps, ...categoricalProps, ...commentProps];\n return Array.from({ length: plotCount }).map((_, idx) => {\n const cfg = traceConfigs[idx] || {};\n const hole = holes.find((h) => (h.id || h.holeId) === cfg.holeId) || null;\n\n // Per-hole property list: only columns this hole actually has data for\n const holePropertyOptions = hole\n ? allProps.filter((p) => holeHasData(hole, p))\n : allProps;\n\n let property = cfg.property || defaultProp;\n // Auto-select first available property if the configured one has no data for this hole\n if (hole && !holePropertyOptions.includes(property)) {\n property = holePropertyOptions[0] || property;\n }\n\n const isComment = commentProps.includes(property);\n const isCategorical = !isComment && categoricalProps.includes(property);\n const isTadpole = !isComment && !isCategorical && property === 'dip';\n const displayType = isComment ? 'comment' : isTadpole ? 'tadpole' : (isCategorical ? 'categorical' : 'numeric');\n\n const chartType = isTadpole ? 'tadpole' : cfg.chartType || (isComment ? 'comment' : (isCategorical ? 'categorical' : 'markers+line'));\n const holeId = cfg.holeId || hole?.id || hole?.holeId || '';\n\n const points = isTadpole\n ? (hole?.points || [])\n : isComment\n ? buildCommentPoints(hole, property)\n : buildIntervalPoints(hole, property, isCategorical);\n\n return {\n config: { holeId, property, chartType },\n hole,\n loading: loadingHoles.includes(cfg.holeId),\n isCategorical,\n isComment,\n isTadpole,\n displayType,\n points,\n propertyOptions: holePropertyOptions,\n label: holeId\n };\n });\n }, [traceConfigs, holes, defaultProp, categoricalProps, commentProps, loadingHoles, plotCount, numericProps]);\n\n const handleConfigChange = (index, patch) => {\n setTraceConfigs((prev) => {\n const next = [...prev];\n const base = next[index] || {};\n const merged = { ...base, ...patch };\n if (patch.property) {\n merged.chartType = coerceChartTypeForProperty({\n property: patch.property,\n chartType: merged.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n }\n next[index] = merged;\n return next;\n });\n };\n\n return {\n error,\n focusedHoleId,\n setFocusedHoleId,\n setError,\n holeCount: holeIds.length,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n propertyOptions,\n labeledHoleOptions,\n traceGraphs,\n handleConfigChange\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n * @param {*} rows - Value to convert to array\n * @returns {Array} Array\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n * @param {*} value - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Normalize a point to ensure x, y, z are finite numbers\n * @private\n * @param {Object} point - Point object\n * @returns {Object} Normalized point\n */\nfunction normalizePoint(point = {}) {\n return {\n ...point,\n x: toNumber(point.x),\n y: toNumber(point.y),\n z: toNumber(point.z)\n };\n}\n\n/**\n * Project 3D trace points onto a 2D cross-section plane\n * @param {Array<Object>} traces - Array of trace points with x, y, z coordinates\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @returns {Array<Object>} Trace points with added 'along' and 'across' section coordinates\n */\nexport function projectTraceToSection(traces = [], origin = [0, 0], azimuth = 0) {\n const [ox, oy] = origin;\n const azRad = (Number(azimuth) * Math.PI) / 180;\n const cosA = Math.cos(azRad);\n const sinA = Math.sin(azRad);\n\n return toArray(traces)\n .map(normalizePoint)\n .map((row) => {\n if (!Number.isFinite(row.x) || !Number.isFinite(row.y)) return { ...row };\n const dx = row.x - ox;\n const dy = row.y - oy;\n return {\n ...row,\n along: (dx * sinA) + (dy * cosA),\n across: (dx * cosA) - (dy * sinA)\n };\n });\n}\n\n/**\n * Filter trace points to those within a width window around a section line\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width (points within ±width/2 are included)\n * @returns {Array<Object>} Filtered trace points within section window\n */\nexport function sectionWindow(traces = [], origin = [0, 0], azimuth = 0, width = 50) {\n const projected = projectTraceToSection(traces, origin, azimuth);\n const half = 0.5 * Number(width || 0);\n if (!Number.isFinite(half) || half <= 0) return projected;\n return projected.filter((row) => Number.isFinite(row.across) && Math.abs(row.across) <= half);\n}\n\n/**\n * Generate plan view of traces, optionally filtered by depth slice\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>|null} depthSlice - Optional [top, bottom] depth range to filter\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Trace points for plan view\n */\nexport function planView(traces = [], depthSlice = null, colorBy = null) {\n let rows = toArray(traces).map(normalizePoint);\n if (Array.isArray(depthSlice) && depthSlice.length === 2) {\n const [top, bottom] = depthSlice;\n rows = rows.filter((row) => Number.isFinite(row.z) && row.z <= Number(top) && row.z >= Number(bottom));\n }\n if (colorBy) {\n rows = rows.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return rows;\n}\n\n/**\n * Generate cross-section view of traces within a window\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Section trace points with along/across coordinates\n */\nexport function sectionView(traces = [], origin = [0, 0], azimuth = 0, width = 50, colorBy = null) {\n let section = sectionWindow(traces, origin, azimuth, width);\n if (colorBy) {\n section = section.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return section;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Extract hole ID from a row using common field name variations\n * @private\n */\nfunction getHoleId(row = {}) {\n return row.hole_id ?? row.holeId ?? row.id;\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Convert trace points to line segments grouped by hole ID\n * @param {Array<Object>} traces - Array of trace points with x, y, z, md coordinates\n * @param {string|null} colorBy - Optional property name for per-point color values\n * @returns {Array<{hole_id: string, x: Array<number>, y: Array<number>, z: Array<number>, color: Array|null}>} Array of line segments\n */\nexport function tracesAsSegments(traces = [], colorBy = null) {\n const grouped = new Map();\n\n toArray(traces).forEach((row) => {\n const holeId = getHoleId(row);\n if (holeId === undefined || holeId === null || `${holeId}`.trim() === '') return;\n const key = `${holeId}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(row);\n });\n\n const segments = [];\n grouped.forEach((rows, holeId) => {\n const sorted = [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0));\n const payload = {\n hole_id: holeId,\n x: sorted.map((row) => toNumber(row.x, 0)),\n y: sorted.map((row) => toNumber(row.y, 0)),\n z: sorted.map((row) => toNumber(row.z, 0)),\n color: null\n };\n if (colorBy) {\n payload.color = sorted.map((row) => row?.[colorBy]);\n }\n segments.push(payload);\n });\n\n return segments;\n}\n\n/**\n * Convert interval data to tube/cylinder representations for 3D rendering\n * @param {Array<Object>} intervals - Array of interval objects with from/to depths and x, y, z coordinates\n * @param {number} radius - Tube radius\n * @param {string|null} colorBy - Optional property name for color value\n * @returns {Array<{hole_id: string, x: number, y: number, z: number, from: number, to: number, radius: number, color_value: *}>} Array of tube specifications\n */\nexport function intervalsAsTubes(intervals = [], radius = 1, colorBy = null) {\n return toArray(intervals).map((row) => ({\n hole_id: getHoleId(row),\n from: row?.from,\n to: row?.to,\n radius,\n color: colorBy ? row?.[colorBy] : null,\n value: colorBy ? row?.[colorBy] : null\n }));\n}\n\n/**\n * Extract annotation labels from interval data for 3D text display\n * @param {Array<Object>} intervals - Array of interval objects\n * @param {string|null} labelCol - Property name to use for label text\n * @returns {Array<{hole_id: string, label: string, depth: number}>} Array of annotations with computed mid-depth\n */\nexport function annotationsFromIntervals(intervals = [], labelCol = null) {\n if (!labelCol) return [];\n return toArray(intervals)\n .filter((row) => Object.prototype.hasOwnProperty.call(row || {}, labelCol))\n .map((row) => ({\n hole_id: getHoleId(row),\n label: row?.[labelCol],\n depth: 0.5 * ((toNumber(row?.from, 0)) + (toNumber(row?.to, 0)))\n }));\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './Baselode3DControls.css';\n\n/**\n * 3D scene control buttons component\n * Provides UI controls for camera manipulation in the 3D drillhole viewer\n * @param {Object} props - Component props\n * @param {string} props.controlMode - Current control mode ('orbit' or 'fly')\n * @param {Function} props.onToggleFly - Handler for toggling fly mode\n * @param {Function} props.onRecenter - Handler for recentering camera\n * @param {Function} props.onLookDown - Handler for top-down view\n * @param {Function} props.onFit - Handler for fitting camera to scene\n * @returns {JSX.Element} Control buttons component\n */\nfunction Baselode3DControls({\n controlMode = 'orbit',\n onToggleFly = () => {},\n onRecenter = () => {},\n onLookDown = () => {},\n onFit = () => {},\n darkBackground = false,\n onToggleDarkBackground = () => {},\n}) {\n return (\n <div className=\"baselode-3d-controls\">\n <button type=\"button\" className=\"ghost-button\" onClick={onRecenter}>\n Recenter to (0,0,0)\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onLookDown}>\n Look down\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onFit}>\n Fit to scene\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onToggleFly}>\n {controlMode === 'orbit' ? 'Enable fly controls' : 'Disable fly controls'}\n </button>\n <label className=\"baselode-3d-controls-checkbox\">\n <input\n type=\"checkbox\"\n checked={darkBackground}\n onChange={onToggleDarkBackground}\n />\n Dark background\n </label>\n </div>\n );\n}\n\nexport default Baselode3DControls;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './BlockModelWidget.css';\n\n/**\n * A panel of controls for exploring a block model in the 3-D scene.\n *\n * Exports widget state (attribute selection, opacity) so that a wrapping\n * application can pass the values on to the 3-D scene. Renders:\n * - An attribute (color-by) drop-down selector\n * - A translucency / opacity slider\n * - A simple color-scale legend (numeric attributes)\n * - A popup card showing all attributes of the last clicked block\n *\n * @param {Object} props\n * @param {Array<string>} props.properties - List of attribute column names\n * @param {string} props.selectedProperty - Currently selected attribute\n * @param {Function} props.onPropertyChange - Called with new property name string\n * @param {number} props.opacity - Current opacity value (0–1, default 0.85)\n * @param {Function} props.onOpacityChange - Called with new opacity number\n * @param {Object|null} props.propertyStats - Stats object for the selected\n * property (``{type, min?, max?, categories?}`` from calculatePropertyStats)\n * @param {Object|null} props.clickedBlock - Block row data to display in the\n * popup, or null when no block is selected\n * @param {Function} props.onPopupClose - Called when the user dismisses the popup\n */\nfunction BlockModelWidget({\n properties = [],\n selectedProperty = '',\n onPropertyChange = () => {},\n opacity = 0.85,\n onOpacityChange = () => {},\n propertyStats = null,\n clickedBlock = null,\n onPopupClose = () => {},\n}) {\n return (\n <div className=\"bm-widget\">\n {/* Attribute selector */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-property-select\">\n Color by\n </label>\n <select\n id=\"bm-property-select\"\n className=\"bm-widget__select\"\n value={selectedProperty}\n onChange={(e) => onPropertyChange(e.target.value)}\n >\n {properties.length === 0 && (\n <option value=\"\">— no attributes —</option>\n )}\n {properties.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n\n {/* Color scale legend */}\n {propertyStats && propertyStats.type === 'numeric' && (\n <div className=\"bm-widget__scale\">\n <span className=\"bm-widget__scale-label bm-widget__scale-label--min\">\n {propertyStats.min?.toFixed(2) ?? '—'}\n </span>\n <div className=\"bm-widget__scale-bar\" />\n <span className=\"bm-widget__scale-label bm-widget__scale-label--max\">\n {propertyStats.max?.toFixed(2) ?? '—'}\n </span>\n </div>\n )}\n {propertyStats && propertyStats.type === 'categorical' && (\n <div className=\"bm-widget__categories\">\n {(propertyStats.categories || []).map((cat, i) => {\n const hue = Math.round((i / Math.max(propertyStats.categories.length, 1)) * 360);\n return (\n <span key={cat} className=\"bm-widget__category-chip\"\n style={{ background: `hsl(${hue},70%,50%)` }}>\n {cat}\n </span>\n );\n })}\n </div>\n )}\n\n {/* Opacity slider */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-opacity-slider\">\n Opacity ({Math.round(opacity * 100)}%)\n </label>\n <input\n id=\"bm-opacity-slider\"\n type=\"range\"\n className=\"bm-widget__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={opacity}\n onChange={(e) => onOpacityChange(parseFloat(e.target.value))}\n />\n\n {/* Block attribute popup */}\n {clickedBlock && (\n <div className=\"bm-widget__popup\">\n <div className=\"bm-widget__popup-header\">\n <span>Block attributes</span>\n <button\n type=\"button\"\n className=\"bm-widget__popup-close\"\n onClick={onPopupClose}\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n <table className=\"bm-widget__popup-table\">\n <tbody>\n {Object.entries(clickedBlock).map(([key, value]) => (\n <tr key={key}>\n <th>{key}</th>\n <td>{value === null || value === undefined ? '—' : String(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nexport default BlockModelWidget;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Core photo table visualization helpers.\n *\n * Provides depth-ordered layout utilities, LOD image selection, and depth\n * marker generation for the CorePhotoTable component.\n */\n\n/**\n * Default LOD (Level of Detail) breakpoints.\n *\n * At low zoom values small/thumbnail images are loaded; at high zoom values\n * full-resolution images are used. Each entry specifies the minimum zoom level\n * that activates the corresponding LOD key.\n *\n * Photo objects may supply a ``lod_urls`` map keyed by these values. If a\n * photo has no ``lod_urls``, ``image_url`` is used regardless of zoom.\n */\nexport const DEFAULT_LOD_BREAKPOINTS = [\n { minZoom: 0, lodKey: 'thumb' },\n { minZoom: 4, lodKey: 'medium' },\n { minZoom: 7, lodKey: 'full' },\n];\n\n/**\n * Base pixels-per-metre at zoom level 5. At other zoom levels this value is\n * scaled linearly so that ``pixelsPerMetre = BASE_PIXELS_PER_METRE * zoom / 5``.\n */\nexport const BASE_PIXELS_PER_METRE = 50;\n\n/**\n * Select the image URL appropriate for the given zoom level.\n *\n * If the photo object contains a ``lod_urls`` map the function returns the\n * URL for the highest LOD whose ``minZoom`` is still ≤ the current zoom\n * level. If no ``lod_urls`` are present (or the resolved key is missing)\n * the function falls back to ``photo.image_url``.\n *\n * @param {Object} photo - Photo entry. Expected fields: ``image_url`` (string),\n * ``lod_urls`` (optional object mapping lodKey → URL string).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {Array<{minZoom: number, lodKey: string}>} [lodBreakpoints] - LOD\n * breakpoints to use. Defaults to {@link DEFAULT_LOD_BREAKPOINTS}.\n * @returns {string} The selected image URL, or an empty string if none found.\n */\nexport function selectPhotoLodUrl(photo, zoom, lodBreakpoints = DEFAULT_LOD_BREAKPOINTS) {\n const lodUrls = photo.lod_urls;\n if (!lodUrls || typeof lodUrls !== 'object' || Array.isArray(lodUrls)) {\n return photo.image_url || '';\n }\n\n // Walk breakpoints and keep the last one whose minZoom ≤ zoom.\n let selectedKey = lodBreakpoints[0]?.lodKey ?? 'thumb';\n for (const bp of lodBreakpoints) {\n if (zoom >= bp.minZoom) {\n selectedKey = bp.lodKey;\n }\n }\n\n return lodUrls[selectedKey] || photo.image_url || '';\n}\n\n/**\n * Return a copy of ``photos`` sorted ascending by ``from_depth``.\n *\n * @param {Array<Object>} photos - Array of photo objects, each with a\n * numeric ``from_depth`` field.\n * @returns {Array<Object>} New sorted array.\n */\nexport function sortPhotosByDepth(photos) {\n return [...photos].sort((a, b) => (a.from_depth ?? 0) - (b.from_depth ?? 0));\n}\n\n/**\n * Group photos by their ``photo_set`` value.\n *\n * Photos without a ``photo_set`` (null, undefined, or empty string) are\n * placed in the ``'default'`` group. The returned object's keys preserve\n * insertion order (first-seen order within ``photos``).\n *\n * @param {Array<Object>} photos - Array of photo objects.\n * @returns {Object.<string, Array<Object>>} Map of set name → photo array.\n */\nexport function groupPhotosBySet(photos) {\n /** @type {Object.<string, Array<Object>>} */\n const sets = {};\n for (const photo of photos) {\n const key =\n photo.photo_set != null && photo.photo_set !== ''\n ? String(photo.photo_set)\n : 'default';\n if (!sets[key]) sets[key] = [];\n sets[key].push(photo);\n }\n return sets;\n}\n\n/**\n * Build an array of depth marker descriptors at regular intervals.\n *\n * Markers start at the first multiple of ``intervalMetres`` that is ≥\n * ``minDepth`` and continue up to and including ``maxDepth``.\n *\n * @param {number} minDepth - Shallowest depth of the view (m).\n * @param {number} maxDepth - Deepest depth of the view (m).\n * @param {number} [intervalMetres=10] - Spacing between markers in metres.\n * @returns {Array<{depth: number, label: string}>} Ordered marker descriptors.\n */\nexport function buildDepthMarkers(minDepth, maxDepth, intervalMetres = 10) {\n const markers = [];\n if (minDepth >= maxDepth || intervalMetres <= 0) return markers;\n\n const first = Math.ceil(minDepth / intervalMetres) * intervalMetres;\n // Guard against floating-point drift with a small epsilon.\n const epsilon = intervalMetres * 1e-9;\n for (let d = first; d <= maxDepth + epsilon; d += intervalMetres) {\n const rounded = Math.round(d * 1e6) / 1e6;\n markers.push({ depth: rounded, label: `${rounded} m` });\n }\n return markers;\n}\n\n/**\n * Choose an appropriate depth-marker interval (in metres) for the given zoom\n * level so that labels are legible without overcrowding.\n *\n * @param {number} zoom - Current zoom level (1–10).\n * @returns {number} Interval in metres.\n */\nexport function depthMarkerInterval(zoom) {\n if (zoom >= 9) return 1;\n if (zoom >= 7) return 2;\n if (zoom >= 5) return 5;\n if (zoom >= 3) return 10;\n return 20;\n}\n\n/**\n * Default filename generator used by {@link buildTrayPhotos}.\n *\n * @param {number} index - Zero-based tray index.\n * @returns {string}\n */\nexport function defaultTrayFilename(index) {\n return `tray_${String(index).padStart(3, '0')}.jpg`;\n}\n\n/**\n * Build a CorePhotoTable-compatible photos array from tray depth intervals\n * and a pair of image base URLs.\n *\n * This is the same transform performed internally by CorePhotoViewer; it is\n * exported so callers that need to combine multiple datasets (e.g. two\n * concurrent scan runs for the same hole) can build the array themselves and\n * pass it directly to CorePhotoTable.\n *\n * @param {string} holeId - Drillhole identifier stored on every photo entry.\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} trays - One entry per image, in any depth order.\n * @param {string} thumbBaseUrl - URL prefix for thumbnail images.\n * @param {string} fullBaseUrl - URL prefix for full-resolution images.\n * @param {string} [photoSet='Tray Images'] - Default column label; overridden\n * per-tray via ``tray.photoSet``.\n * @param {function} [getFilename] - ``(index: number) => string`` filename\n * generator. ``index`` is the zero-based position in ``trays``. Defaults to\n * {@link defaultTrayFilename} (``tray_000.jpg``, ``tray_001.jpg``, …).\n * @returns {Array<Object>} Photos array suitable for ``CorePhotoTable.photos``.\n */\nexport function buildTrayPhotos(\n holeId,\n trays,\n thumbBaseUrl,\n fullBaseUrl,\n photoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n) {\n const thumb = (thumbBaseUrl ?? '').replace(/\\/$/, '');\n const full = (fullBaseUrl ?? '').replace(/\\/$/, '');\n return trays.map((tray, index) => {\n const filename = tray.filename ?? getFilename(index);\n const set = tray.photoSet ?? photoSet;\n return {\n hole_id: holeId,\n from_depth: tray.fromDepth,\n to_depth: tray.toDepth,\n photo_set: set,\n image_url: `${thumb}/${filename}`,\n lod_urls: {\n thumb: `${thumb}/${filename}`,\n full: `${full}/${filename}`,\n },\n };\n });\n}\n\n/**\n * Convert a depth interval in metres to a pixel height for the given zoom.\n *\n * Uses a linear scale so that the displayed height is always proportional to\n * the physical depth span, enabling correct alignment across photo columns.\n *\n * @param {number} depthInterval - Depth span in metres (``to_depth - from_depth``).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {number} [basePixelsPerMetre] - Pixels per metre at zoom 5.\n * Defaults to {@link BASE_PIXELS_PER_METRE}.\n * @returns {number} Pixel height (always ≥ 1).\n */\nexport function depthIntervalToPixels(depthInterval, zoom, basePixelsPerMetre = BASE_PIXELS_PER_METRE) {\n const scale = zoom / 5;\n return Math.max(1, Math.round(depthInterval * basePixelsPerMetre * scale));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n BASE_PIXELS_PER_METRE,\n buildDepthMarkers,\n depthMarkerInterval,\n groupPhotosBySet,\n selectPhotoLodUrl,\n sortPhotosByDepth,\n} from './corePhotoViz.js';\nimport './CorePhotoTable.css';\n\nconst BASE_ZOOM = 5; // fixed layout zoom (no reflow on pan/zoom)\nconst ZOOM_FACTOR = 1.12; // scale multiplier per wheel tick\nconst SCALE_MIN = 0.05;\nconst SCALE_MAX = 40;\n\n/**\n * CorePhotoTable\n *\n * Renders a pannable/zoomable core photograph table with:\n *\n * - Depth-ordered images stacked from shallow → deep\n * - Multiple photo sets displayed side-by-side (one column per set)\n * - A depth ruler for spatial orientation\n * - Mouse-wheel zoom centred on cursor (Leaflet-style)\n * - Left-click drag to pan\n *\n * @param {Object} props\n * @param {Array<CorePhoto>} [props.photos=[]] - Photo entries to display.\n * Each entry should have: ``hole_id`` (string), ``from_depth`` (number),\n * ``to_depth`` (number), ``image_url`` (string), ``photo_set`` (string,\n * optional), ``lod_urls`` (object, optional).\n * @param {string} [props.holeId=''] - Hole identifier shown in the control bar.\n * @param {number} [props.initialZoom=5] - Starting zoom level (1–10) for LOD.\n */\nexport function CorePhotoTable({\n photos = [],\n holeId = '',\n initialZoom = 5,\n transform: controlledTransform,\n onTransformChange,\n}) {\n // ── Pan / zoom transform ────────────────────────────────────────────────\n // Supports both uncontrolled (internal state) and controlled modes.\n // Pass transform + onTransformChange to share state across multiple tables.\n const [internalTransform, setInternalTransform] = useState({ scale: 1, tx: 0, ty: 0 });\n const transform = controlledTransform ?? internalTransform;\n const transformRef = useRef(transform);\n transformRef.current = transform;\n\n // Always resolves functional updaters to a plain object before dispatching,\n // so onTransformChange is always called with a transform object, never a function.\n const setTransform = useCallback(\n (updater) => {\n const next = typeof updater === 'function' ? updater(transformRef.current) : updater;\n if (onTransformChange) {\n onTransformChange(next);\n } else {\n setInternalTransform(next);\n }\n },\n [onTransformChange],\n );\n\n const [dragging, setDragging] = useState(false);\n const dragOrigin = useRef(null);\n\n const viewportRef = useRef(null);\n\n // ── Derived layout values (fixed at BASE_ZOOM, no reflow on pan/zoom) ──\n\n const sorted = useMemo(() => sortPhotosByDepth(photos), [photos]);\n const grouped = useMemo(() => groupPhotosBySet(sorted), [sorted]);\n\n // Derive column order from the original (unsorted) photos so that callers\n // control left-to-right ordering by the sequence they pass photos in.\n const setNames = useMemo(() => {\n const seen = new Set();\n for (const p of photos) {\n const key = p.photo_set != null && p.photo_set !== '' ? String(p.photo_set) : 'default';\n seen.add(key);\n }\n return [...seen];\n }, [photos]);\n\n const { minDepth, maxDepth } = useMemo(() => {\n if (!sorted.length) return { minDepth: 0, maxDepth: 0 };\n const allTo = sorted.map((p) => p.to_depth ?? p.from_depth ?? 0);\n return {\n minDepth: sorted[0].from_depth ?? 0,\n maxDepth: Math.max(...allTo),\n };\n }, [sorted]);\n\n const pixelsPerMetre = (BASE_PIXELS_PER_METRE * BASE_ZOOM) / 5;\n\n const totalHeight = useMemo(\n () => Math.max(1, Math.round((maxDepth - minDepth) * pixelsPerMetre)),\n [minDepth, maxDepth, pixelsPerMetre],\n );\n\n const markers = useMemo(() => {\n const interval = depthMarkerInterval(BASE_ZOOM);\n return buildDepthMarkers(minDepth, maxDepth, interval);\n }, [minDepth, maxDepth]);\n\n const imageWidth = (540 * BASE_ZOOM) / 5; // fixed 540 px at base zoom\n\n // Effective LOD zoom derived from visual scale so thumbnails swap to full\n // resolution when the user zooms in close enough.\n const lodZoom = useMemo(\n () => Math.max(1, Math.min(10, Math.round(initialZoom * transform.scale))),\n [initialZoom, transform.scale],\n );\n\n // ── Wheel zoom (non-passive, centred on cursor) ─────────────────────────\n\n const handleWheel = useCallback((e) => {\n e.preventDefault();\n const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;\n const rect = viewportRef.current.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n\n setTransform((prev) => {\n const newScale = Math.max(SCALE_MIN, Math.min(SCALE_MAX, prev.scale * factor));\n const ratio = newScale / prev.scale;\n return {\n scale: newScale,\n tx: cx - (cx - prev.tx) * ratio,\n ty: cy - (cy - prev.ty) * ratio,\n };\n });\n }, [setTransform]);\n\n useEffect(() => {\n const el = viewportRef.current;\n if (!el) return;\n el.addEventListener('wheel', handleWheel, { passive: false });\n return () => el.removeEventListener('wheel', handleWheel);\n }, [handleWheel]);\n\n // ── Left-drag pan ───────────────────────────────────────────────────────\n\n const handleMouseDown = useCallback((e) => {\n if (e.button !== 0) return;\n e.preventDefault();\n dragOrigin.current = {\n x: e.clientX,\n y: e.clientY,\n tx: transformRef.current.tx,\n ty: transformRef.current.ty,\n };\n setDragging(true);\n }, []);\n\n useEffect(() => {\n const handleMouseMove = (e) => {\n if (!dragOrigin.current) return;\n // Snapshot ref values before entering the async updater — the ref may\n // be nulled by handleMouseUp before React executes the updater.\n const { tx, ty, x, y } = dragOrigin.current;\n setTransform((prev) => ({\n ...prev,\n tx: tx + (e.clientX - x),\n ty: ty + (e.clientY - y),\n }));\n };\n const handleMouseUp = () => {\n dragOrigin.current = null;\n setDragging(false);\n };\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n };\n }, [setTransform]);\n\n // ── Render ──────────────────────────────────────────────────────────────\n\n return (\n <div className=\"core-photo-table\">\n {/* Controls */}\n <div className=\"core-photo-controls\">\n {holeId && <span className=\"core-photo-hole-id\">{holeId}</span>}\n <span className=\"core-photo-zoom-label\">\n {Math.round(transform.scale * 100)}%\n </span>\n <button\n className=\"core-photo-zoom-btn\"\n onClick={() => setTransform({ scale: 1, tx: 0, ty: 0 })}\n aria-label=\"Reset view\"\n title=\"Reset view\"\n >\n ⌂\n </button>\n </div>\n\n {/* Column label strip */}\n {photos.length > 0 && (\n <div className=\"core-photo-col-headers\">\n <div className=\"core-photo-ruler-spacer\" />\n {setNames.map((name) => (\n <div\n key={name}\n className=\"core-photo-set-header\"\n style={{ width: imageWidth }}\n >\n {name}\n </div>\n ))}\n </div>\n )}\n\n {/* Body */}\n {photos.length === 0 ? (\n <div className=\"core-photo-empty\">No photos to display.</div>\n ) : (\n <div\n className={`core-photo-scroll${dragging ? ' is-dragging' : ''}`}\n ref={viewportRef}\n onMouseDown={handleMouseDown}\n >\n <div\n className=\"core-photo-inner\"\n style={{\n height: totalHeight,\n transform: `translate(${transform.tx}px, ${transform.ty}px) scale(${transform.scale})`,\n transformOrigin: '0 0',\n }}\n >\n {/* Depth ruler */}\n <div\n className=\"core-photo-depth-ruler\"\n style={{ height: totalHeight }}\n >\n {markers.map(({ depth, label }) => (\n <div\n key={depth}\n className=\"core-photo-depth-marker\"\n style={{\n top: Math.round((depth - minDepth) * pixelsPerMetre),\n }}\n >\n {label}\n </div>\n ))}\n </div>\n\n {/* Photo set columns */}\n {setNames.map((setName) => (\n <div\n key={setName}\n className=\"core-photo-col-body\"\n style={{ height: totalHeight, width: imageWidth }}\n >\n {grouped[setName].map((photo) => {\n const fromDepth = photo.from_depth ?? 0;\n const toDepth = photo.to_depth ?? fromDepth;\n const top = Math.round(\n (fromDepth - minDepth) * pixelsPerMetre,\n );\n const height = Math.max(\n 2,\n Math.round((toDepth - fromDepth) * pixelsPerMetre),\n );\n const src = selectPhotoLodUrl(photo, lodZoom);\n\n return (\n <div\n key={`${photo.hole_id ?? ''}-${fromDepth}-${toDepth}-${setName}`}\n className=\"core-photo-item\"\n style={{ top, height, width: imageWidth }}\n title={`${fromDepth}–${toDepth} m`}\n >\n {src ? (\n <img\n src={src}\n alt={`Core ${fromDepth}–${toDepth} m`}\n loading=\"lazy\"\n />\n ) : (\n <div className=\"core-photo-no-image\" />\n )}\n {height >= 18 && (\n <span className=\"core-photo-item-label\">\n {fromDepth}–{toDepth} m\n </span>\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default CorePhotoTable;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useMemo } from 'react';\nimport CorePhotoTable from './CorePhotoTable.jsx';\nimport { buildTrayPhotos, defaultTrayFilename } from './corePhotoViz.js';\n\n/**\n * CorePhotoViewer\n * ===============\n *\n * A self-contained, pannable/zoomable core box photograph viewer for use in\n * any React application. Point it at a sequence of tray images (e.g. from an\n * NVCL HyLogger dataset) and it renders them depth-aligned with a ruler,\n * mouse-wheel zoom, and left-drag panning.\n *\n *\n * ## Minimal usage\n *\n * ```jsx\n * import { CorePhotoViewer } from 'baselode';\n * import 'baselode/style.css'; // required\n *\n * <div style={{ width: '100%', height: '80vh' }}>\n * <CorePhotoViewer\n * holeId=\"09RTD001\"\n * trays={[\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * // … one entry per image, in ascending depth order\n * ]}\n * thumbBaseUrl=\"/data/09RTD001/thumb\"\n * fullBaseUrl=\"/data/09RTD001/full\"\n * />\n * </div>\n * ```\n *\n *\n * ## What you must supply\n *\n * ### 1 · Tray depth intervals (`trays`)\n *\n * An array of objects, one per image file, each containing the downhole depth\n * range (metres) that the tray covers:\n *\n * ```js\n * [\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * ]\n * ```\n *\n * **Where to get these values:**\n * - Core scanning system export (HyLogger, Corescan, etc.) — usually a CSV\n * with from/to depths per tray.\n * - LIMS or core database — query from/to depths for the hole ordered by depth.\n * - Manual entry or calculation from known tray length and start depth.\n *\n *\n * ### 2 · Image directories (`thumbBaseUrl` / `fullBaseUrl`)\n *\n * Two URL prefixes pointing to directories that contain the image files:\n *\n * | Prop | Purpose | Typical size |\n * |----------------|-------------------------------------------|----------------|\n * | `thumbBaseUrl` | Low-resolution thumbnails — always loaded | ~10–50 KB each |\n * | `fullBaseUrl` | High-resolution originals — loaded on zoom| 1–10 MB each |\n *\n * If you only have one resolution pass the same URL for both props.\n *\n * The directories must be **browser-accessible** — e.g.:\n * - Served from your web server's `public/` folder\n * - An S3 bucket / CDN with CORS enabled (`Access-Control-Allow-Origin: *`)\n * - A Vercel / Netlify static asset path\n *\n * The viewer never loads all full-res images at once; it swaps to full\n * resolution only when the user zooms in past a threshold.\n *\n *\n * ### 3 · Container size\n *\n * The component fills 100 % of its parent's width and height. Wrap it in a\n * `<div>` with an explicit height (e.g. `height: '80vh'`).\n *\n *\n * ## Filename conventions\n *\n * By default filenames are generated as `tray_000.jpg`, `tray_001.jpg`, …\n * (zero-padded three-digit index, 0-based).\n *\n * Override per-tray via `tray.filename`:\n * ```js\n * { fromDepth: 3.4, toDepth: 6.8, filename: 'DDH001_box002.jpg' }\n * ```\n *\n * Or supply a custom generator for the whole dataset:\n * ```jsx\n * <CorePhotoViewer\n * getFilename={(index) => `box_${String(index + 1).padStart(4, '0')}.jpg`}\n * // …\n * />\n * ```\n *\n *\n * ## Multiple photo sets (columns)\n *\n * If you have parallel image sets (e.g. \"Wet\" and \"Dry\") for the same hole,\n * interleave the trays and tag each with `photoSet`:\n *\n * ```js\n * trays={[\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Wet', filename: 'wet_000.jpg' },\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Dry', filename: 'dry_000.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Wet', filename: 'wet_001.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Dry', filename: 'dry_001.jpg' },\n * ]}\n * ```\n *\n *\n * ## Interaction\n *\n * | Input | Action |\n * |------------------------|-------------------------------------|\n * | Mouse wheel | Zoom in / out (centred on cursor) |\n * | Left-click drag | Pan |\n * | ⌂ button in header | Reset to default view |\n *\n *\n * @param {Object} props\n * @param {string} [props.holeId='']\n * Drillhole identifier — shown in the header bar.\n *\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} [props.trays=[]]\n * Tray depth intervals. `fromDepth` / `toDepth` in metres downhole.\n * Optional `filename` overrides the auto-generated name for that tray.\n * Optional `photoSet` overrides the column label for that tray.\n *\n * @param {string} [props.thumbBaseUrl='']\n * URL prefix for thumbnail images (no trailing slash needed).\n * Example: `'https://cdn.example.com/holes/09RTD001/thumb'`\n *\n * @param {string} [props.fullBaseUrl='']\n * URL prefix for full-resolution images.\n * Example: `'https://cdn.example.com/holes/09RTD001/full'`\n *\n * @param {string} [props.photoSet='Tray Images']\n * Default column label used when `tray.photoSet` is absent.\n *\n * @param {function} [props.getFilename]\n * `(index: number) => string` — custom filename generator.\n * `index` is 0-based. Defaults to `tray_NNN.jpg`.\n *\n * @param {number} [props.initialZoom=5]\n * Starting LOD level (1–10). Controls when the viewer switches from\n * thumbnails to full-res as the user zooms in. 5 is a sensible default.\n */\nexport function CorePhotoViewer({\n holeId = '',\n trays = [],\n thumbBaseUrl = '',\n fullBaseUrl = '',\n photoSet: defaultPhotoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n initialZoom = 5,\n transform,\n onTransformChange,\n}) {\n const photos = useMemo(\n () => buildTrayPhotos(holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename),\n [holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename],\n );\n\n return (\n <CorePhotoTable\n photos={photos}\n holeId={holeId}\n initialZoom={initialZoom}\n transform={transform}\n onTransformChange={onTransformChange}\n />\n );\n}\n\nexport default CorePhotoViewer;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * @module grade_blocks\n *\n * Loading and Three.js rendering of 3D polygonal grade block meshes.\n *\n * A grade block is a closed polyhedral mesh defined by:\n * - An array of 3-D vertices [[x,y,z], ...]\n * - An array of triangle indices [[i,j,k], ...]\n * - Optional attributes and material hints\n *\n * Schema version \"1.0\" is the only version supported.\n */\n\nimport * as THREE from 'three';\n\n// ---------------------------------------------------------------------------\n// Data model helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and validate a raw JSON object (or JSON string) and return a\n * GradeBlockSet plain object.\n *\n * @param {object|string} input - Parsed JSON object or a JSON string.\n * @returns {{ schema_version: string, units: string, blocks: GradeBlock[] }}\n * @throws {Error} If schema_version is not \"1.0\" or required fields are missing.\n */\nexport function loadGradeBlocksFromJson(input) {\n const data = typeof input === 'string' ? JSON.parse(input) : input;\n\n if (data.schema_version !== '1.0') {\n throw new Error(\n `Unsupported schema_version: ${JSON.stringify(data.schema_version)}. Expected \"1.0\".`\n );\n }\n\n if (!Array.isArray(data.blocks)) {\n throw new Error('\"blocks\" must be a JSON array.');\n }\n\n const blocks = data.blocks.map((raw, i) => {\n if (raw.id == null) throw new Error(`Block at index ${i} is missing required field \"id\".`);\n if (raw.name == null) throw new Error(`Block \"${raw.id}\" is missing required field \"name\".`);\n if (!Array.isArray(raw.vertices)) throw new Error(`Block \"${raw.id}\" is missing required field \"vertices\".`);\n if (!Array.isArray(raw.triangles)) throw new Error(`Block \"${raw.id}\" is missing required field \"triangles\".`);\n\n return {\n id: raw.id,\n name: raw.name,\n vertices: raw.vertices,\n triangles: raw.triangles,\n attributes: raw.attributes ?? {},\n material: raw.material ?? {},\n };\n });\n\n return {\n schema_version: data.schema_version,\n units: data.units ?? '',\n blocks,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Three.js geometry\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a grade block to a Three.js BufferGeometry.\n *\n * @param {object} block - A grade block object (from loadGradeBlocksFromJson).\n * @returns {THREE.BufferGeometry}\n */\nexport function gradeBlockToThreeGeometry(block) {\n const geometry = new THREE.BufferGeometry();\n\n // Flatten vertices: [[x,y,z], ...] -> Float32Array\n const positionData = new Float32Array(block.vertices.length * 3);\n block.vertices.forEach(([x, y, z], i) => {\n positionData[i * 3] = x;\n positionData[i * 3 + 1] = y;\n positionData[i * 3 + 2] = z;\n });\n geometry.setAttribute('position', new THREE.BufferAttribute(positionData, 3));\n\n // Flatten triangles: [[i,j,k], ...] -> Uint32Array\n const indexData = new Uint32Array(block.triangles.length * 3);\n block.triangles.forEach(([a, b, c], i) => {\n indexData[i * 3] = a;\n indexData[i * 3 + 1] = b;\n indexData[i * 3 + 2] = c;\n });\n geometry.setIndex(new THREE.BufferAttribute(indexData, 1));\n\n return geometry;\n}\n\n// ---------------------------------------------------------------------------\n// Scene helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Create Three.js meshes for all blocks and add them to the scene.\n *\n * @param {THREE.Scene} scene\n * @param {{ blocks: object[] }} blockSet - Result of loadGradeBlocksFromJson.\n * @param {object} [options]\n * @param {number} [options.defaultOpacity=1.0] - Fallback opacity when not set per block.\n * @returns {THREE.Group}\n */\nexport function addGradeBlocksToScene(scene, blockSet, options = {}) {\n const { defaultOpacity = 1.0 } = options;\n const group = new THREE.Group();\n\n blockSet.blocks.forEach((block) => {\n const geometry = gradeBlockToThreeGeometry(block);\n\n const color = block.material?.color ?? '#888888';\n const opacity = block.material?.opacity ?? defaultOpacity;\n const transparent = opacity < 1.0;\n\n const material = new THREE.MeshStandardMaterial({\n color: new THREE.Color(color),\n opacity,\n transparent,\n side: THREE.DoubleSide,\n flatShading: true,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData = {\n id: block.id,\n attributes: block.attributes,\n };\n\n // Edge highlight — hidden by default, shown on selection\n const edgeGeo = new THREE.EdgesGeometry(geometry, 15);\n const edgeMat = new THREE.LineBasicMaterial({ color: '#ffffbb', linewidth: 1 });\n const edgeLines = new THREE.LineSegments(edgeGeo, edgeMat);\n edgeLines.visible = false;\n mesh.add(edgeLines);\n\n group.add(mesh);\n });\n\n scene.add(group);\n return group;\n}\n"],"names":["normalizeFieldName","name","standardizeColumns","row","columnMap","sourceColumnMap","lookup","_COLUMN_LOOKUP","rawName","expectedName","normalizedKey","normalizedValue","renamed","col","value","key","mapped","standardizeRowArray","rows","ASSAY_NON_VALUE_FIELDS","normalizeRow","rawRow","extractIdFields","HOLE_ID","extractInterval","holeIdRaw","holeId","project","PROJECT_ID","from","FROM","to","TO","intervalsToHole","intervals","sorted","a","b","points","iv","rest","pointData","_a","parseAssayHoleIds","file","resolve","reject","holeIds","Papa","results","hid","error","withDataErrorContext","hasAssayValue","k","v","parseAssayHoleIdsWithAssays","byHole","parseAssayHole","config","wanted","interval","hole","parseAssaysCSV","holes","normalizeCsvRow","normalized","pickFirstPresent","keys","fallback","DEFAULT_TRACE_PLOT_COUNT","reorderHoleIds","ids","focusId","matchIdx","id","selected","_","idx","coerceChartTypeForProperty","property","chartType","categoricalProps","commentProps","numericDefaultChartType","buildTraceConfigsForHoleIds","focusedHoleId","plotCount","defaultProp","ordered","deriveAssayProps","h","numericCols","categoricalCols","commentCols","byType","classifyColumns","loadAssayMetadata","loadAssayHole","buildAssayState","numericProps","columnMeta","traceConfigs","loadAssayFile","state","parseSurveyCSV","DEPTH","DIP","AZIMUTH","err","norm","lat","toNumber","LATITUDE","lng","LONGITUDE","surveyDepth","dip","azimuth","maxDepth","n","desurveyTraces","collars","surveys","collarByKey","c","refLat","_b","refLng","_c","_d","refMetersPerDegLat","refMetersPerDegLon","grouped","s","stations","collar","lat0","lng0","metersPerDegLat","metersPerDegLon","baseX","baseY","accX","accY","accZ","i","curr","prev","currDepth","currAzimuth","currDip","prevDepth","prevAzimuth","prevDip","deltaMD","inc1","toInclination","inc2","az1","degToRad","az2","beta","rf","dx","dy","dzDown","pointsWithGeo","p","d","dipDeg","val","incDeg","clamped","normalizeHoleIdValue","canonicalizeHoleIdRows","holeIdCol","preferred","resolved","angle","directionCosines","azRad","dipRad","ca","cb","cc","segmentDisplacement","deltaMd","az0","dip0","dip1","method","dc0","dc1","azAvg","dipAvg","dcAvg","dot","dogleg","desurvey","rowsCollars","rowsSurveys","options","step","safeStep","collarsCanonical","surveysCanonical","collarsByHole","surveysByHole","out","x","y","z","mdCursor","azPrev","dipPrev","firstRecord","s0","s1","md0","segmentSteps","mdIncrement","stepIdx","weight","azInterp","dipInterp","disp","record","minimumCurvatureDesurvey","tangentialDesurvey","balancedTangentialDesurvey","buildTraces","nearestByMeasuredDepth","traceRows","midMd","best","bestDist","md","dist","attachAssayPositions","assays","traces","assaysCanonical","tracesCanonical","tracesByHole","assay","nearest","merged","parseDrillholesCSV","EASTING","NORTHING","ELEVATION","order","pts","toArray","source","sortByColumns","columns","av","bv","validateNoOverlappingIntervals","label","prevToByHole","fromValue","toValue","prevTo","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","MID","loadGeology","hasCode","GEOLOGY_CODE","hasDescription","GEOLOGY_DESCRIPTION","code","description","keep","BASELODE_DATA_MODEL_DRILL_GEOLOGY","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","geology","structures","metadata","detectSchema","first","hasInterval","hasPoint","extractStructuralPoint","depth","extractStructuralInterval","mid","validateStructuralPoints","valid","errors","messages","az","parseStructuralPointsCSV","opts","point","parseStructuralIntervalsCSV","groupRowsByHole","byId","parseStructuralCSV","r","schema","parsed","parseAssayCsvTextToHoles","csvText","_dip","_az","rowWithoutStructural","parseGeologyCsvText","parseUnifiedDataset","assayCsv","structuralCsv","geologyCsv","assayHoles","structuralHoles","geologyHoles","sh","existing","significantIntercepts","assayField","minGrade","minLength","fromCol","toCol","holeCol","holeIntervals","qualifying","grade","runs","currentRun","f","t","run","totalFrom","totalTo","totalLength","weightedSum","totalWeight","len","avgGrade","nSamples","groupByHole","intervalLength","fromToMidpoints","detectGaps","minGap","group","left","right","fromDepth","toDepth","detectOverlaps","indexed","originalIndex","entry","firstIdx","firstFrom","firstTo","secondIdx","secondFrom","secondTo","overlapFrom","overlapTo","normalizeDepthsArg","depths","firstEntry","list","splitAt","all","candidate","inner","boundaries","boundaryIdx","clip","segFrom","segTo","rowContaining","mergeTables","tables","tableNames","leftName","leftByHole","otherByHole","otherName","leftRowsForHole","leftMin","leftMax","otherRowsForHole","otherFrom","otherTo","second","midpoint","leftRow","outRow","otherRow","sample","otherCols","SEVERITY_ERROR","SEVERITY_WARNING","SEVERITY_INFO","BDL_PATTERN","makeIssue","check","severity","message","table","rowIndex","fix","buildMaxDepthLookup","maxDepthCol","groupBy","checkDuplicateHoleIds","counts","issues","count","checkSingleStationSurveys","survey","checkAzimuthRange","azimuthCol","allowFullCircle","intervalText","numeric","checkDipRange","dipCol","checkOrphanIntervals","tableName","collarHoleIds","checkNegativeLengths","checkIntervalsBeyondMaxDepth","maxDepthLookup","checkIntervalGaps","gap","checkIntervalOverlaps","overlap","checkBelowDetectionLimit","reserved","validateDrillholeDb","intervalTables","depthCol","MAX_DEPTH","issue","fixSingleStationSurveys","additions","original","originalDepth","anchorDepth","firstHole","secondHole","dropOrphanIntervals","validHoleIds","swapInvertedIntervals","normalizeAzimuth","wrapped","replaceBelowDetectionLimit","sentinelFactor","targetColumns","match","DEPTH_ALIASES","resolveDepth","alias","detectValueColumns","skipSet","numericCount","parseGeophysicsCSV","rawRows","valueCols","samples","channels","values","GEOPHYSICS_NULL_SENTINEL","geophysicsToStripLogs","geophysicsHoles","channel","stripLogs","parseMnemonicLine","line","dotIdx","mnem","afterDot","unitMatch","unit","valueAndDesc","lastColon","splitSections","lasText","sections","current","raw","parseHeaderSection","lines","map","trimmed","parseLasFile","versionSection","wellSection","curveSection","dataSection","version","well","curveEntries","_f","_e","_h","_g","nullSentinel","_i","valueCurves","units","channelData","tokens","data","DEFAULT_PALETTE","STRIPLOG_COMPACT_MARGIN","STRIPLOG_AXIS_TICK_FONT_SIZE","STRIPLOG_AXIS_TITLE_FONT_SIZE","STRIPLOG_XAXIS_TITLE_STANDOFF","applyStriplogLayoutDefaults","layout","buildTadpoleConfig","tailScale","colorBy","palette","azCol","template","colorMap","cat","byCat","shapes","color","length","showLegend","BASELODE_TEMPLATE","buildStructuralStripConfig","labelCol","records","lv","textY","texts","rec","wrapComment","text","charsPerLine","words","word","buildCommentsConfig","commentCol","bgColor","borderColor","textColor","comment","textXs","textYs","hovers","hasComment","buildStrikeDipSymbol","symbolSize","xCol","yCol","strike","strikeRad","dxS","dyS","tickLen","dxD","dyD","resolveTracePlotBody","holeOptions","propertyOptions","displayType","renderError","props","DISPLAY_COMMENT","DISPLAY_TADPOLE","deriveGroupValue","holeOption","groupValuesFromHoles","seen","g","filterHolesByGroup","groupValue","resolveTracePlotSelectVisibility","chartOptions","showHoleSelect","showPropertySelect","showChartTypeSelect","DEFAULT_NUMERIC_CHART_TYPE","resolveChartType","requestedChartType","getChartOptions","opt","holeOptionAsTuple","genericOptionAsTuple","o","renderHoleSelector","selector","selectedHoleId","onConfigChange","jsxs","e","jsx","l","groupLabel","groupOptions","visibleHoles","Fragment","enabled","TracePlot","graph","propertyMeta","holeSelector","bodyRef","useRef","containerRef","meta","DISPLAY_CATEGORICAL","DISPLAY_NUMERIC","effectiveChartType","setRenderError","useState","plotSize","setPlotSize","bodyState","isPlaceholder","visibility","propertySelectEnabled","useEffect","body","frame","updatePlotSize","width","height","observer","target","isComment","isTadpole","plotData","buildPlotConfig","plotConfig","Plotly","resizeObserver","formatPropertyLabel","mergeHoleSets","primary","extra","eh","buildCommentPoints","useDrillholeTraceGrid","initialFocusedHoleId","sourceFile","extraHoles","setHoles","setHoleIds","setNumericProps","setCategoricalProps","setCommentProps","setColumnMeta","setDefaultProp","setTraceConfigs","setError","setFocusedHoleId","loadingHoles","setLoadingHoles","loadedSourceFileRef","uniqueIds","newIds","orderedHoleIds","cfg","already","loading","configs","useMemo","labeledHoleOptions","traceGraphs","allProps","holePropertyOptions","holeHasData","isCategorical","buildIntervalPoints","handleConfigChange","index","patch","normalizePoint","projectTraceToSection","origin","ox","oy","cosA","sinA","sectionWindow","projected","half","planView","depthSlice","top","bottom","sectionView","section","getHoleId","tracesAsSegments","segments","payload","intervalsAsTubes","radius","annotationsFromIntervals","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit","darkBackground","onToggleDarkBackground","BlockModelWidget","properties","selectedProperty","onPropertyChange","opacity","onOpacityChange","propertyStats","clickedBlock","onPopupClose","hue","DEFAULT_LOD_BREAKPOINTS","BASE_PIXELS_PER_METRE","selectPhotoLodUrl","photo","zoom","lodBreakpoints","lodUrls","selectedKey","bp","sortPhotosByDepth","photos","groupPhotosBySet","sets","buildDepthMarkers","minDepth","intervalMetres","markers","epsilon","rounded","depthMarkerInterval","defaultTrayFilename","buildTrayPhotos","trays","thumbBaseUrl","fullBaseUrl","photoSet","getFilename","thumb","full","tray","filename","set","depthIntervalToPixels","depthInterval","basePixelsPerMetre","scale","BASE_ZOOM","ZOOM_FACTOR","SCALE_MIN","SCALE_MAX","CorePhotoTable","initialZoom","controlledTransform","onTransformChange","internalTransform","setInternalTransform","transform","transformRef","setTransform","useCallback","updater","dragging","setDragging","dragOrigin","viewportRef","setNames","allTo","pixelsPerMetre","totalHeight","imageWidth","lodZoom","handleWheel","factor","rect","cx","cy","newScale","ratio","el","handleMouseDown","handleMouseMove","tx","ty","handleMouseUp","setName","src","CorePhotoViewer","defaultPhotoSet","loadGradeBlocksFromJson","input","blocks","gradeBlockToThreeGeometry","block","geometry","THREE","positionData","indexData","addGradeBlocksToScene","scene","blockSet","defaultOpacity","transparent","material","mesh","edgeGeo","edgeMat","edgeLines"],"mappings":"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"}