feat: 내정보 상세 설명 툴팁 이관

말·무기·서적·도구·병종·특기 설명과 국가 성향 정보를 API에 투영하고 Tippy.js 기반 공용 툴팁으로 표시한다. 실제 Chromium의 hover·focus와 모바일 경계를 회귀 테스트한다.
This commit is contained in:
2026-08-21 15:55:44 +00:00
parent 9e0f1f6003
commit a0e64a6f74
10 changed files with 708 additions and 30 deletions
+24 -7
View File
@@ -17,8 +17,8 @@ import { ConflictingTurnDaemonCommandError } from '../../daemon/databaseTranspor
import { resolveAccessWindows } from '../../services/generalAccess.js';
import { adjustAccountIconForUser } from '../../services/accountIconSync.js';
import {
loadCrewTypeDisplayNames,
loadItemDisplayNames,
loadCrewTypeDisplayDetails,
loadItemDisplayDetails,
resolveCityLevelName,
resolveDedicationLevelName,
resolveNationLevelName,
@@ -428,13 +428,13 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
}
}
}
const [personalityNames, domesticNames, warNames, nationTypeNames, crewTypeNames, itemNames] = await Promise.all([
const [personalityNames, domesticNames, warNames, nationTypeNames, crewTypeDetails, itemDetails] = await Promise.all([
loadTraitNames([general.personalCode], 'personality'),
loadTraitNames([general.specialCode], 'domestic'),
loadTraitNames([general.special2Code], 'war'),
loadTraitNames([nation.typeCode], 'nation'),
loadCrewTypeDisplayNames(worldState, ctx.profile.id),
loadItemDisplayNames([general.horseCode, general.weaponCode, general.bookCode, general.itemCode]),
loadCrewTypeDisplayDetails(worldState, ctx.profile.id),
loadItemDisplayDetails([general.horseCode, general.weaponCode, general.bookCode, general.itemCode]),
]);
const worldConfig = asRecord(worldState?.config);
@@ -461,7 +461,11 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
);
const itemName = (code: string | null): string | null => {
const normalized = normalizeItemCode(code);
return normalized ? (itemNames.get(normalized) ?? sanitizeInternalDisplayCode(normalized)) : null;
return normalized ? (itemDetails.get(normalized)?.name ?? sanitizeInternalDisplayCode(normalized)) : null;
};
const itemInfo = (code: string | null): string | null => {
const normalized = normalizeItemCode(code);
return normalized ? (itemDetails.get(normalized)?.info ?? '') : null;
};
const worldMeta = asRecord(worldState?.meta);
const rawLastExecuted = worldMeta.lastTurnTime ?? worldMeta.turntime;
@@ -523,12 +527,18 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
worldState?.tickSeconds ?? 0
),
crewTypeId: general.crewTypeId,
crewTypeName: crewTypeNames.get(general.crewTypeId) ?? '-',
crewTypeName: crewTypeDetails.get(general.crewTypeId)?.name ?? '-',
crewTypeInfo: crewTypeDetails.get(general.crewTypeId) ?? null,
traits: {
personal: resolveTraitDisplayName(general.personalCode, personalityNames),
specialDomestic: resolveTraitDisplayName(general.specialCode, domesticNames),
specialWar: resolveTraitDisplayName(general.special2Code, warNames),
},
traitInfo: {
personal: personalityNames.get(general.personalCode)?.info ?? '',
specialDomestic: domesticNames.get(general.specialCode)?.info ?? '',
specialWar: warNames.get(general.special2Code)?.info ?? '',
},
progression: {
experienceLevel: readNumber(metaRecord.explevel, 0),
dedicationLevel,
@@ -562,6 +572,12 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
book: itemName(general.bookCode),
item: itemName(general.itemCode),
},
itemInfo: {
horse: itemInfo(general.horseCode),
weapon: itemInfo(general.weaponCode),
book: itemInfo(general.bookCode),
item: itemInfo(general.itemCode),
},
troop: troop
? {
name: troop.name,
@@ -603,6 +619,7 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
capitalCityId: nation.capitalCityId,
levelName: resolveNationLevelName(nation.level),
typeName: nation.id === 0 ? '-' : (nationType?.name ?? sanitizeInternalDisplayCode(nation.typeCode)),
typeInfo: nation.id === 0 ? '' : (nationType?.info ?? ''),
typePros: nationTypeEffects.pros,
typeCons: nationTypeEffects.cons,
capitalCityName: nation.id === 0 ? null : (capitalCity?.name ?? null),