feat(map): refine sector inspector styling
This commit is contained in:
@@ -538,6 +538,8 @@ const GRID_LOD_PROFILE_KEYS = new Set([
|
||||
"crossesColor", "crossesOpacity", "graticuleStepDegrees", "graticuleLineWidthPx", "graticuleColor",
|
||||
"graticuleOpacity", "majorLinesEnabled", "majorLabelsEnabled", "majorLineWidthMultiplier",
|
||||
"volumeEnabled", "volumeMinimumHeightMeters", "volumeMaximumHeightMeters", "volumeBandHeightMeters",
|
||||
"selectionFillColor", "selectionFillOpacityPercent", "selectionOutlineColor", "selectionOutlineWidthPx",
|
||||
"selectionOutlineOpacityPercent",
|
||||
]);
|
||||
|
||||
const GRID_LOD_PROFILE_INPUT_KEYS = new Set([...GRID_LOD_PROFILE_KEYS, "lineWidthPx"]);
|
||||
@@ -564,6 +566,8 @@ const DEFAULT_GRID_LOD_PROFILES = Object.freeze([
|
||||
graticuleStepDegrees: 0.25, graticuleLineWidthPx: 1, graticuleColor: "#f5f5f5", graticuleOpacity: 12,
|
||||
majorLinesEnabled: true, majorLabelsEnabled: true, majorLineWidthMultiplier: 2.5,
|
||||
volumeEnabled: true, volumeMinimumHeightMeters: 0, volumeMaximumHeightMeters: 300, volumeBandHeightMeters: 300,
|
||||
selectionFillColor: "#35cfff", selectionFillOpacityPercent: 10,
|
||||
selectionOutlineColor: "#35cfff", selectionOutlineWidthPx: 6.25, selectionOutlineOpacityPercent: 95,
|
||||
}),
|
||||
Object.freeze({
|
||||
maxHeightKm: 50, stepKm: 5, mode: "3d", heightMeters: 500, max3dViewAngleDegrees: 30,
|
||||
@@ -573,6 +577,8 @@ const DEFAULT_GRID_LOD_PROFILES = Object.freeze([
|
||||
graticuleStepDegrees: 0.5, graticuleLineWidthPx: 1, graticuleColor: "#f8fbfc", graticuleOpacity: 12,
|
||||
majorLinesEnabled: true, majorLabelsEnabled: true, majorLineWidthMultiplier: 2.5,
|
||||
volumeEnabled: true, volumeMinimumHeightMeters: 0, volumeMaximumHeightMeters: 500, volumeBandHeightMeters: 500,
|
||||
selectionFillColor: "#35cfff", selectionFillOpacityPercent: 10,
|
||||
selectionOutlineColor: "#35cfff", selectionOutlineWidthPx: 6.25, selectionOutlineOpacityPercent: 95,
|
||||
}),
|
||||
Object.freeze({
|
||||
maxHeightKm: 200, stepKm: 25, mode: "3d", heightMeters: 500, max3dViewAngleDegrees: 30,
|
||||
@@ -582,6 +588,8 @@ const DEFAULT_GRID_LOD_PROFILES = Object.freeze([
|
||||
graticuleStepDegrees: 1, graticuleLineWidthPx: 1, graticuleColor: "#fafafa", graticuleOpacity: 12,
|
||||
majorLinesEnabled: true, majorLabelsEnabled: false, majorLineWidthMultiplier: 2,
|
||||
volumeEnabled: false, volumeMinimumHeightMeters: 0, volumeMaximumHeightMeters: 500, volumeBandHeightMeters: 500,
|
||||
selectionFillColor: "#35cfff", selectionFillOpacityPercent: 16,
|
||||
selectionOutlineColor: "#35cfff", selectionOutlineWidthPx: 5, selectionOutlineOpacityPercent: 95,
|
||||
}),
|
||||
Object.freeze({
|
||||
maxHeightKm: 800, stepKm: 50, mode: "graticule", heightMeters: 500, max3dViewAngleDegrees: 30,
|
||||
@@ -591,6 +599,8 @@ const DEFAULT_GRID_LOD_PROFILES = Object.freeze([
|
||||
graticuleStepDegrees: 2, graticuleLineWidthPx: 1, graticuleColor: "#fcfdfd", graticuleOpacity: 12,
|
||||
majorLinesEnabled: true, majorLabelsEnabled: false, majorLineWidthMultiplier: 2,
|
||||
volumeEnabled: false, volumeMinimumHeightMeters: 0, volumeMaximumHeightMeters: 500, volumeBandHeightMeters: 500,
|
||||
selectionFillColor: "#35cfff", selectionFillOpacityPercent: 12,
|
||||
selectionOutlineColor: "#35cfff", selectionOutlineWidthPx: 3, selectionOutlineOpacityPercent: 95,
|
||||
}),
|
||||
Object.freeze({
|
||||
maxHeightKm: 3_000, stepKm: 50, mode: "graticule", heightMeters: 500, max3dViewAngleDegrees: 30,
|
||||
@@ -600,6 +610,8 @@ const DEFAULT_GRID_LOD_PROFILES = Object.freeze([
|
||||
graticuleStepDegrees: 2, graticuleLineWidthPx: 1, graticuleColor: "#9c9c9c", graticuleOpacity: 8,
|
||||
majorLinesEnabled: true, majorLabelsEnabled: false, majorLineWidthMultiplier: 2,
|
||||
volumeEnabled: false, volumeMinimumHeightMeters: 0, volumeMaximumHeightMeters: 500, volumeBandHeightMeters: 500,
|
||||
selectionFillColor: "#35cfff", selectionFillOpacityPercent: 12,
|
||||
selectionOutlineColor: "#35cfff", selectionOutlineWidthPx: 3, selectionOutlineOpacityPercent: 95,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -618,6 +630,11 @@ function promoteLegacyGridLodProfiles(settings) {
|
||||
volumeMinimumHeightMeters: _volumeMinimumHeightMeters,
|
||||
volumeMaximumHeightMeters: _volumeMaximumHeightMeters,
|
||||
volumeBandHeightMeters: _volumeBandHeightMeters,
|
||||
selectionFillColor: _selectionFillColor,
|
||||
selectionFillOpacityPercent: _selectionFillOpacityPercent,
|
||||
selectionOutlineColor: _selectionOutlineColor,
|
||||
selectionOutlineWidthPx: _selectionOutlineWidthPx,
|
||||
selectionOutlineOpacityPercent: _selectionOutlineOpacityPercent,
|
||||
...legacyFallback
|
||||
} = fallback;
|
||||
return {
|
||||
@@ -679,6 +696,11 @@ function validateGridLodProfiles(value) {
|
||||
volumeMinimumHeightMeters: profile.volumeMinimumHeightMeters ?? DEFAULT_GRID_LOD_PROFILES[index].volumeMinimumHeightMeters,
|
||||
volumeMaximumHeightMeters: profile.volumeMaximumHeightMeters ?? DEFAULT_GRID_LOD_PROFILES[index].volumeMaximumHeightMeters,
|
||||
volumeBandHeightMeters: profile.volumeBandHeightMeters ?? DEFAULT_GRID_LOD_PROFILES[index].volumeBandHeightMeters,
|
||||
selectionFillColor: profile.selectionFillColor ?? DEFAULT_GRID_LOD_PROFILES[index].selectionFillColor,
|
||||
selectionFillOpacityPercent: profile.selectionFillOpacityPercent ?? DEFAULT_GRID_LOD_PROFILES[index].selectionFillOpacityPercent,
|
||||
selectionOutlineColor: profile.selectionOutlineColor ?? DEFAULT_GRID_LOD_PROFILES[index].selectionOutlineColor,
|
||||
selectionOutlineWidthPx: profile.selectionOutlineWidthPx ?? DEFAULT_GRID_LOD_PROFILES[index].selectionOutlineWidthPx,
|
||||
selectionOutlineOpacityPercent: profile.selectionOutlineOpacityPercent ?? DEFAULT_GRID_LOD_PROFILES[index].selectionOutlineOpacityPercent,
|
||||
};
|
||||
delete normalized.lineWidthPx;
|
||||
if (GRID_LOD_PROFILE_KEYS.size !== Object.keys(normalized).length
|
||||
@@ -771,6 +793,11 @@ function validateGridLodProfiles(value) {
|
||||
volumeMinimumHeightMeters,
|
||||
volumeMaximumHeightMeters,
|
||||
volumeBandHeightMeters,
|
||||
selectionFillColor: requireHex(normalized.selectionFillColor, code("selectionFillColor")),
|
||||
selectionFillOpacityPercent: requireNumber(normalized.selectionFillOpacityPercent, 0, 100, code("selectionFillOpacityPercent")),
|
||||
selectionOutlineColor: requireHex(normalized.selectionOutlineColor, code("selectionOutlineColor")),
|
||||
selectionOutlineWidthPx: requireNumber(normalized.selectionOutlineWidthPx, 1, 12, code("selectionOutlineWidthPx")),
|
||||
selectionOutlineOpacityPercent: requireNumber(normalized.selectionOutlineOpacityPercent, 0, 100, code("selectionOutlineOpacityPercent")),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -492,6 +492,11 @@ const mapPageSettingsPatchInputSchema = {
|
||||
volumeMinimumHeightMeters: { type: "number", minimum: -1000, maximum: 10000 },
|
||||
volumeMaximumHeightMeters: { type: "number", minimum: -1000, maximum: 10000 },
|
||||
volumeBandHeightMeters: { type: "number", minimum: 1, maximum: 10000 },
|
||||
selectionFillColor: { type: "string", pattern: "^#[0-9A-Fa-f]{6}$" },
|
||||
selectionFillOpacityPercent: { type: "number", minimum: 0, maximum: 100 },
|
||||
selectionOutlineColor: { type: "string", pattern: "^#[0-9A-Fa-f]{6}$" },
|
||||
selectionOutlineWidthPx: { type: "number", minimum: 1, maximum: 12 },
|
||||
selectionOutlineOpacityPercent: { type: "number", minimum: 0, maximum: 100 },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -15,6 +15,11 @@ const extensionKeys = [
|
||||
"volumeMinimumHeightMeters",
|
||||
"volumeMaximumHeightMeters",
|
||||
"volumeBandHeightMeters",
|
||||
"selectionFillColor",
|
||||
"selectionFillOpacityPercent",
|
||||
"selectionOutlineColor",
|
||||
"selectionOutlineWidthPx",
|
||||
"selectionOutlineOpacityPercent",
|
||||
];
|
||||
|
||||
function applicationError(code, statusCode = 400) {
|
||||
@@ -96,3 +101,40 @@ test("flat layouts migrate mode-aware extensions while explicit invalid hierarch
|
||||
test("Foundry MCP exposes every persisted sector-v2 profile field", () => {
|
||||
for (const key of extensionKeys) assert.match(mcpSource, new RegExp(`\\b${key}: \\{`));
|
||||
});
|
||||
|
||||
test("server persists selected-sector visuals and accepts a 1,000 m address band inside its range", () => {
|
||||
const profiles = structuredClone(DEFAULT_GRID_LOD_PROFILES);
|
||||
Object.assign(profiles[0], {
|
||||
volumeMaximumHeightMeters: 1_000,
|
||||
volumeBandHeightMeters: 1_000,
|
||||
selectionFillColor: "#A0B1C2",
|
||||
selectionFillOpacityPercent: 34,
|
||||
selectionOutlineColor: "#C3D4E5",
|
||||
selectionOutlineWidthPx: 7.5,
|
||||
selectionOutlineOpacityPercent: 81,
|
||||
});
|
||||
const validated = structuredClone(serverGrid.validateGridLodProfiles(profiles));
|
||||
assert.equal(validated[0].volumeBandHeightMeters, 1_000);
|
||||
assert.equal(validated[0].selectionFillColor, "#a0b1c2");
|
||||
assert.equal(validated[0].selectionFillOpacityPercent, 34);
|
||||
assert.equal(validated[0].selectionOutlineColor, "#c3d4e5");
|
||||
assert.equal(validated[0].selectionOutlineWidthPx, 7.5);
|
||||
assert.equal(validated[0].selectionOutlineOpacityPercent, 81);
|
||||
});
|
||||
|
||||
test("server rejects selected-sector visual values outside the canonical bounds", () => {
|
||||
for (const [field, value] of [
|
||||
["selectionFillColor", "cyan"],
|
||||
["selectionFillOpacityPercent", 101],
|
||||
["selectionOutlineColor", "#ffff"],
|
||||
["selectionOutlineWidthPx", 12.01],
|
||||
["selectionOutlineOpacityPercent", -1],
|
||||
]) {
|
||||
const profiles = structuredClone(DEFAULT_GRID_LOD_PROFILES);
|
||||
profiles[0][field] = value;
|
||||
assert.throws(
|
||||
() => serverGrid.validateGridLodProfiles(profiles),
|
||||
new RegExp(`invalid_map_grid_lod_profile_1_${field}`),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user