// Source-backed rules shared by the configuration loader, case matrix and UI adapters. export const LINUXCNC_SOURCE_SEMANTICS_SCHEMA = "linuxcnc-source-semantics.v1"; export const LINUXCNC_SOURCE_SEMANTICS = Object.freeze({ schema: LINUXCNC_SOURCE_SEMANTICS_SCHEMA, optional_display_keys: Object.freeze(["OPEN_FILE", "INTRO_GRAPHIC"]), hal_keys: Object.freeze(["HAL", "HALFILE"]), runtime_fixture_keys: Object.freeze(["PARAMETER_FILE", "VARFILE", "TOOL_TABLE", "TOOLTABLE"]), classifications: Object.freeze({ hal_missing: "BLOCKED_HAL", resource_missing: "BLOCKED_RESOURCE", }), source_refs: Object.freeze({ axis_open_file: "linuxcnc/src/emc/usr_intf/axis/scripts/axis.py", parameter_file: "linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc", program_prefix: "linuxcnc/src/emc/task/emctask.cc", }), control_panel_projection: Object.freeze([ "configuration.state", "configuration.diagnostics", "configuration.resources", "configuration.hal", "configuration.optional_missing", "configuration.runtime_fixtures", "configuration.state_hash", ]), }); export function isOptionalSourceKey(key, section) { const normalizedKey = String(key ?? "").toUpperCase(); const normalizedSection = String(section ?? "").toUpperCase(); return normalizedSection === "DISPLAY" && LINUXCNC_SOURCE_SEMANTICS.optional_display_keys.includes(normalizedKey); } export function isHalResourcePath(path) { const value = String(path ?? ""); return value.endsWith(".hal") || value.includes("/hallib/"); } export function classifyMissingResources(paths) { return (paths ?? []).some(isHalResourcePath) ? LINUXCNC_SOURCE_SEMANTICS.classifications.hal_missing : LINUXCNC_SOURCE_SEMANTICS.classifications.resource_missing; }