feat: 검증된 인게임 글자 크기를 네 단계로 통합

This commit is contained in:
2026-09-15 15:45:16 +00:00
parent 7dc1670935
commit e094246963
80 changed files with 3595 additions and 329 deletions
@@ -0,0 +1,2914 @@
{
"main": {
"lobby.info": {
"result": {
"data": {
"myGeneral": {
"id": 7,
"name": "가나다라마바사아자"
},
"serverId": "che_fixture_season",
"profile": "che",
"gameIdx": 101,
"year": 185,
"month": 1,
"turnTerm": 10,
"serverTime": "2026-08-13T00:00:00.000Z",
"serverWallTime": "2026-08-13T00:00:00.000Z",
"clockMode": "realtime",
"clockRunning": true,
"clockStartsAt": null,
"clockRecovery": null,
"turnEngineRunning": true,
"scenarioTitle": "",
"autorunUser": null
}
}
},
"auth.status": {
"result": {
"data": {
"ok": true
}
}
},
"dashboard.getContextBundleDelta": {
"result": {
"data": {
"context": {
"kind": "snapshot",
"revision": "AAAAAAAAAAAAAAAAAAAAAA",
"data": {
"general": {
"id": 7,
"name": "가나다라마바사아자",
"nationId": 1,
"cityId": 1,
"troopId": 7,
"npcState": 0,
"officerLevel": 12,
"officerLevelText": "군주",
"officerCityName": null,
"generalType": "용장",
"leadershipBonus": 6,
"picture": null,
"imageServer": 0,
"stats": {
"leadership": 70,
"strength": 60,
"intelligence": 50
},
"gold": 1000,
"rice": 2000,
"crew": 300,
"train": 80,
"atmos": 90,
"injury": 0,
"experience": 100,
"dedication": 200,
"age": 25,
"retirementYear": 70,
"defenceTrain": 80,
"killTurn": 5,
"remainingMinutes": 7,
"troop": {
"name": "백마대",
"status": "present",
"leaderCityName": "업"
},
"refreshScore": {
"current": 3,
"total": 120,
"text": "보통"
},
"progression": {
"experienceLevel": 1,
"dedicationLevel": 2,
"statExperience": {
"leadership": 5,
"strength": 10,
"intelligence": 15
},
"statUpgradeLimit": 20,
"dex": [350, 100000, 500000, 1000000, 1275975]
},
"items": {
"horse": null,
"weapon": null,
"book": null,
"item": null
},
"itemNames": {
"horse": "적토마",
"weapon": "청룡언월도",
"book": "육도",
"item": "옥벽"
},
"crewTypeId": 1100,
"crewTypeName": "보병",
"traits": {
"personal": "대담",
"specialDomestic": "상재",
"specialWar": "무쌍"
},
"turnTime": "0185-01-01T00:00:00.000Z",
"nextTurnMonthOffset": 0
},
"city": {
"id": 1,
"name": "업",
"level": 8,
"levelName": "특",
"nationId": 1,
"nationName": "대한민국천하통일국",
"nationColor": "#008000",
"region": 1,
"regionName": "중원",
"population": 100000,
"populationMax": 200000,
"agriculture": 1000,
"agricultureMax": 2000,
"commerce": 1000,
"commerceMax": 2000,
"security": 1000,
"securityMax": 2000,
"trust": 80,
"trade": 100,
"defence": 1000,
"defenceMax": 2000,
"wall": 1000,
"wallMax": 2000,
"supplyState": 1,
"frontState": 0,
"state": 0,
"officers": {
"2": {
"id": 10,
"name": "가나다라마바사아자",
"npcState": 6
},
"3": {
"id": 9,
"name": "가나다라마바사아자",
"npcState": 2
},
"4": {
"id": 8,
"name": "가나다라마바사아자",
"npcState": 0
}
}
},
"nation": {
"id": 1,
"name": "가나다라마바사아자",
"color": "#008000",
"level": 3,
"gold": 10000,
"rice": 20000,
"tech": 100,
"rate": 20,
"bill": 100,
"capitalCityId": 1,
"typeCode": "che_유가",
"typeName": "유가",
"typePros": "농상↑ 민심↑",
"typeCons": "쌀수입↓",
"population": {
"cityCount": 2,
"current": 150000,
"max": 620500
},
"crew": {
"generalCount": 2,
"current": 500,
"max": 7000
},
"power": 1234,
"taxRate": 20,
"strategicCommandLimit": 2,
"diplomaticLimit": 0,
"prohibitScout": false,
"prohibitWar": true,
"techLevel": 0,
"techLimited": false,
"topChiefs": {
"11": {
"id": 2,
"name": "가나다라마바사아자",
"npcState": 1
},
"12": {
"id": 1,
"name": "가나다라마바사아자",
"npcState": 0
}
},
"impossibleStrategicCommands": [
{
"name": "수몰",
"remainingTurns": 2,
"availableYear": 190,
"availableMonth": 5
}
]
},
"settings": {},
"penalties": {}
},
"sourceRevision": "AAAAAAAAAAAAAAAAAAAAAA"
},
"commandTable": {
"kind": "snapshot",
"revision": "CCCCCCCCCCCCCCCCCCCCCC",
"sourceRevision": "CCCCCCCCCCCCCCCCCCCCCC",
"data": {
"general": [],
"nation": [],
"inputOptions": {
"cities": [
{
"value": 1,
"label": "도시 1"
},
{
"value": 2,
"label": "도시 2"
},
{
"value": 3,
"label": "도시 3"
},
{
"value": 4,
"label": "도시 4"
},
{
"value": 5,
"label": "도시 5"
},
{
"value": 6,
"label": "도시 6"
},
{
"value": 7,
"label": "도시 7"
},
{
"value": 8,
"label": "도시 8"
},
{
"value": 9,
"label": "도시 9"
},
{
"value": 10,
"label": "도시 10"
},
{
"value": 11,
"label": "도시 11"
},
{
"value": 12,
"label": "도시 12"
},
{
"value": 13,
"label": "도시 13"
},
{
"value": 14,
"label": "도시 14"
},
{
"value": 15,
"label": "도시 15"
},
{
"value": 16,
"label": "도시 16"
},
{
"value": 17,
"label": "도시 17"
},
{
"value": 18,
"label": "도시 18"
},
{
"value": 19,
"label": "도시 19"
},
{
"value": 20,
"label": "도시 20"
}
],
"nations": [],
"generals": [],
"crewTypes": [],
"armTypes": [],
"nationTypes": [],
"colors": [],
"items": {}
}
}
},
"boardAccess": {
"kind": "snapshot",
"revision": "DDDDDDDDDDDDDDDDDDDDDD",
"sourceRevision": "DDDDDDDDDDDDDDDDDDDDDD",
"data": {
"permission": 4,
"canMeeting": true,
"canSecret": true
}
}
}
}
},
"world.getState": {
"result": {
"data": {
"currentYear": 185,
"currentMonth": 1,
"tickSeconds": 600,
"config": {
"npcMode": 1,
"const": {},
"environment": {}
},
"meta": {}
}
}
},
"world.getMapLayout": {
"result": {
"data": {
"mapName": "che",
"cityList": [
{
"id": 1,
"name": "업",
"level": 8,
"region": 1,
"x": 200,
"y": 120,
"path": []
}
],
"regionMap": {
"1": "하북"
},
"levelMap": {
"8": "특"
}
}
}
},
"turns.reserved.getGeneral": {
"result": {
"data": {
"turns": [],
"revision": 0,
"autorunLimit": null
}
}
},
"general.getRecentRecords": {
"result": {
"data": {
"global": [
{
"id": 3,
"text": "장수 동향 기록"
}
],
"general": [
{
"id": 2,
"text": "개인 기록"
}
],
"history": [
{
"id": 1,
"text": "중원 정세 기록"
}
]
}
}
},
"general.getFrontStatus": {
"result": {
"data": {
"serverId": "che_fixture_season",
"onlineUserCount": 1,
"onlineNations": "위(1)",
"onlineGenerals": "메뉴검증장수",
"nationNotice": "<p>국가 방침</p>",
"lastExecuted": "2026-08-13T00:05:06.000Z",
"latestVote": {
"id": 9,
"title": "메뉴 설문",
"hasVoted": false
}
}
}
},
"tournament.getState": {
"result": {
"data": {
"stage": 5,
"type": 0
}
}
},
"world.getMap": {
"result": {
"data": {
"result": true,
"version": 0,
"startYear": 180,
"year": 185,
"month": 1,
"cityList": [[1, 8, 0, 1, 1, 1]],
"nationList": [[1, "대한민국천하통일국", "#008000", 1]],
"spyList": {},
"shownByGeneralList": [],
"myCity": 1,
"myNation": 1
}
}
},
"messages.getRecent": {
"result": {
"data": {
"private": [],
"national": [],
"public": [],
"diplomacy": [],
"permission": 4,
"sequence": -1,
"hasMore": {
"private": false,
"national": false,
"public": false,
"diplomacy": false
},
"latestRead": {
"private": 0,
"national": 0,
"public": 0,
"diplomacy": 0
},
"canRespondDiplomacy": false
}
}
},
"messages.getContacts": {
"result": {
"data": {
"nation": []
}
}
}
},
"chief": {
"lobby.info": {
"result": {
"data": {
"myGeneral": {
"id": 1,
"name": "가나다라마바사아자"
},
"year": 200,
"month": 1,
"turnTerm": 10,
"userCnt": 1,
"maxUserCnt": 100,
"npcCnt": 0,
"nationCnt": 2
}
}
},
"auth.status": {
"result": {
"data": {
"ok": true
}
}
},
"nation.getChiefCenter": {
"result": {
"data": {
"me": {
"id": 1,
"officerLevel": 5,
"nationId": 1
},
"nation": {
"id": 1,
"name": "대한민국천하통일국",
"level": 1
},
"currentYear": 200,
"currentMonth": 1,
"turnTermMinutes": 10,
"maxTurns": 12,
"chiefs": [
{
"officerLevel": 12,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "che_포상",
"args": {
"destGeneralId": 2,
"isGold": false,
"amount": 300
}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 10,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 8,
"name": "가나다라마바사아자",
"npcState": 2,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 6,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 11,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 9,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 7,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
},
{
"officerLevel": 5,
"name": "가나다라마바사아자",
"npcState": 0,
"turnTime": "2026-08-30T01:46:47.590Z",
"revision": 0,
"turns": [
{
"index": 0,
"action": "휴식",
"args": {}
},
{
"index": 1,
"action": "휴식",
"args": {}
},
{
"index": 2,
"action": "휴식",
"args": {}
},
{
"index": 3,
"action": "휴식",
"args": {}
},
{
"index": 4,
"action": "휴식",
"args": {}
},
{
"index": 5,
"action": "휴식",
"args": {}
},
{
"index": 6,
"action": "휴식",
"args": {}
},
{
"index": 7,
"action": "휴식",
"args": {}
},
{
"index": 8,
"action": "휴식",
"args": {}
},
{
"index": 9,
"action": "휴식",
"args": {}
},
{
"index": 10,
"action": "휴식",
"args": {}
},
{
"index": 11,
"action": "휴식",
"args": {}
}
]
}
]
}
}
},
"turns.getCommandTable": {
"result": {
"data": {
"general": [
{
"category": "계략",
"values": [
{
"key": "che_화계",
"name": "화계",
"reqArg": true,
"possible": false,
"status": "blocked",
"reason": "현재 조건에서는 실행할 수 없습니다.",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_선동",
"name": "선동",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_탈취",
"name": "탈취",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_파괴",
"name": "파괴",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
}
]
},
{
"category": "내정",
"values": [
{
"key": "che_징병",
"name": "징병",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "crewType",
"label": "병종",
"kind": "select",
"required": true,
"optionSource": "crewTypes"
},
{
"key": "amount",
"label": "수량",
"kind": "number",
"required": true,
"min": 0,
"step": 1
}
]
},
{
"key": "che_모병",
"name": "모병",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "crewType",
"label": "병종",
"kind": "select",
"required": true,
"optionSource": "crewTypes"
},
{
"key": "amount",
"label": "수량",
"kind": "number",
"required": true,
"min": 0,
"step": 1
}
]
}
]
},
{
"category": "군사",
"values": [
{
"key": "che_출병",
"name": "출병",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_첩보",
"name": "첩보",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
}
]
}
],
"nation": [
{
"category": "인사",
"values": [
{
"key": "che_발령",
"name": "발령",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destGeneralId",
"label": "대상 장수",
"kind": "select",
"required": true,
"optionSource": "generals"
},
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_포상",
"name": "포상",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "isGold",
"label": "물자",
"kind": "boolean",
"required": true
},
{
"key": "amount",
"label": "수량",
"kind": "number",
"required": true,
"min": 0,
"step": 1
},
{
"key": "destGeneralId",
"label": "대상 장수",
"kind": "select",
"required": true,
"optionSource": "generals"
}
]
},
{
"key": "che_몰수",
"name": "몰수",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "isGold",
"label": "물자",
"kind": "boolean",
"required": true
},
{
"key": "amount",
"label": "수량",
"kind": "number",
"required": true,
"min": 0,
"step": 1
},
{
"key": "destGeneralId",
"label": "대상 장수",
"kind": "select",
"required": true,
"optionSource": "generals"
}
]
},
{
"key": "che_부대탈퇴지시",
"name": "부대 탈퇴 지시",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destGeneralId",
"label": "대상 장수",
"kind": "select",
"required": true,
"optionSource": "generals"
}
]
}
]
},
{
"category": "외교",
"values": [
{
"key": "che_물자원조",
"name": "원조",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
},
{
"key": "amountList",
"label": "지원 물자",
"kind": "numberTuple",
"required": true,
"min": 0,
"step": 1,
"tupleLabels": ["금", "쌀"]
}
]
},
{
"key": "che_불가침제의",
"name": "불가침 제의",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
},
{
"key": "year",
"label": "기간(년)",
"kind": "number",
"required": true,
"min": 191,
"max": 210,
"step": 1,
"defaultValue": 191
},
{
"key": "month",
"label": "기간(월)",
"kind": "number",
"required": true,
"min": 1,
"max": 12,
"step": 1,
"defaultValue": 1
}
]
},
{
"key": "che_선전포고",
"name": "선전포고",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
}
]
},
{
"key": "che_종전제의",
"name": "종전 제의",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
}
]
},
{
"key": "che_불가침파기제의",
"name": "불가침 파기 제의",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
}
]
}
]
},
{
"category": "특수",
"values": [
{
"key": "che_초토화",
"name": "초토화",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_천도",
"name": "천도",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_증축",
"name": "증축",
"reqArg": false,
"possible": true,
"status": "available",
"inputFields": []
},
{
"key": "che_감축",
"name": "감축",
"reqArg": false,
"possible": true,
"status": "available",
"inputFields": []
}
]
},
{
"category": "전략",
"values": [
{
"key": "che_백성동원",
"name": "백성동원",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_수몰",
"name": "수몰",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_허보",
"name": "허보",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destCityId",
"label": "대상 도시",
"kind": "select",
"required": true,
"optionSource": "cities"
}
]
},
{
"key": "che_이호경식",
"name": "이호경식",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
}
]
},
{
"key": "che_급습",
"name": "급습",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
}
]
},
{
"key": "che_피장파장",
"name": "피장파장",
"reqArg": true,
"possible": true,
"status": "needsInput",
"inputFields": [
{
"key": "destNationId",
"label": "대상 국가",
"kind": "select",
"required": true,
"optionSource": "nations"
},
{
"key": "commandType",
"label": "대응 명령",
"kind": "select",
"required": true,
"options": [
{
"value": "che_수몰",
"label": "수몰"
}
]
}
]
}
]
}
],
"inputOptions": {
"cities": [
{
"value": 1,
"label": "업 (아국)"
},
{
"value": 2,
"label": "허창 (적국)",
"description": "적국 · 예주 · 대도시"
},
{
"value": 3,
"label": "단양 (오)"
}
],
"nations": [
{
"value": 1,
"label": "아국",
"color": "#008000"
},
{
"value": 2,
"label": "적국",
"color": "#800000",
"description": "수도 허창"
},
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"description": "수도 단양"
}
],
"nationTargets": {
"che_물자원조": [
{
"value": 2,
"label": "적국",
"color": "#800000",
"availableNow": true,
"description": "현재 원조 대상 · 교역"
},
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"availableNow": true,
"description": "현재 원조 대상 · 불가침 12턴"
},
{
"value": 1,
"label": "아국",
"color": "#008000",
"availableNow": false,
"description": "아국은 대상이 아닙니다."
}
],
"che_불가침제의": [
{
"value": 2,
"label": "적국",
"color": "#800000",
"availableNow": true,
"description": "현재 제의 가능 · 교역"
},
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"availableNow": true,
"description": "현재 제의 가능 · 불가침 12턴"
},
{
"value": 1,
"label": "아국",
"color": "#008000",
"availableNow": false,
"description": "아국은 대상이 아닙니다."
}
],
"che_선전포고": [
{
"value": 2,
"label": "적국",
"color": "#800000",
"availableNow": true,
"description": "현재 선전포고 가능 · 교역"
},
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"availableNow": false,
"description": "현재 외교 관계에서는 선전포고 불가"
},
{
"value": 1,
"label": "아국",
"color": "#008000",
"availableNow": false,
"description": "아국은 대상이 아닙니다."
}
],
"che_종전제의": [
{
"value": 2,
"label": "적국",
"color": "#800000",
"availableNow": true,
"description": "현재 종전 제의 가능 · 전쟁 6턴"
},
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"availableNow": false,
"description": "전쟁·선포 중인 국가가 아닙니다."
},
{
"value": 1,
"label": "아국",
"color": "#008000",
"availableNow": false,
"description": "아국은 대상이 아닙니다."
}
],
"che_불가침파기제의": [
{
"value": 3,
"label": "불가침국",
"color": "#000080",
"availableNow": true,
"description": "현재 불가침 파기 제의 가능 · 불가침 12턴"
},
{
"value": 2,
"label": "적국",
"color": "#800000",
"availableNow": false,
"description": "불가침 중인 국가가 아닙니다."
},
{
"value": 1,
"label": "아국",
"color": "#008000",
"availableNow": false,
"description": "아국은 대상이 아닙니다."
}
]
},
"generals": [
{
"value": 1,
"label": "장수 (아국 · 업)"
},
{
"value": 2,
"label": "관우 (아국 · 업)"
}
],
"generalTargets": {
"che_포상": [
{
"value": 1,
"label": "장수 (업)",
"gold": 5000,
"rice": 400,
"crew": 500,
"description": "금 5,000 · 쌀 400 · 병력 500"
},
{
"value": 2,
"label": "관우 (업)",
"gold": 100,
"rice": 4000,
"crew": 1200,
"troopId": 2,
"description": "금 100 · 쌀 4,000 · 병력 1,200"
},
{
"value": 3,
"label": "여포NPC (업)",
"npcState": 2,
"gold": 3000,
"rice": 500,
"crew": 1500,
"troopId": 2,
"description": "금 3,000 · 쌀 500 · 병력 1,500"
}
],
"che_몰수": [
{
"value": 1,
"label": "장수 (업)",
"gold": 5000,
"rice": 400,
"crew": 500,
"description": "금 5,000 · 쌀 400 · 병력 500"
},
{
"value": 2,
"label": "관우 (업)",
"gold": 100,
"rice": 4000,
"crew": 1200,
"troopId": 2,
"description": "금 100 · 쌀 4,000 · 병력 1,200"
},
{
"value": 3,
"label": "여포NPC (업)",
"npcState": 2,
"gold": 3000,
"rice": 500,
"crew": 1500,
"troopId": 2,
"description": "금 3,000 · 쌀 500 · 병력 1,500"
}
],
"che_발령": [
{
"value": 1,
"label": "장수 (업)",
"targetNames": {
"name": "장수",
"nationName": "대한민국천하통일국",
"cityName": "업",
"troopName": null
},
"description": "통솔 70 · 무력 80 · 지력 90\n병력 500 · 훈련 0 · 사기 0\n금 5,000 · 쌀 400",
"gold": 5000,
"rice": 400,
"crew": 500,
"troopId": 0,
"npcState": 0
},
{
"value": 2,
"label": "관우 (업)",
"targetNames": {
"name": "관우",
"nationName": "대한민국천하통일국",
"cityName": "업",
"troopName": "청룡대"
},
"description": "통솔 100 · 무력 95 · 지력 70\n병력 1,200 · 훈련 80 · 사기 70\n금 100 · 쌀 4,000",
"gold": 100,
"rice": 4000,
"crew": 1200,
"troopId": 2,
"npcState": 0
},
{
"value": 3,
"label": "여포NPC (업)",
"targetNames": {
"name": "여포NPC",
"nationName": "대한민국천하통일국",
"cityName": "업",
"troopName": "청룡대"
},
"description": "통솔 90 · 무력 100 · 지력 0\n병력 1,500 · 훈련 100 · 사기 100\n금 3,000 · 쌀 500",
"gold": 3000,
"rice": 500,
"crew": 1500,
"troopId": 2,
"npcState": 2
}
],
"che_부대탈퇴지시": [
{
"value": 3,
"label": "여포NPC (아국 · 업)",
"npcState": 2,
"availableNow": true,
"crew": 1500,
"troopId": 2,
"description": "현재 탈퇴 지시 가능 · 병력 1,500 · 탑승 부대 청룡대"
},
{
"value": 1,
"label": "장수 (아국 · 업)",
"availableNow": false,
"crew": 500,
"description": "현재 탈퇴 지시 불가 · 탑승 부대 없음"
},
{
"value": 2,
"label": "관우 (아국 · 업)",
"availableNow": false,
"crew": 1200,
"troopId": 2,
"description": "현재 탈퇴 지시 불가 · 탑승 부대 청룡대 (부대장)"
}
]
},
"crewTypes": [
{
"value": 1100,
"label": "보병"
}
],
"armTypes": [
{
"value": 1,
"label": "보병"
}
],
"nationTypes": [
{
"value": "che_도적",
"label": "도적",
"description": "금 수입 증가, 쌀 수입 감소"
}
],
"colors": [
{
"value": 0,
"label": "색상 1",
"color": "#ff0000"
}
],
"items": {
"horse": [
{
"value": "None",
"label": "판매/해제"
}
]
},
"recruitment": {
"techLevel": 1,
"leadership": 68,
"fullLeadership": 70,
"currentCrewTypeId": 1100,
"currentCrewTypeName": "보병",
"crew": 500,
"gold": 12345,
"groups": [
{
"armType": 1,
"armName": "보병",
"values": [
{
"id": 1100,
"armType": 1,
"name": "보병",
"available": true,
"special": false,
"attack": 125,
"defence": 175,
"speed": 7,
"avoid": 10,
"baseCost": 10.35,
"baseRice": 10.35,
"info": ["표준적인 보병입니다.", "보병은 방어특화입니다."]
},
{
"id": 1101,
"armType": 1,
"name": "정예병",
"available": false,
"special": true,
"attack": 175,
"defence": 225,
"speed": 8,
"avoid": 20,
"baseCost": 13.8,
"baseRice": 11.5,
"info": [
"강력하지만 기술이 필요합니다.",
"기술력 2000 이상 필요",
"중원 지역 소유시 가능",
"저 소유시 가능"
]
}
]
}
]
},
"amountPresets": {
"che_포상": {
"values": [100, 500, 1000, 5000, 10000],
"defaultValue": 1000,
"min": 100,
"max": 10000,
"step": 1
},
"che_몰수": {
"values": [100, 500, 1000, 5000, 10000],
"defaultValue": 1000,
"min": 100,
"max": 10000,
"step": 1
},
"che_물자원조": {
"values": [10000, 20000, 30000],
"defaultValue": 1000,
"min": 1000,
"max": 30000,
"step": 10
}
},
"context": {
"actorGold": 1000,
"actorRice": 1000,
"citySecurity": 500,
"nationGold": 5000,
"nationRice": 6000,
"nationLevel": 1
}
}
}
}
},
"world.getMap": {
"result": {
"data": {
"result": true,
"version": 0,
"startYear": 180,
"year": 200,
"month": 1,
"cityList": [
[1, 8, 0, 1, 1, 1],
[2, 7, 41, 2, 2, 1]
],
"nationList": [
[1, "대한민국천하통일국", "#008000", 1],
[2, "대한민국천하통일국", "#800000", 2]
],
"spyList": {},
"shownByGeneralList": [],
"myCity": 1,
"myNation": 1
}
}
},
"world.getMapLayout": {
"result": {
"data": {
"mapName": "che",
"cityList": [
{
"id": 1,
"name": "업",
"level": 8,
"region": 1,
"x": 100,
"y": 100,
"path": [2]
},
{
"id": 2,
"name": "허창",
"level": 7,
"region": 2,
"x": 240,
"y": 180,
"path": [1]
}
],
"regionMap": {
"1": "하북",
"2": "예주"
},
"levelMap": {
"7": "대",
"8": "특"
}
}
}
}
},
"personnel": {
"lobby.info": {
"result": {
"data": {
"myGeneral": {
"id": 1,
"name": "가나다라마바사아자"
}
}
}
},
"auth.status": {
"result": {
"data": {
"ok": true
}
}
},
"nation.getPersonnelInfo": {
"result": {
"data": {
"me": {
"id": 1,
"officerLevel": 12,
"canManage": true,
"canChangePermissions": true,
"canKick": true
},
"nation": {
"id": 1,
"name": "대한민국천하통일국",
"color": "#777777",
"level": 3,
"typeCode": "che_법가",
"capitalCityId": 1,
"chiefSet": 0
},
"chiefStatMin": 65,
"generals": [
{
"id": 1,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 12,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 2,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 11,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 3,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 4,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 4,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 3,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 5,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 2,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 6,
"name": "가나다라마바사아자",
"npcState": 2,
"officerLevel": 1,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
{
"id": 7,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 1,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "ambassador"
},
{
"id": 8,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 1,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "auditor"
},
{
"id": 9,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 1,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
}
],
"chiefAssignments": {
"11": {
"id": 2,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 11,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
"12": {
"id": 1,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 12,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 0,
"officerCityName": null,
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
}
},
"cityAssignments": [
{
"id": 1,
"name": "허창",
"level": 7,
"region": 2,
"officerSet": 8,
"officers": {
"2": {
"id": 5,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 2,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
"3": {
"id": 4,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 3,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
},
"4": {
"id": 3,
"name": "가나다라마바사아자",
"npcState": 0,
"officerLevel": 4,
"cityId": 1,
"cityName": "허창",
"troopId": 0,
"troopName": null,
"picture": "default.jpg",
"imageServer": 0,
"officerCity": 1,
"officerCityName": "허창",
"stats": {
"leadership": 70,
"strength": 70,
"intelligence": 70
},
"experience": 100,
"dedication": 200,
"injury": 0,
"gold": 1000,
"rice": 1000,
"crew": 100,
"personality": null,
"specialDomestic": null,
"specialWar": null,
"belong": 10,
"permission": "normal"
}
}
},
{
"id": 2,
"name": "낙양",
"level": 6,
"region": 2,
"officerSet": 0,
"officers": {
"2": null,
"3": null,
"4": null
}
}
],
"awards": {
"tigers": [
{
"id": 3,
"name": "하후돈",
"value": 12
}
],
"eagles": [
{
"id": 4,
"name": "곽가",
"value": 9
}
]
},
"permissionCandidates": {
"ambassadors": [
{
"id": 6,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "normal",
"maxPermission": 4
},
{
"id": 7,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "ambassador",
"maxPermission": 4
},
{
"id": 9,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "normal",
"maxPermission": 4
}
],
"auditors": [
{
"id": 6,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "normal",
"maxPermission": 4
},
{
"id": 8,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "auditor",
"maxPermission": 4
},
{
"id": 9,
"name": "가나다라마바사아자",
"npcState": 0,
"permission": "normal",
"maxPermission": 4
}
]
}
}
}
}
},
"possession": {
"lobby.info": {
"result": {
"data": {
"myGeneral": null,
"year": 180,
"month": 1,
"turnTerm": 5
}
}
},
"auth.status": {
"result": {
"data": {
"ok": true
}
}
},
"join.getConfig": {
"result": {
"data": {
"rules": {
"stat": {
"total": 165,
"min": 15,
"max": 80,
"bonusMin": 3,
"bonusMax": 5
},
"allowDirectCreation": false,
"allowCustomName": true
},
"user": {
"id": "npc-user",
"displayName": "빙의사용자",
"canCreateGeneral": true,
"icons": [],
"preferredPicture": null
},
"personalities": [
{
"key": "Random",
"name": "???",
"info": ""
}
],
"warSpecials": [],
"nations": [],
"serverInfo": {
"currentYear": 180,
"currentMonth": 1,
"tickMinutes": 5,
"maxGeneral": 500,
"userGeneralCount": 0,
"npcGeneralCount": 12
},
"inherit": {
"totalPoint": 0,
"costs": {
"inheritBornSpecialPoint": 0,
"inheritBornTurntimePoint": 0,
"inheritBornCityPoint": 0,
"inheritBornStatPoint": 0
},
"availableCities": [],
"turnTimeZones": [],
"availableSpecialWar": []
},
"selectionPool": {
"enabled": false
},
"npcPossession": {
"enabled": true
}
}
}
},
"join.listPossessCandidates": {
"result": {
"data": {
"tokenNonce": 101,
"validUntil": "2026-09-15T15:10:08.460Z",
"pickMoreFrom": "2026-09-15T15:08:37.460Z",
"pickMoreSeconds": 0,
"candidates": [
{
"id": 1,
"name": "가나다라마바사아자",
"nation": {
"id": 0,
"name": "대한민국천하통일국",
"color": "#aaaaaa"
},
"stats": {
"leadership": 40,
"strength": 50,
"intelligence": 60
},
"picture": "default.jpg",
"imageServer": 1,
"personality": {
"code": "che_안전",
"name": "안전",
"info": "안전을 중시합니다."
},
"specialDomestic": {
"code": "che_인덕",
"name": "인덕",
"info": "인덕 설명"
},
"specialWar": {
"code": "che_무쌍",
"name": "무쌍",
"info": "무쌍 설명"
},
"keepCount": 3
},
{
"id": 2,
"name": "가나다라마바사아자",
"nation": {
"id": 0,
"name": "대한민국천하통일국",
"color": "#aaaaaa"
},
"stats": {
"leadership": 41,
"strength": 51,
"intelligence": 61
},
"picture": "default.jpg",
"imageServer": 0,
"personality": {
"code": "che_안전",
"name": "안전",
"info": "안전을 중시합니다."
},
"specialDomestic": {
"code": "che_인덕",
"name": "인덕",
"info": "인덕 설명"
},
"specialWar": {
"code": "che_무쌍",
"name": "무쌍",
"info": "무쌍 설명"
},
"keepCount": 3
},
{
"id": 3,
"name": "가나다라마바사아자",
"nation": {
"id": 0,
"name": "대한민국천하통일국",
"color": "#aaaaaa"
},
"stats": {
"leadership": 42,
"strength": 52,
"intelligence": 62
},
"picture": "default.jpg",
"imageServer": 0,
"personality": {
"code": "che_안전",
"name": "안전",
"info": "안전을 중시합니다."
},
"specialDomestic": {
"code": "che_인덕",
"name": "인덕",
"info": "인덕 설명"
},
"specialWar": {
"code": "che_무쌍",
"name": "무쌍",
"info": "무쌍 설명"
},
"keepCount": 3
},
{
"id": 4,
"name": "가나다라마바사아자",
"nation": {
"id": 0,
"name": "대한민국천하통일국",
"color": "#aaaaaa"
},
"stats": {
"leadership": 43,
"strength": 53,
"intelligence": 63
},
"picture": "default.jpg",
"imageServer": 0,
"personality": {
"code": "che_안전",
"name": "안전",
"info": "안전을 중시합니다."
},
"specialDomestic": {
"code": "che_인덕",
"name": "인덕",
"info": "인덕 설명"
},
"specialWar": {
"code": "che_무쌍",
"name": "무쌍",
"info": "무쌍 설명"
},
"keepCount": 3
},
{
"id": 5,
"name": "가나다라마바사아자",
"nation": {
"id": 0,
"name": "대한민국천하통일국",
"color": "#aaaaaa"
},
"stats": {
"leadership": 44,
"strength": 54,
"intelligence": 64
},
"picture": "default.jpg",
"imageServer": 0,
"personality": {
"code": "che_안전",
"name": "안전",
"info": "안전을 중시합니다."
},
"specialDomestic": {
"code": "che_인덕",
"name": "인덕",
"info": "인덕 설명"
},
"specialWar": {
"code": "che_무쌍",
"name": "무쌍",
"info": "무쌍 설명"
},
"keepCount": 3
}
]
}
}
}
}
}
@@ -20,6 +20,7 @@ export default defineConfig({
testDir: '.',
testMatch: [
'troop.spec.ts',
'typographyPolicy.spec.ts',
'board.spec.ts',
'inGameInfo.spec.ts',
'infoMobileLayout.spec.ts',
@@ -0,0 +1,194 @@
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { expect, test, type Page } from '@playwright/test';
import { gameProfile, gameTrpcRoute } from './gameTestPaths.js';
// 기존 화면 fixture의 읽기 응답을 고정했다. 실제 계정이나 게임 서버에는 접근하지 않는다.
const fixtureText = await readFile(new URL('./fixtures/typography.json', import.meta.url), 'utf8');
const imageRoot = process.env.FRONTEND_PARITY_IMAGE_ROOT ?? resolve(import.meta.dirname, '../../../../image');
const fontRoot = process.env.TYPOGRAPHY_FONT_ROOT;
const names = [
{ label: '전각9자', general: '가나다라마바사아자', nation: '대한민국천하통일국' },
{ label: '반각18자', general: 'WWWWWWWWWWWWWWWWWW', nation: 'MMMMMMMMMMMMMMMMMM' },
] as const;
const installAssets = async (page: Page): Promise<void> => {
await page.route(/(?:\/image\/|sam-image\.hided\.net\/)/u, async (route) => {
const url = new URL(route.request().url());
const relative = decodeURIComponent(url.pathname.replace(/^\/image\//u, '').replace(/^\//u, ''));
if (relative.split('/').includes('..')) throw new Error('Invalid typography image path');
await route.fulfill({
body: await readFile(resolve(imageRoot, relative)),
contentType: relative.endsWith('.png')
? 'image/png'
: relative.endsWith('.gif')
? 'image/gif'
: 'image/jpeg',
});
});
if (fontRoot) {
await page.route('**/pretendard.css', (route) =>
route.fulfill({ path: resolve(fontRoot, 'source.css'), contentType: 'text/css' })
);
await page.route('**/*.woff2', (route) =>
route.fulfill({
path: resolve(fontRoot, new URL(route.request().url()).pathname.split('/').at(-1)!),
contentType: 'font/woff2',
})
);
}
};
const install = async (page: Page, scene: string, general: string, nation: string): Promise<void> => {
const fixtures = JSON.parse(
fixtureText.replaceAll(names[0].general, general).replaceAll(names[0].nation, nation)
) as Record<string, Record<string, unknown>>;
await page.addInitScript((profile) => {
localStorage.setItem('sammo-game-token', 'ga_typography_fixture');
localStorage.setItem('sammo-game-profile', profile);
localStorage.removeItem('sam_customCSS');
}, gameProfile);
await page.route('**/events**', (route) => route.abort());
await page.route(gameTrpcRoute, async (route) => {
const operations = decodeURIComponent(new URL(route.request().url()).pathname.split('/trpc/')[1]!).split(',');
const responses = operations.map((operation) => {
const response = fixtures[scene]?.[operation];
if (response !== undefined) return response;
if (operation === 'public.recordAccess') return { result: { data: { recorded: true } } };
throw new Error(`Uncovered typography fixture: ${scene}/${operation}`);
});
await route.fulfill({ contentType: 'application/json', body: JSON.stringify(responses) });
});
await installAssets(page);
};
const waitForFonts = async (page: Page): Promise<void> => {
await page.evaluate(async () => {
await document.fonts.ready;
});
expect(
await page.evaluate(() =>
[...document.fonts].some((font) => font.family.includes('Pretendard') && font.status === 'loaded')
)
).toBe(true);
};
for (const width of [500, 1000]) {
for (const name of names) {
test(`메인 주요 이름과 제목을 보존한다 ${width}px ${name.label}`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 900 });
await install(page, 'main', name.general, name.nation);
await page.goto('./');
const general = page.locator('.battle-general-name:visible').first();
await expect(general).toContainText(name.general);
const nation = page.locator('.city-nation:visible').first();
await expect(nation).toContainText(name.nation);
await waitForFonts(page);
for (const label of [general, nation]) {
expect(await label.evaluate((element) => element.scrollWidth <= element.clientWidth + 1)).toBe(true);
}
await expect(page.locator('.game-shell__title')).toHaveCSS('font-size', '24px');
await page.screenshot({ path: testInfo.outputPath('main-maximum-name.png'), fullPage: true });
});
test(`네 단계와 밀집 화면 보류 계약 ${width}px ${name.label}`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 900 });
await install(page, 'personnel', name.general, name.nation);
await page.goto('nation/personnel');
await expect(page.locator('.nation-heading')).toContainText(name.nation);
await expect(page.locator('.chief-entry-copy strong').first()).toHaveText(name.general);
await waitForFonts(page);
await expect(page.locator('.nation-heading')).toHaveCSS('font-size', width === 500 ? '16px' : '24px');
// 500px의 15px 이름/10px 잠금은 확대 시 표시 이름이 줄어들어 보류했다.
await expect(page.locator('.chief-entry-copy strong').first()).toHaveCSS(
'font-size',
width === 500 ? '15px' : '16px'
);
const geometry = await page.locator('#personnel-container').evaluate((element) => ({
width: element.getBoundingClientRect().width,
scrollWidth: element.scrollWidth,
controls: [...element.querySelectorAll<HTMLElement>('.personnel-change-button')].map((button) => {
const rect = button.getBoundingClientRect();
return { width: rect.width, height: rect.height, text: button.innerText };
}),
}));
expect(geometry.scrollWidth).toBeLessThanOrEqual(width + 1);
expect(geometry.controls.every((control) => control.width > 0 && control.height >= 24)).toBe(true);
await testInfo.attach('geometry', {
body: JSON.stringify(geometry, null, 2),
contentType: 'application/json',
});
await page.screenshot({ path: testInfo.outputPath('personnel-maximum-name.png'), fullPage: true });
});
}
test(`사령부 12턴 요약의 기존 밀도를 유지한다 ${width}px`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 900 });
await install(page, 'chief', names[0].general, names[0].nation);
await page.goto('chief-center');
const row = page.locator('.chief-overview .chief-card.compact .chief-row').first();
await expect(row).toBeVisible();
await waitForFonts(page);
await expect(row).toHaveCSS('font-size', '8.8px');
await expect(row).toHaveCSS('line-height', '11.25px');
await expect(page.locator('.chief-overview .compact-name').first()).toHaveCSS('font-size', '10.4px');
expect((await row.boundingBox())?.height).toBe(11.25);
await page.screenshot({ path: testInfo.outputPath('chief-deferred-density.png'), fullPage: true });
});
for (const length of [4, 7, 9, 18]) {
test(`빙의 이름 길이별 축소를 유지한다 ${width}px ${length}`, async ({ page }, testInfo) => {
const general = length === 18 ? names[1].general : names[0].general.slice(0, length);
await page.setViewportSize({ width, height: 900 });
await install(page, 'possession', general, names[0].nation);
await page.goto('join');
const title = page.locator('.npc-card-name').first();
await expect(title).toHaveText(general);
await waitForFonts(page);
await expect(title).toHaveCSS('font-size', length >= 9 ? '12px' : '16px');
await expect(title).toHaveCSS('white-space', 'nowrap');
expect((await title.boundingBox())?.height).toBe(25);
await page.screenshot({ path: testInfo.outputPath('possession-name-length.png'), fullPage: true });
});
}
test(`전투 로그의 장수명과 병력 축소를 유지한다 ${width}px`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 900 });
await installAssets(page);
const log =
'◆217년 7월:<div class="small_war_log">마귀 ' +
'<span class="name_plate">【Hide_D】</span> <span class="crew_plate">0(-3714)</span> ' +
'<span class="war_type_defense">←</span> <span class="crew_plate">3955(-3545)</span> ' +
'백이 <span class="name_plate">【경국지색소교】</span> 19:57</div>';
await page.route(gameTrpcRoute, async (route) => {
const operations = decodeURIComponent(new URL(route.request().url()).pathname.split('/trpc/')[1]!).split(
','
);
const data: Record<string, unknown> = {
'public.getMapLayout': { mapName: 'che', cityList: [] },
'public.getCachedMap': {
year: 217,
month: 7,
cityList: [],
nationList: [],
history: [{ id: 1, text: log }],
},
'public.getWorldTrend': { year: 217, month: 7, turnTerm: 10 },
'public.getNationList': [],
'public.getGeneralList': [],
};
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify(operations.map((operation) => ({ result: { data: data[operation] } }))),
});
});
await page.goto('public');
const summary = page.locator('.small_war_log').first();
await expect(summary).toContainText('【경국지색소교】');
await expect(summary).toHaveCSS('font-size', '14px');
await expect(summary.locator('.name_plate').first()).toHaveCSS('font-size', '10.5px');
await expect(summary.locator('.crew_plate').first()).toHaveCSS('font-size', '12.6px');
await waitForFonts(page);
await page.screenshot({ path: testInfo.outputPath('battle-log-exception.png'), fullPage: true });
});
}
+1 -1
View File
@@ -27,7 +27,7 @@ body {
background-repeat: repeat-y;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -23,12 +23,12 @@
}
.game-shell__title {
font-size: 1.6rem;
font-size: var(--sammo-font-size-title);
font-weight: 600;
}
.game-shell__subtitle {
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
color: var(--sammo-color-text-muted);
}
@@ -44,11 +44,11 @@
background: var(--sammo-color-action-bg);
color: inherit;
text-decoration: none;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
cursor: pointer;
}
.game-feedback--error {
color: var(--sammo-color-error);
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
@@ -133,7 +133,7 @@
.legacy-sort-indicator {
margin-left: 2px;
color: #9ee7ba;
font-size: 0.75em;
font-size: var(--sammo-font-size-small);
opacity: 0.65;
}
@@ -16,7 +16,7 @@
background-image: var(--sammo-texture-walnut);
color: var(--sammo-color-text);
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.5;
}
@@ -35,7 +35,7 @@
}
.ref-shell__title {
font-size: 17px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 500;
}
@@ -1,4 +1,9 @@
:root {
/* 검증된 화면이 공유하는 네 단계. 콘텐츠 내부 축소/숨김은 별도 계약이다. */
--sammo-font-size-small: 12px;
--sammo-font-size-normal: 14px;
--sammo-font-size-emphasis: 16px;
--sammo-font-size-title: 24px;
--sammo-font-sans: Pretendard, 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', sans-serif;
--sammo-color-text: #fff;
--sammo-color-text-muted: rgba(232, 221, 196, 0.7);
@@ -352,7 +352,7 @@ const officerLevelOptions = [
.general-title {
font-weight: 600;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.general-subtitle {
@@ -371,7 +371,7 @@ const officerLevelOptions = [
background: #3498db;
color: #fff;
padding: 4px 9px;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
cursor: pointer;
}
@@ -419,7 +419,7 @@ const officerLevelOptions = [
display: grid;
grid-template-columns: auto minmax(0, 1fr);
color: #ddd;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.field span {
@@ -438,7 +438,7 @@ const officerLevelOptions = [
background: #ddd;
color: #303030;
padding: 6px 10px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 21px;
box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075);
}
@@ -131,20 +131,20 @@ const handleClick = () => {
}
.chief-level {
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.7);
}
.chief-name {
overflow: hidden;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.chief-me {
font-size: 0.65rem;
font-size: var(--sammo-font-size-small);
padding: 2px 6px;
border-radius: 0;
background: rgba(201, 164, 90, 0.2);
@@ -166,7 +166,7 @@ const handleClick = () => {
/* Ref draws no rule between reservation rows and keeps them left aligned. */
border: 0;
text-align: left;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.chief-card.compact .chief-row {
@@ -270,13 +270,18 @@ watch(
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.recruitment-command-form {
box-sizing: border-box;
width: 100%;
min-width: 0;
color: #fff;
background: #1d1d1d;
font: 14px/1.25 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.25 var(--sammo-font-sans);
}
.recruitment-intro {
padding: 6px;
@@ -335,7 +340,7 @@ watch(
}
.crew-group > header strong {
padding: 0 12px;
font-size: 1.3em;
font-size: var(--sammo-font-size-title);
}
.crew-group > header button {
min-height: 34px;
@@ -377,7 +382,7 @@ watch(
}
.crew-name small {
display: block;
font-size: 0.72em;
font-size: var(--sammo-font-size-small);
}
.crew-row small {
display: none;
@@ -509,11 +514,11 @@ watch(
}
.crew-row small {
display: block;
font-size: 0.62em;
font-size: var(--sammo-font-size-small);
line-height: 1;
}
.crew-row .crew-name small {
font-size: 0.72em;
font-size: var(--sammo-font-size-small);
}
.crew-header .attack,
.crew-header .defence,
@@ -521,18 +526,18 @@ watch(
.crew-header .avoid,
.crew-header .cost,
.crew-header .rice {
font-size: 0.78em;
font-size: var(--sammo-font-size-small);
}
.crew-group > header {
grid-template-columns: 7fr 5fr;
}
.crew-group > header button {
font-size: 0.82em;
font-size: var(--sammo-font-size-small);
}
.crew-info {
overflow: hidden;
overflow-wrap: anywhere;
font-size: 0.9em;
font-size: var(--sammo-font-size-small);
}
}
@@ -832,13 +832,18 @@ const clickOutsideMenu = (event: Event) => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.reserved-command-editor {
position: relative;
width: 100%;
min-width: 0;
color: #fff;
background: #1d1d1d;
font: 14px/1.05 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.05 var(--sammo-font-sans);
}
.reserved-command-editor.picker-open {
z-index: 50;
@@ -850,7 +855,7 @@ const clickOutsideMenu = (event: Event) => {
align-items: center;
justify-content: center;
gap: 5px;
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 400;
}
.identity strong {
@@ -1069,7 +1074,7 @@ const clickOutsideMenu = (event: Event) => {
background: #102c35;
box-shadow: 0 3px 8px #000;
color: #aaffff;
font-size: 0.78rem;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 1.35;
text-align: left;
@@ -1091,7 +1096,7 @@ const clickOutsideMenu = (event: Event) => {
visibility: visible;
}
.action-column small {
font-size: 0.72em;
font-size: var(--sammo-font-size-small);
line-height: 1;
}
.reserved-command-editor.mobile .action-column > div.autonomous {
@@ -52,7 +52,7 @@ defineProps<{
box-shadow: 0 3px 12px rgb(0 0 0 / 65%);
color: #f5f5f5;
font-family: var(--sammo-font-sans);
font-size: 12.5px;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 1.45;
text-align: left;
@@ -66,7 +66,7 @@ defineProps<{
.directory-tooltip__content strong {
margin-bottom: 4px;
color: #7fd4ff;
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.directory-tooltip--enabled:hover > .directory-tooltip__content,
.directory-tooltip--enabled:focus > .directory-tooltip__content {
@@ -355,12 +355,17 @@ const sortHelp = (header: Header): string =>
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.directory-table {
width: 1000px;
border-collapse: collapse;
table-layout: auto;
padding: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
word-break: break-all;
}
@@ -414,7 +419,7 @@ const sortHelp = (header: Header): string =>
display: grid;
grid-template-columns: 66px repeat(4, minmax(0, 1fr));
color: inherit;
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 1.2;
}
.general-card + .general-card {
@@ -453,7 +458,7 @@ const sortHelp = (header: Header): string =>
min-height: 32px;
flex-direction: row;
gap: 4px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.name-field,
.nation-field {
@@ -461,11 +466,11 @@ const sortHelp = (header: Header): string =>
}
.field-label {
color: #a8d8bd;
font-size: 10px;
font-size: var(--sammo-font-size-small);
line-height: 1;
}
.identity-field .field-label {
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.general-card-loading {
min-height: 65px;
@@ -699,6 +699,11 @@ watch(
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.target-search-toggle {
margin: 6px 8px;
}
@@ -714,7 +719,7 @@ watch(
flex: 1 1 140px;
width: 140px;
margin: 0;
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
}
.target-search small {
flex-basis: 100%;
@@ -725,7 +730,7 @@ watch(
.command-argument-form {
border: 1px solid rgba(201, 164, 90, 0.35);
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.command-map {
@@ -765,7 +770,7 @@ watch(
.status-key {
border-radius: 2px;
padding: 0 4px;
font-size: 10px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
}
@@ -877,7 +882,7 @@ watch(
.target-state {
grid-column: 3;
color: #aee6a7;
font-size: 10px;
font-size: var(--sammo-font-size-small);
text-align: right;
}
@@ -174,6 +174,11 @@ const commandTitle = (command: CommandAvailability) =>
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.command-form {
display: flex;
flex-direction: column;
@@ -194,7 +199,7 @@ const commandTitle = (command: CommandAvailability) =>
--legacy-button-color: #fff;
min-height: 0;
padding-inline: 4px;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.category-btn.legacy-button--lumen.active {
@@ -220,7 +225,7 @@ const commandTitle = (command: CommandAvailability) =>
align-items: center;
justify-content: center;
text-align: center;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.command-item.ok {
@@ -251,7 +256,7 @@ const commandTitle = (command: CommandAvailability) =>
.command-duration {
display: block;
font-size: 0.875em;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 1.1;
white-space: nowrap;
@@ -260,7 +265,7 @@ const commandTitle = (command: CommandAvailability) =>
.command-cost {
display: block;
color: #f3d58b;
font-size: 0.875em;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 1.1;
overflow-wrap: anywhere;
@@ -465,7 +465,7 @@ const specialText = computed(() => {
background-color: #172a52;
background-image: var(--sammo-texture-blue);
color: #fff;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.general-basic-grid {
@@ -523,7 +523,7 @@ const specialText = computed(() => {
.general-title {
grid-column: 2 / 8;
grid-row: 1;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
line-height: 18px;
}
@@ -75,7 +75,7 @@ const unavailableText: Record<GeneralRecordType, string> = {
color: orange;
background-color: #000;
background-image: var(--sammo-texture-green);
font-size: 1.3em;
font-size: var(--sammo-font-size-title);
font-weight: 500;
}
@@ -48,7 +48,7 @@ const detail = computed(() => (props.autorun ? formatAutorunUserDetail(props.aut
background: #18181b;
box-shadow: 0 4px 12px rgb(0 0 0 / 45%);
color: #f4f4f5;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 1.4;
opacity: 0;
@@ -106,7 +106,7 @@ watch(
background-color: #302016;
background-image: var(--sammo-texture-walnut);
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
line-height: 21px;
}
@@ -169,7 +169,7 @@ watch(
.execution-status > span {
display: block;
white-space: nowrap;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.execution-status:disabled {
opacity: 1;
@@ -287,7 +287,7 @@ const onAction = (action: NonNullable<MainNavigationLinkItem['action']>) => {
width: 125px;
padding: 6px 4px;
font-family: inherit;
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
line-height: 1.5;
text-align: center;
cursor: pointer;
@@ -299,13 +299,13 @@ const onAction = (action: NonNullable<MainNavigationLinkItem['action']>) => {
align-items: center;
justify-content: center;
padding: 2px 1px;
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 1.15;
}
.auto-refresh-trigger strong {
color: #bbb;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 1;
}
@@ -315,7 +315,7 @@ const onAction = (action: NonNullable<MainNavigationLinkItem['action']>) => {
.manual-refresh-trigger {
padding: 0;
font-size: 22px;
font-size: var(--sammo-font-size-title);
line-height: 1;
}
@@ -360,7 +360,7 @@ const onAction = (action: NonNullable<MainNavigationLinkItem['action']>) => {
.bottom-popup li {
break-inside: avoid;
min-width: 125px;
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
}
.bottom-popup :deep(.main-menu-link),
@@ -373,7 +373,7 @@ const onAction = (action: NonNullable<MainNavigationLinkItem['action']>) => {
justify-content: flex-start;
padding: 5px 16px;
font-family: inherit;
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
line-height: 1.5;
text-align: left;
text-decoration: none;
@@ -94,7 +94,7 @@ const lumenClasses = computed(() =>
background-image: var(--sammo-texture-walnut);
color: #fff;
font-family: inherit;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
line-height: 1.5;
text-align: center;
@@ -75,7 +75,7 @@ const emit = defineEmits<{
.main-turn-controls__auto strong {
color: #bbb;
font-size: 0.85em;
font-size: var(--sammo-font-size-small);
}
.main-turn-controls__auto.active strong {
@@ -102,7 +102,7 @@ const { touchstart, touchmove, touchend, touchcancel } = useMapCityTouch(
position: absolute;
box-sizing: border-box;
transform: translate(-50%, -50%);
font-size: 0.65rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.8);
cursor: pointer;
touch-action: manipulation;
@@ -216,7 +216,7 @@ const cityStateStyle = computed(() => ({
.city-base {
position: absolute;
transform: translate(-50%, -50%);
font-size: 0.65rem;
font-size: var(--sammo-font-size-small);
color: #fff;
cursor: pointer;
touch-action: manipulation;
@@ -296,7 +296,7 @@ const cityStateStyle = computed(() => ({
bottom: -10px;
background: rgba(0, 0, 0, 0.5);
white-space: nowrap;
font-size: 0.6rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.9);
}
@@ -816,7 +816,7 @@ const selectCity = (cityId: number) => {
left top,
right top;
background-repeat: no-repeat;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 700;
line-height: 20px;
text-align: center;
@@ -834,7 +834,7 @@ const selectCity = (cityId: number) => {
padding: 5px 8px;
background: #000;
color: #fff;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-weight: 400;
line-height: 18px;
text-align: left;
@@ -880,7 +880,7 @@ const selectCity = (cityId: number) => {
background: #345c85;
color: #fff;
cursor: pointer;
font-size: 17px;
font-size: var(--sammo-font-size-emphasis);
line-height: 1;
pointer-events: auto;
}
@@ -928,7 +928,7 @@ const selectCity = (cityId: number) => {
padding: 3px 7px;
background: #345c85;
color: #fff;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 18px;
cursor: pointer;
text-align: left;
@@ -998,7 +998,7 @@ const selectCity = (cityId: number) => {
padding: 0;
background: rgb(30, 164, 255);
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 15px;
white-space: nowrap;
}
@@ -1013,7 +1013,7 @@ const selectCity = (cityId: number) => {
padding: 1px 4px;
background: rgba(5, 27, 43, 0.92);
color: #d9f3ff;
font-size: 10px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
line-height: 14px;
pointer-events: none;
@@ -273,7 +273,7 @@ const forwardResponse = (messageId: number, response: boolean) => {
<style scoped>
.MessagePanel {
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.MessageInputForm {
@@ -375,7 +375,7 @@ const forwardResponse = (messageId: number, response: boolean) => {
border: 1px solid transparent;
border-radius: 3px;
padding: 2px 6px;
font-size: 11.2px;
font-size: var(--sammo-font-size-small);
line-height: 1.5;
cursor: pointer;
}
@@ -329,7 +329,7 @@ onBeforeUnmount(() => {
min-height: 64px;
outline: 1px solid gray;
color: #fff;
font-size: 12.5px;
font-size: var(--sammo-font-size-small);
word-break: break-all;
}
@@ -414,7 +414,7 @@ button.msg-target {
}
.msg-time {
font-size: 0.75em;
font-size: var(--sammo-font-size-small);
font-weight: 400;
}
@@ -429,7 +429,7 @@ button.msg-target {
background: transparent;
padding: 2px 4px;
color: #ffc107;
font-size: 8px;
font-size: var(--sammo-font-size-small);
cursor: pointer;
}
@@ -479,7 +479,7 @@ button.msg-target {
.message-response .legacy-button {
min-width: 42px;
padding: 2px 8px;
font-size: 12.5px;
font-size: var(--sammo-font-size-small);
cursor: pointer;
}
@@ -42,7 +42,7 @@ const formattedLogs = computed(() =>
min-width: 0;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.35;
}
@@ -17,7 +17,7 @@ defineProps<{
.record-panel {
min-width: 0;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
line-height: 21px;
}
@@ -67,7 +67,7 @@ const props = defineProps<{
}
.tag {
font-size: 0.65rem;
font-size: var(--sammo-font-size-small);
padding: 2px 4px;
border: 1px solid rgba(201, 164, 90, 0.4);
}
@@ -76,7 +76,7 @@ const props = defineProps<{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
gap: 4px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.empty {
@@ -203,7 +203,7 @@ onBeforeUnmount(() => document.removeEventListener('pointerdown', handleDocument
border-radius: 2px;
color: #111;
background: #fff;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
line-height: 1;
}
@@ -212,7 +212,7 @@ onBeforeUnmount(() => document.removeEventListener('pointerdown', handleDocument
margin: 0;
padding: 6px 8px;
color: #bbb;
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.permission-multiselect-help {
border-top: 1px solid #454545;
@@ -216,6 +216,11 @@ onBeforeUnmount(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.personnel-picker-backdrop {
position: fixed;
z-index: 2050;
@@ -237,7 +242,7 @@ onBeforeUnmount(() => {
border: 1px solid #78653d;
border-radius: 14px;
box-shadow: 0 24px 70px rgb(0 0 0 / 78%);
font: 14px/1.45 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.45 var(--sammo-font-sans);
}
.personnel-picker-header {
display: flex;
@@ -251,13 +256,13 @@ onBeforeUnmount(() => {
.personnel-picker-eyebrow {
margin: 0 0 3px;
color: #cbb171;
font-size: 11px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
letter-spacing: 0.13em;
}
.personnel-picker h2 {
margin: 0;
font-size: 22px;
font-size: var(--sammo-font-size-title);
line-height: 1.25;
}
.personnel-picker-header p:last-child {
@@ -273,7 +278,7 @@ onBeforeUnmount(() => {
padding: 0;
color: #ddd7ca;
background: rgb(0 0 0 / 28%);
font-size: 24px;
font-size: var(--sammo-font-size-title);
line-height: 1;
cursor: pointer;
}
@@ -385,7 +390,7 @@ onBeforeUnmount(() => {
}
.personnel-picker-card-title strong,
.vacancy-card strong {
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
}
.personnel-picker-card-title small,
.vacancy-card small {
@@ -397,7 +402,7 @@ onBeforeUnmount(() => {
color: #13150f;
background: #d5bb6f;
border-radius: 999px;
font-size: 10px;
font-size: var(--sammo-font-size-small);
font-weight: 800;
}
.personnel-picker-badges {
@@ -412,7 +417,7 @@ onBeforeUnmount(() => {
background: #32352d;
border: 1px solid #555948;
border-radius: 999px;
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.personnel-picker-stats {
display: grid;
@@ -431,7 +436,7 @@ onBeforeUnmount(() => {
.personnel-picker-stats small,
.personnel-picker-details small {
color: #aaa99f;
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.personnel-picker-stats strong {
color: #f1d47e;
@@ -418,12 +418,12 @@ const mobilePairs = computed(() => {
left: 50%;
transform: translate(-50%, -50%);
color: #ffd25e;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.tournament-bracket > p {
margin: 8px 0 0;
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
@media (max-width: 800px) {
.desktop-bracket {
@@ -434,7 +434,7 @@ const mobilePairs = computed(() => {
}
.tournament-bracket > p {
padding: 0 8px;
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
}
@@ -511,13 +511,13 @@ const mobilePairs = computed(() => {
}
}
.bracket-info-identity :deep(.general-identity-name) {
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
line-height: 18px;
}
.bracket-info-identity :deep(.general-identity-details) {
display: flex;
flex-direction: column;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 18px;
text-align: left;
overflow-wrap: anywhere;
@@ -92,7 +92,7 @@ const pointsOf = (participant: StandingParticipant): number => (participant.win
padding: 3px;
background: #000;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
text-align: center;
}
@@ -129,7 +129,7 @@ const pointsOf = (participant: StandingParticipant): number => (participant.win
min-width: 0;
flex-direction: column;
color: #d8cfca;
font-size: 12px;
font-size: var(--sammo-font-size-small);
font-variant-numeric: tabular-nums;
line-height: 14px;
white-space: nowrap;
@@ -156,7 +156,7 @@ const pointsOf = (participant: StandingParticipant): number => (participant.win
grid-template-columns: 28px minmax(0, 1fr);
}
.standing-summary {
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 15px;
}
}
@@ -62,7 +62,7 @@ defineProps<{
.tournament-page-title {
min-width: 0;
overflow: hidden;
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 400;
text-overflow: ellipsis;
white-space: nowrap;
@@ -76,7 +76,7 @@ defineProps<{
button {
--legacy-button-height: 44px;
margin: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.tournament-page-tabs button {
min-width: 72px;
@@ -100,7 +100,7 @@ button:focus-visible {
padding-inline: 2px;
}
.tournament-page-title {
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.tournament-page-actions,
.tournament-page-tabs {
@@ -220,7 +220,7 @@ onBeforeUnmount(() => {
.game-feedback-copy strong {
color: #e5c982;
font-size: 0.78rem;
font-size: var(--sammo-font-size-small);
}
.game-feedback-close {
@@ -231,7 +231,7 @@ onBeforeUnmount(() => {
background: transparent;
border: 0;
cursor: pointer;
font-size: 1.25rem;
font-size: var(--sammo-font-size-title);
line-height: 1;
}
@@ -276,7 +276,7 @@ onBeforeUnmount(() => {
.game-dialog-panel h2 {
color: #e5c982;
font-size: 1rem;
font-size: var(--sammo-font-size-emphasis);
}
.game-dialog-panel p {
@@ -30,7 +30,7 @@ const { reconnecting } = useGameServerConnectionRecovery();
border-top: 0;
background: #fff3bf;
color: #3d3100;
font: 13px/1.45 var(--sammo-font-sans);
font: var(--sammo-font-size-small)/1.45 var(--sammo-font-sans);
text-align: center;
box-shadow: 0 2px 6px rgb(0 0 0 / 25%);
}
@@ -102,7 +102,7 @@ const experiencePercent = computed(() =>
max-width: 100%;
overflow: hidden;
border-top: 1px solid #666;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.stat-grid {
@@ -427,7 +427,7 @@ onBeforeUnmount(() => editor.value?.destroy());
}
.legacy-html-editor__image-toolbar span {
padding: 0 4px;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.legacy-html-editor__image-toolbar button {
border: 1px solid transparent;
@@ -453,7 +453,7 @@ onBeforeUnmount(() => editor.value?.destroy());
align-items: center;
gap: 0;
color: inherit;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.legacy-html-editor__select select {
min-width: 0;
@@ -49,14 +49,14 @@ withDefaults(defineProps<Props>(), {
.panel-title {
margin: 0;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 700;
}
.panel-subtitle {
margin: 1px 0 0;
color: #ccc;
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.panel-actions {
@@ -68,6 +68,6 @@ withDefaults(defineProps<Props>(), {
.panel-body {
padding: 6px;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
</style>
@@ -112,7 +112,7 @@ watch(
box-shadow: 0 3px 12px rgb(0 0 0 / 65%);
color: #f5f5f5;
font-family: var(--sammo-font-sans);
font-size: 12.5px;
font-size: var(--sammo-font-size-small);
line-height: 1.45;
text-align: left;
}
@@ -147,7 +147,7 @@ watch(
.rich-tooltip-content__title {
margin-bottom: 4px;
color: #7fd4ff;
font-size: 13px;
font-size: var(--sammo-font-size-small);
font-weight: 700;
}
+2 -2
View File
@@ -517,7 +517,7 @@ onMounted(() => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 21px;
}
.legacy-auction-page.resource-page {
@@ -549,7 +549,7 @@ onMounted(() => {
}
.top-back-bar h1 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 32px;
text-align: center;
@@ -405,7 +405,7 @@ onMounted(() => {
}
:deep(.panel-title) {
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 500;
}
:deep(.panel-header) {
@@ -443,7 +443,7 @@ onMounted(() => {
}
.battle-top h1 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
line-height: 32px;
text-align: center;
@@ -1368,7 +1368,7 @@ const shouldShowUI = computed(() => !loading.value && !!options.value);
padding: 2px 6px;
background: #444;
color: #aaa;
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.independence-notice p {
@@ -1403,7 +1403,7 @@ button {
border-radius: 5.25px;
padding: 5.25px 10.5px;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 700;
line-height: 21px;
vertical-align: middle;
@@ -1465,12 +1465,12 @@ button:disabled {
.error {
color: #f5b7b1;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
.status {
color: #f5d08a;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
.settings-grid {
@@ -1484,7 +1484,7 @@ button:disabled {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
color: #ddd;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.unit-after {
@@ -1507,7 +1507,7 @@ button:disabled {
background: #ddd;
color: #303030;
padding: 5.25px 10.5px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 21px;
box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075);
transition:
@@ -1571,7 +1571,7 @@ button:disabled {
border: 1px solid #111;
background: #444;
color: #ddd;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
white-space: nowrap;
}
@@ -1595,7 +1595,7 @@ button:disabled {
padding: 6px 14px;
border-radius: 5px;
background: #444;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.defender-toolbar > div {
@@ -1653,7 +1653,7 @@ button:disabled {
.log-body {
min-height: 0;
line-height: 1.5;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
.modal-backdrop {
@@ -1686,7 +1686,7 @@ button:disabled {
.modal h3 {
margin: 0;
font-size: 1rem;
font-size: var(--sammo-font-size-emphasis);
}
.modal-body {
@@ -1696,7 +1696,7 @@ button:disabled {
}
.hint {
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.7);
}
@@ -179,6 +179,11 @@ watch(viewMode, () => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
:global(body:has(.legacy-ranking-page)) {
min-width: 500px;
overflow-x: hidden;
@@ -191,7 +196,7 @@ watch(viewMode, () => {
background-color: transparent;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -251,7 +256,7 @@ watch(viewMode, () => {
}
.legacy-banner {
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.legacy-banner a {
@@ -278,7 +283,7 @@ watch(viewMode, () => {
margin: 0;
border-bottom: 1px solid gray;
padding: 2px;
font-size: calc(19px + 0.784615vw);
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 1.2;
text-align: center;
@@ -377,7 +382,7 @@ watch(viewMode, () => {
}
.rankType {
font-size: 28px;
font-size: var(--sammo-font-size-title);
}
}
</style>
+9 -4
View File
@@ -281,11 +281,16 @@ const placeBet = async (target: TournamentBracketSlot) => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.betting-page {
--bet-font-title: 24px;
--bet-font-large: 16px;
--bet-font-normal: 14px;
--bet-font-small: 12px;
--bet-font-title: var(--sammo-font-size-title);
--bet-font-large: var(--sammo-font-size-emphasis);
--bet-font-normal: var(--sammo-font-size-normal);
--bet-font-small: var(--sammo-font-size-small);
width: 100%;
max-width: 1200px;
min-width: 0;
+4 -4
View File
@@ -271,7 +271,7 @@ onMounted(() => {
padding: 8px;
color: #000;
background: #fff;
font: 16px/normal var(--sammo-font-sans);
font: var(--sammo-font-size-emphasis)/normal var(--sammo-font-sans);
}
.legacy-board-page {
@@ -280,7 +280,7 @@ onMounted(() => {
color: #fff;
background: transparent;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -309,7 +309,7 @@ onMounted(() => {
.top-back-bar h1 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 32px;
text-align: center;
@@ -440,7 +440,7 @@ onMounted(() => {
.date {
width: 83.333px;
flex: 0 0 auto;
font-size: 0.9em;
font-size: var(--sammo-font-size-small);
}
.article-body {
+13 -13
View File
@@ -511,7 +511,7 @@ const repeatTurns = async (amount: number) => {
padding: 8px;
display: grid;
gap: 6px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
margin-top: 12px;
}
@@ -522,7 +522,7 @@ const repeatTurns = async (amount: number) => {
.command-selected .meta {
display: flex;
gap: 8px;
font-size: 0.7rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.6);
}
@@ -535,7 +535,7 @@ const repeatTurns = async (amount: number) => {
.turn-actions button {
border: 1px solid rgba(201, 164, 90, 0.3);
padding: 4px 8px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
background: rgba(12, 12, 12, 0.6);
color: inherit;
cursor: pointer;
@@ -555,7 +555,7 @@ const repeatTurns = async (amount: number) => {
flex-wrap: wrap;
justify-content: space-between;
gap: 8px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.turn-info {
@@ -584,7 +584,7 @@ const repeatTurns = async (amount: number) => {
.turn-buttons button {
border: 1px solid rgba(201, 164, 90, 0.3);
padding: 4px 8px;
font-size: 0.7rem;
font-size: var(--sammo-font-size-small);
background: rgba(12, 12, 12, 0.6);
color: inherit;
cursor: pointer;
@@ -655,7 +655,7 @@ const repeatTurns = async (amount: number) => {
.chief-page {
margin: 0 auto;
color: #fff;
font: 14px/21px var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/21px var(--sammo-font-sans);
}
.chief-top {
height: 32px;
@@ -665,7 +665,7 @@ const repeatTurns = async (amount: number) => {
}
.chief-top h1 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
line-height: 32px;
text-align: center;
@@ -731,7 +731,7 @@ const repeatTurns = async (amount: number) => {
justify-content: center;
padding: 0;
background-color: transparent;
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
line-height: 14.7px;
}
.chief-grid-row :deep(.chief-title) {
@@ -742,7 +742,7 @@ const repeatTurns = async (amount: number) => {
}
.chief-grid-row :deep(.chief-level),
.chief-grid-row :deep(.chief-name) {
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 400;
line-height: 14.7px;
color: inherit;
@@ -758,7 +758,7 @@ const repeatTurns = async (amount: number) => {
gap: 0;
padding: 0;
border: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 14.7px;
color: #fff;
text-align: center;
@@ -884,7 +884,7 @@ const repeatTurns = async (amount: number) => {
justify-content: center;
padding: 0;
background-color: transparent;
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
line-height: 14.7px;
}
.mobile-readonly :deep(.chief-title) {
@@ -895,7 +895,7 @@ const repeatTurns = async (amount: number) => {
}
.mobile-readonly :deep(.chief-level),
.mobile-readonly :deep(.chief-name) {
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 400;
line-height: 14.7px;
color: inherit;
@@ -907,7 +907,7 @@ const repeatTurns = async (amount: number) => {
height: 30px;
grid-template-columns: 43.33px 216.67px;
padding: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 14.7px;
color: #fff;
}
@@ -340,7 +340,7 @@ const commandBrief = (command: ReservedCommand): string =>
width: 1000px;
margin: 0 auto;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.legacy-table {
@@ -371,7 +371,7 @@ const commandBrief = (command: ReservedCommand): string =>
background: #6b6b6b;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 13.3333px;
font-size: var(--sammo-font-size-normal);
}
.selector {
transform: translateY(-2px);
@@ -450,7 +450,7 @@ const commandBrief = (command: ReservedCommand): string =>
object-fit: fill;
}
.turns--reserved {
font-size: x-small;
font-size: var(--sammo-font-size-small);
}
.turn-line {
display: block;
@@ -466,7 +466,7 @@ const commandBrief = (command: ReservedCommand): string =>
}
.back-link {
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.legacy-banner a {
color: #fff;
@@ -542,7 +542,7 @@ const commandBrief = (command: ReservedCommand): string =>
.generals tbody td[data-label]::before {
content: attr(data-label);
margin-right: 3px;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 1;
color: #bbb;
}
@@ -659,7 +659,7 @@ onBeforeUnmount(() => {
color: #fff;
overflow-x: clip;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -699,7 +699,7 @@ onBeforeUnmount(() => {
.panel-header h2 {
margin: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 500;
}
@@ -809,7 +809,7 @@ onBeforeUnmount(() => {
.letter-header h3 {
margin: 0;
font-size: 28px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
}
@@ -817,7 +817,7 @@ onBeforeUnmount(() => {
position: absolute;
right: 10px;
bottom: 3px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.letter-text {
@@ -308,7 +308,7 @@ onMounted(loadDetail);
margin: 8px auto 0;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 16px;
font-size: var(--sammo-font-size-emphasis);
line-height: normal;
}
@@ -351,7 +351,7 @@ onMounted(loadDetail);
}
.large-text {
font-size: x-large;
font-size: var(--sammo-font-size-title);
}
.centered {
@@ -387,7 +387,7 @@ onMounted(loadDetail);
background: buttonface;
color: buttontext;
font-family: var(--sammo-font-sans);
font-size: 13.3333px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
line-height: normal;
cursor: default;
@@ -411,6 +411,6 @@ onMounted(loadDetail);
}
.banner {
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
</style>
@@ -186,7 +186,7 @@ watch(selectedSource, loadDynasty);
width: 1000px;
background-color: transparent;
border-collapse: collapse;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
word-break: break-all;
}
@@ -241,7 +241,7 @@ watch(selectedSource, loadDynasty);
}
.large-text {
font-size: x-large;
font-size: var(--sammo-font-size-title);
}
.label-column {
@@ -261,7 +261,7 @@ watch(selectedSource, loadDynasty);
background: buttonface;
color: buttontext;
font-family: var(--sammo-font-sans);
font-size: 13.3333px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
line-height: normal;
cursor: default;
@@ -284,6 +284,6 @@ watch(selectedSource, loadDynasty);
}
.banner {
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
</style>
@@ -133,10 +133,15 @@ onMounted(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.directory-page {
width: 1000px;
margin: 0 auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.directory-table {
@@ -144,7 +149,7 @@ onMounted(() => {
border-collapse: collapse;
table-layout: auto;
padding: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
word-break: break-all;
}
.directory-table td {
@@ -158,7 +163,7 @@ onMounted(() => {
}
.legacy-button {
padding: 5px 10px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.directory-error {
width: 998px;
@@ -171,7 +171,7 @@ onMounted(async () => {
.global-page {
width: 1000px;
margin: 0 auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
background-color: transparent;
background-image: none;
}
@@ -188,7 +188,7 @@ onMounted(async () => {
text-align: center;
}
.legacy-title strong {
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
line-height: 32px;
}
@@ -206,7 +206,7 @@ onMounted(async () => {
background-image: var(--sammo-texture-walnut);
}
.section h2 {
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 400;
text-align: center;
margin: 0;
@@ -193,6 +193,11 @@ onMounted(loadOptions);
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
/* Ref's document does not clip; the page column stays 500px wide instead. */
:global(body:has(.legacy-hall-page)) {
min-width: 500px;
@@ -204,7 +209,7 @@ onMounted(loadOptions);
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.legacy-hall-title,
@@ -274,7 +279,7 @@ onMounted(loadOptions);
}
.legacy-banner {
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.legacy-banner a {
@@ -296,7 +301,7 @@ onMounted(loadOptions);
margin: 0;
border-bottom: 1px solid gray;
padding: 2px;
font-size: calc(19px + 0.784615vw);
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 1.2;
text-align: center;
@@ -380,7 +385,7 @@ onMounted(loadOptions);
}
.rankType {
font-size: 28px;
font-size: var(--sammo-font-size-title);
}
}
</style>
+8 -3
View File
@@ -787,6 +787,11 @@ onMounted(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.top-back-bar {
width: min(100%, 1000px);
height: 32px;
@@ -816,7 +821,7 @@ onMounted(() => {
padding: 0 7px;
color: #fff;
min-height: 1597px;
font: 14px/21px var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/21px var(--sammo-font-sans);
}
.inherit-page.legacy-bg0 {
@@ -892,7 +897,7 @@ onMounted(() => {
}
.section-title {
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
text-align: center;
margin: 0 -8px;
@@ -969,7 +974,7 @@ onMounted(() => {
.stat-layout strong {
text-align: left;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.owner-result {
+36 -31
View File
@@ -1276,6 +1276,11 @@ onUnmounted(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.join-page {
min-height: 100vh;
padding: 24px;
@@ -1298,12 +1303,12 @@ onUnmounted(() => {
}
.join-title {
font-size: 1.6rem;
font-size: var(--sammo-font-size-title);
font-weight: 600;
}
.join-subtitle {
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
color: rgba(232, 221, 196, 0.7);
}
@@ -1315,14 +1320,14 @@ onUnmounted(() => {
.join-tabs button {
border: 1px solid rgba(201, 164, 90, 0.4);
padding: 6px 10px;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
}
.simulator-link {
border: 1px solid rgba(112, 170, 141, 0.55);
padding: 6px 10px;
color: #bfe2cd;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
text-decoration: none;
}
@@ -1371,7 +1376,7 @@ onUnmounted(() => {
.nation-message {
margin: 0;
padding: 8px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.7);
}
@@ -1400,12 +1405,12 @@ onUnmounted(() => {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.primary-field > span:first-child {
color: #f0d99e;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
font-weight: 700;
}
@@ -1418,7 +1423,7 @@ onUnmounted(() => {
.primary-field > .form-input {
min-height: 36px;
font-size: 1rem;
font-size: var(--sammo-font-size-emphasis);
}
.stat-actions {
@@ -1433,7 +1438,7 @@ onUnmounted(() => {
height: 40px;
min-height: 40px;
padding: 8px 14px;
font-size: 0.875rem;
font-size: var(--sammo-font-size-normal);
line-height: 1.2;
}
@@ -1449,7 +1454,7 @@ onUnmounted(() => {
.stat-summary {
margin-top: 10px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.7);
}
@@ -1467,7 +1472,7 @@ onUnmounted(() => {
.form-actions button {
border: 1px solid rgba(201, 164, 90, 0.4);
padding: 6px 12px;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
}
.form-actions .primary-action {
@@ -1520,13 +1525,13 @@ onUnmounted(() => {
.advanced-title small {
color: #ccc;
font-size: 0.7rem;
font-size: var(--sammo-font-size-small);
font-weight: 400;
}
.advanced-point-summary {
color: #ead8ac;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
white-space: nowrap;
}
@@ -1546,7 +1551,7 @@ onUnmounted(() => {
border: 0;
border-right: 1px solid rgba(201, 164, 90, 0.25);
color: rgba(232, 221, 196, 0.68);
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
}
.context-tabs button:last-child {
@@ -1583,7 +1588,7 @@ onUnmounted(() => {
gap: 12px;
margin-bottom: 8px;
color: rgba(232, 221, 196, 0.75);
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.general-list-head label {
@@ -1602,7 +1607,7 @@ onUnmounted(() => {
.context-general-table {
width: 100%;
border-collapse: collapse;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.context-general-table th,
@@ -1629,7 +1634,7 @@ onUnmounted(() => {
padding: 1px 3px;
background: rgba(111, 74, 141, 0.8);
color: #fff;
font-size: 0.6rem;
font-size: var(--sammo-font-size-small);
}
.context-error,
@@ -1637,7 +1642,7 @@ onUnmounted(() => {
padding: 12px;
color: rgba(240, 150, 150, 0.9);
text-align: center;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.sr-only {
@@ -1662,7 +1667,7 @@ onUnmounted(() => {
flex-wrap: wrap;
justify-content: space-between;
gap: 8px;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.85);
}
@@ -1678,7 +1683,7 @@ onUnmounted(() => {
}
.bonus-title {
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.8);
}
@@ -1690,7 +1695,7 @@ onUnmounted(() => {
.inherit-errors {
color: rgba(240, 150, 150, 0.9);
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.form-actions .ghost,
@@ -1708,7 +1713,7 @@ onUnmounted(() => {
.npc-token-status {
min-height: 22px;
text-align: center;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.npc-token-expired {
@@ -1738,14 +1743,14 @@ onUnmounted(() => {
height: 25px;
overflow: hidden;
border: 1px solid rgba(201, 164, 90, 0.3);
font-size: 1rem;
font-size: var(--sammo-font-size-emphasis);
line-height: 23px;
text-overflow: ellipsis;
white-space: nowrap;
}
.npc-card-name--long {
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.npc-card-image {
@@ -1755,7 +1760,7 @@ onUnmounted(() => {
.npc-card p {
min-height: 78px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
line-height: 1.3;
}
@@ -1789,14 +1794,14 @@ onUnmounted(() => {
width: 100%;
border: 1px solid rgba(201, 164, 90, 0.4);
padding: 4px 8px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.npc-keep {
display: block;
padding-left: 15px;
text-indent: -15px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.npc-keep input {
@@ -1830,7 +1835,7 @@ onUnmounted(() => {
margin-bottom: 8px;
color: rgba(240, 150, 150, 0.9);
text-align: center;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.npc-general-list-wrap {
@@ -1843,7 +1848,7 @@ onUnmounted(() => {
width: 970px;
border-collapse: collapse;
table-layout: fixed;
font-size: 12px;
font-size: var(--sammo-font-size-small);
word-break: break-all;
}
@@ -1872,7 +1877,7 @@ onUnmounted(() => {
}
.npc-general-name small {
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
#btn-print-more-generals {
@@ -1883,7 +1888,7 @@ onUnmounted(() => {
.muted {
color: rgba(232, 221, 196, 0.6);
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.ghost {
+1 -1
View File
@@ -28,7 +28,7 @@ onMounted(() => {
h1 {
margin: 0 0 12px;
font-size: 20px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
}
+9 -9
View File
@@ -788,7 +788,7 @@ button {
.game-version-dialog__commit code {
overflow-wrap: anywhere;
color: #d7d7d7;
font-size: 0.85em;
font-size: var(--sammo-font-size-small);
}
/*
@@ -820,7 +820,7 @@ button {
.front-status-error {
color: #ff8a80;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
.legacy-game-info {
@@ -834,7 +834,7 @@ button {
border-top: 1px solid #666;
background: #302016 var(--sammo-texture-walnut);
color: #fff;
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 17px;
text-align: center;
}
@@ -851,7 +851,7 @@ button {
.warning {
color: #f5d08a;
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
}
.survey-notice {
@@ -866,7 +866,7 @@ button {
background: rgba(32, 28, 16, 0.96);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -883,7 +883,7 @@ button {
.survey-notice-title button {
padding: 0 4px;
cursor: pointer;
font-size: 20px;
font-size: var(--sammo-font-size-title);
line-height: 1;
}
@@ -912,7 +912,7 @@ button {
background: rgba(12, 12, 12, 0.96);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -933,7 +933,7 @@ button {
.private-message-notice-close {
padding: 0 4px;
cursor: pointer;
font-size: 20px;
font-size: var(--sammo-font-size-title);
line-height: 1;
}
@@ -1158,7 +1158,7 @@ button {
}
.placeholder {
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
color: rgba(232, 221, 196, 0.7);
display: flex;
flex-direction: column;
+3 -3
View File
@@ -629,7 +629,7 @@ onMounted(() => {
background-color: transparent;
background-image: var(--sammo-texture-walnut);
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.my-page-mobile-scroll-spacer {
@@ -645,7 +645,7 @@ onMounted(() => {
gap: 0 4px;
border: 1px solid #666;
background: transparent;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.title-row > span {
flex-basis: 100%;
@@ -714,7 +714,7 @@ button:disabled {
border-bottom: 1px solid #666;
background-color: #14241b;
background-image: var(--sammo-texture-green);
font-size: 1.25em;
font-size: var(--sammo-font-size-emphasis);
font-weight: 500;
}
.section-title {
+12 -7
View File
@@ -314,6 +314,11 @@ onBeforeUnmount(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.legacy-page {
box-sizing: border-box;
width: 100%;
@@ -325,7 +330,7 @@ onBeforeUnmount(() => {
background-color: transparent;
background-image: var(--sammo-texture-walnut);
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.title-row {
@@ -367,7 +372,7 @@ onBeforeUnmount(() => {
background-color: #14241b;
background-image: var(--sammo-texture-green);
color: skyblue;
font-size: 1.25em;
font-size: var(--sammo-font-size-emphasis);
font-weight: 500;
}
.settings-content {
@@ -440,7 +445,7 @@ onBeforeUnmount(() => {
padding: 12px;
background: #171717 var(--sammo-texture-walnut);
color: #fff;
font: 14px/1.3 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.3 var(--sammo-font-sans);
}
.mobile-layout-dialog::backdrop {
background: rgb(0 0 0 / 72%);
@@ -461,7 +466,7 @@ onBeforeUnmount(() => {
}
.mobile-layout-dialog__header h2 {
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.mobile-layout-dialog__header form,
.mobile-layout-dialog__actions form {
@@ -470,7 +475,7 @@ onBeforeUnmount(() => {
.mobile-layout-dialog__header button {
min-width: 32px;
min-height: 32px;
font-size: 20px;
font-size: var(--sammo-font-size-title);
}
.mobile-layout-list {
display: grid;
@@ -494,7 +499,7 @@ onBeforeUnmount(() => {
.mobile-layout-handle {
color: #aaa;
text-align: center;
font-size: 20px;
font-size: var(--sammo-font-size-title);
}
.mobile-layout-label {
min-width: 0;
@@ -508,7 +513,7 @@ onBeforeUnmount(() => {
margin-right: 4px;
border: 1px solid #7186a7;
border-radius: 50%;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.mobile-layout-move-buttons {
gap: 4px;
@@ -457,7 +457,7 @@ onMounted(() => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.5;
}
@@ -474,7 +474,7 @@ onMounted(() => {
.legacy-top-bar h1 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 32px;
text-align: center;
@@ -740,7 +740,7 @@ onMounted(async () => {
.nation-cities-page {
width: 1000px;
margin: 0 auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.legacy-table {
@@ -836,7 +836,7 @@ onMounted(async () => {
}
.secret-commands {
text-align: left !important;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.bonus {
color: cyan;
@@ -1015,12 +1015,17 @@ onBeforeUnmount(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.general-page {
width: 1000px;
min-width: 1000px;
height: 100vh;
margin: 0 auto;
font: 14px/21px var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/21px var(--sammo-font-sans);
color: #fff;
background-color: transparent;
}
@@ -1035,10 +1040,10 @@ onBeforeUnmount(() => {
justify-content: center;
background-color: transparent;
background-image: var(--sammo-texture-walnut);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.top-bar strong {
font-size: 22px;
font-size: var(--sammo-font-size-title);
font-weight: 400;
}
.left-actions,
@@ -1061,7 +1066,7 @@ onBeforeUnmount(() => {
align-items: center;
justify-content: center;
padding: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
text-decoration: none;
}
.nation-button {
@@ -1147,7 +1152,7 @@ table {
table-layout: fixed;
background: #293033;
color: #f5f5f5;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: normal;
cursor: default;
}
@@ -1198,7 +1203,7 @@ th {
}
.sort-indicator {
color: #8dd4ff;
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.filter-head th {
position: relative;
@@ -1234,7 +1239,7 @@ th {
.floating-filter > input::placeholder,
.filter-popup input::placeholder {
color: #8f999d;
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.filter-menu-button {
display: inline-flex;
@@ -1292,7 +1297,7 @@ th {
background: #2d3436;
box-shadow: 0 2px 6px rgb(0 0 0 / 45%);
color: #f5f5f5;
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 18px;
text-align: left;
white-space: normal;
@@ -1311,7 +1316,7 @@ th {
border-radius: 0;
background: #252a2c;
color: #fff;
font: 12px/18px var(--sammo-font-sans);
font: var(--sammo-font-size-small)/18px var(--sammo-font-sans);
}
.filter-condition select {
cursor: pointer;
@@ -138,7 +138,7 @@ onMounted(async () => {
.legacy-info-page {
width: 1000px;
margin: 0 auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.legacy-table {
width: 100%;
@@ -422,17 +422,22 @@ onBeforeUnmount(() => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.directory-page {
width: 1000px;
margin: 0 auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
.directory-table {
width: 1000px;
border-collapse: collapse;
table-layout: auto;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
word-break: break-all;
background-color: transparent;
}
@@ -456,7 +461,7 @@ onBeforeUnmount(() => {
padding: 5.25px 10.5px;
background-color: rgb(55 90 127);
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 700;
line-height: 21px;
cursor: pointer;
@@ -604,12 +604,17 @@ onMounted(() => void loadPersonnel());
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.legacy-office {
width: 1000px;
min-height: 100vh;
margin: 0 auto;
color: #fff;
font: 14px/1.3 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.3 var(--sammo-font-sans);
}
.legacy-table {
width: 1000px;
@@ -672,7 +677,7 @@ select {
.nation-heading {
height: 32px;
text-align: center;
font-size: 20px;
font-size: var(--sammo-font-size-title);
}
.chief-status {
margin-bottom: 0;
@@ -724,7 +729,7 @@ select {
color: #fff;
background: #24472e;
text-align: center;
font-size: 17px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 700;
}
.general-icon {
@@ -742,14 +747,14 @@ select {
}
.chief-entry-copy strong {
overflow: hidden;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
text-overflow: ellipsis;
white-space: nowrap;
}
.chief-entry-copy small {
overflow: hidden;
color: #c8c5bc;
font-size: 11px;
font-size: var(--sammo-font-size-small);
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -765,7 +770,7 @@ select {
grid-area: action;
color: #e7b64c;
text-align: center;
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.award-label {
text-align: center;
@@ -823,7 +828,7 @@ select {
.city-header td {
height: 29px;
text-align: center;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.city-header-compact {
display: none;
@@ -834,20 +839,20 @@ select {
.region-heading {
height: 29px;
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.city-identity {
display: table-cell;
width: 158px;
text-align: center;
font-size: 16.8px;
font-size: var(--sammo-font-size-emphasis);
}
.city-identity small,
.city-identity strong {
display: block;
}
.city-identity small {
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.city-officer-cell {
padding: 5px !important;
@@ -871,7 +876,7 @@ select {
}
.city-officer-copy small {
color: #c9c6bd;
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.city-change-button {
min-width: 70px;
@@ -919,7 +924,7 @@ select {
margin-bottom: 10px;
}
.nation-heading {
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.chief-entry-cell {
padding: 4px !important;
@@ -933,7 +938,7 @@ select {
}
.chief-entry-role {
padding: 5px 2px;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.general-icon {
width: 48px;
@@ -944,19 +949,19 @@ select {
font-size: 15px;
}
.chief-entry-copy small {
font-size: 9px;
font-size: var(--sammo-font-size-small);
}
.personnel-change-button {
min-height: 30px;
padding: 3px 5px;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 18px;
}
.personnel-lock-label {
font-size: 10px;
}
.award-label {
font-size: 11px;
font-size: var(--sammo-font-size-small);
white-space: nowrap;
}
.award-label-full {
@@ -971,7 +976,7 @@ select {
}
.city-header td {
height: 26px;
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.city-header-full {
display: none;
@@ -981,14 +986,14 @@ select {
}
.region-heading {
height: 25px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.city-identity {
width: 16%;
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.city-identity small {
font-size: 8px;
font-size: var(--sammo-font-size-small);
}
.city-officer-cell {
padding: 4px !important;
@@ -998,17 +1003,17 @@ select {
gap: 4px;
}
.city-officer-copy strong {
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.city-officer-copy small {
font-size: 9px;
font-size: var(--sammo-font-size-small);
}
.city-change-button {
width: 100%;
min-width: 0;
}
.legend {
font-size: 10px;
font-size: var(--sammo-font-size-small);
}
.kick-label {
width: auto;
@@ -368,7 +368,7 @@ onMounted(load);
.secret-page {
width: auto;
margin: 0;
font: 14px var(--sammo-font-sans);
font: var(--sammo-font-size-normal) var(--sammo-font-sans);
color: #fff;
}
.layout {
@@ -381,7 +381,7 @@ th,
.state {
border: 1px solid gray;
padding: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
word-break: break-all;
text-align: center;
font-weight: 400;
@@ -455,7 +455,7 @@ th,
}
.turns {
text-align: left;
font-size: 11px;
font-size: var(--sammo-font-size-small);
}
.bonus {
color: cyan;
@@ -483,13 +483,13 @@ th,
}
.list th,
.list td {
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.list tbody tr {
height: auto;
}
.list tbody td {
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.list :is(th, td):nth-child(1),
.list :is(th, td):nth-child(2) {
@@ -592,7 +592,7 @@ th,
min-width: 0;
padding: 0 2px;
border: 0;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 18.2px;
overflow-wrap: anywhere;
align-content: center;
@@ -600,7 +600,7 @@ th,
.list tbody td[data-label]::before {
content: attr(data-label);
margin-right: 3px;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 1;
color: #bbb;
}
@@ -609,13 +609,13 @@ th,
}
.general-level {
margin-left: 3px;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 1;
color: #bbb;
white-space: nowrap;
}
.list tbody .turns {
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 1.3;
border-left: 1px solid gray;
padding-inline: 4px;
@@ -474,7 +474,7 @@ onMounted(() => void loadStratFinan());
margin: 0 auto;
color: #fff;
background: var(--sammo-texture-walnut);
font: 14px/1.3 var(--sammo-font-sans);
font: var(--sammo-font-size-normal)/1.3 var(--sammo-font-sans);
}
.tiptap-compat-controls {
display: none;
@@ -510,7 +510,7 @@ textarea:focus-visible {
.finance-title {
height: 25.47px;
text-align: center;
font-size: 19.6px;
font-size: var(--sammo-font-size-title);
line-height: 25.47px;
}
.diplomacy-title {
+14 -9
View File
@@ -555,6 +555,11 @@ const submitPriority = async (section: PrioritySectionKey) => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
/*
* The document contract belongs to this page only. An unscoped html/body rule
* here leaked a 21px line-height and a 500px min-width onto every other screen.
@@ -566,7 +571,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
margin: 0;
background: #000;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 21px;
}
@@ -576,7 +581,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
box-sizing: border-box;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 21px;
}
.sortable-compat-controls {
@@ -605,7 +610,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
}
.top-back-bar strong {
font-size: 24px;
font-size: var(--sammo-font-size-title);
line-height: 32px;
font-weight: 400;
}
@@ -653,7 +658,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
min-height: 21px;
padding: 0 12px;
color: #8e8e8e;
font-size: 12.25px;
font-size: var(--sammo-font-size-small);
line-height: 18.375px;
text-align: right;
box-sizing: border-box;
@@ -709,7 +714,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
min-height: 18.375px;
margin: 0;
color: #888;
font-size: 12.25px;
font-size: var(--sammo-font-size-small);
line-height: 18.375px;
text-align: right;
}
@@ -807,7 +812,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
}
.priority-description small {
font-size: 12.25px;
font-size: var(--sammo-font-size-small);
line-height: 18.375px;
}
@@ -867,7 +872,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
}
.drag-handle {
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.help-button {
@@ -879,7 +884,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
background: #444;
border: 1px solid #3d3d3d;
border-radius: 3px;
font-size: 12.25px;
font-size: var(--sammo-font-size-small);
line-height: 18.375px;
cursor: pointer;
}
@@ -896,7 +901,7 @@ const submitPriority = async (section: PrioritySectionKey) => {
border: 1px solid #777;
border-radius: 4px;
content: attr(data-text);
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 18px;
text-align: left;
white-space: pre-line;
+3 -3
View File
@@ -252,7 +252,7 @@ onMounted(() => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
}
@@ -261,7 +261,7 @@ onMounted(() => {
border-collapse: collapse;
padding: 0;
table-layout: fixed;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
word-break: break-all;
}
@@ -395,7 +395,7 @@ onMounted(() => {
}
.banner {
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.banner a {
@@ -422,7 +422,7 @@ onMounted(() => {
background-color: #302016;
background-image: var(--sammo-texture-walnut);
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.title-row,
@@ -443,7 +443,7 @@ onMounted(() => {
.title-row h1 {
margin: 0;
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.title-row nav,
@@ -615,7 +615,7 @@ th {
.detail-grid :deep(.panel-title) {
color: skyblue;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
font-weight: 500;
}
@@ -642,7 +642,7 @@ th {
.hall-battle-record__heading span {
color: #bbb;
font-size: 12px;
font-size: var(--sammo-font-size-small);
}
.hall-battle-record dl {
+7 -7
View File
@@ -256,7 +256,7 @@ onMounted(() => {
<style scoped>
.page-hint {
margin-top: 6px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.5);
}
@@ -279,7 +279,7 @@ onMounted(() => {
}
.placeholder {
font-size: 0.85rem;
font-size: var(--sammo-font-size-normal);
color: rgba(232, 221, 196, 0.7);
display: flex;
flex-direction: column;
@@ -295,7 +295,7 @@ onMounted(() => {
.public-table {
width: 100%;
border-collapse: collapse;
font-size: 0.8rem;
font-size: var(--sammo-font-size-small);
}
.public-table th,
@@ -306,7 +306,7 @@ onMounted(() => {
}
.public-table thead th {
font-size: 0.7rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.6);
text-transform: uppercase;
letter-spacing: 0.05em;
@@ -325,7 +325,7 @@ onMounted(() => {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
font-size: var(--sammo-font-size-small);
padding: 2px 4px;
margin-right: 6px;
border: 1px solid rgba(201, 164, 90, 0.4);
@@ -346,11 +346,11 @@ onMounted(() => {
background: rgba(16, 16, 16, 0.8);
color: rgba(232, 221, 196, 0.9);
padding: 6px 8px;
font-size: 0.75rem;
font-size: var(--sammo-font-size-small);
}
.list-count {
font-size: 0.7rem;
font-size: var(--sammo-font-size-small);
color: rgba(232, 221, 196, 0.6);
white-space: nowrap;
}
+3 -3
View File
@@ -424,7 +424,7 @@ onMounted(() => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.5;
}
@@ -464,13 +464,13 @@ onMounted(() => {
}
#vote-title {
font-size: 1.8em;
font-size: var(--sammo-font-size-title);
line-height: 1.5;
text-align: center;
}
#vote-old-title {
font-size: 1.5em;
font-size: var(--sammo-font-size-title);
line-height: 1.5;
text-align: center;
}
+17 -12
View File
@@ -375,6 +375,11 @@ const start = async () => {
</template>
<style scoped>
/* UI 보조 글자는 부모의 80%가 아니라 공통 소형 크기를 사용한다. */
small {
font-size: var(--sammo-font-size-small);
}
.legacy-page {
width: 100%;
max-width: 1200px;
@@ -383,7 +388,7 @@ const start = async () => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.3;
text-align: center;
}
@@ -392,7 +397,7 @@ const start = async () => {
text-align: left;
}
.tournament-guide {
font-size: 12px;
font-size: var(--sammo-font-size-small);
line-height: 14px;
}
.tournament-footer {
@@ -419,7 +424,7 @@ const start = async () => {
--legacy-button-height: 44px;
min-width: 72px;
padding: 10px 16px;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
.operator-row,
.state-row,
@@ -451,10 +456,10 @@ button:not(.legacy-button):focus-visible {
}
.operator-row span {
color: orange;
font-size: 24px;
font-size: var(--sammo-font-size-title);
}
.state-row {
font-size: 24px;
font-size: var(--sammo-font-size-title);
}
.state-row .type {
color: cyan;
@@ -463,7 +468,7 @@ button:not(.legacy-button):focus-visible {
min-height: 38px;
padding: 5px;
color: magenta;
font-size: 24px;
font-size: var(--sammo-font-size-title);
}
.fight {
padding: 8px;
@@ -473,7 +478,7 @@ button:not(.legacy-button):focus-visible {
margin: 0;
text-align: center;
color: orange;
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.fight p {
margin: 2px 10px;
@@ -496,7 +501,7 @@ button:not(.legacy-button):focus-visible {
padding: 4px;
background: #000;
color: orange;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
font-weight: 400;
text-align: center;
}
@@ -525,13 +530,13 @@ button:not(.legacy-button):focus-visible {
@media (max-width: 800px) {
.legacy-page {
max-width: 100%;
font-size: 13px;
font-size: var(--sammo-font-size-small);
}
.state-row {
font-size: 18px;
font-size: var(--sammo-font-size-emphasis);
}
.section-title {
font-size: 20px;
font-size: var(--sammo-font-size-title);
}
.group-tabs {
display: grid;
@@ -568,7 +573,7 @@ button:not(.legacy-button):focus-visible {
}
.tournament-guide {
padding: 10px;
font-size: 11px;
font-size: var(--sammo-font-size-small);
line-height: 16px;
}
.tournament-footer {
+3 -3
View File
@@ -225,7 +225,7 @@ onMounted(() => {
padding: 0;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: normal;
}
@@ -233,7 +233,7 @@ onMounted(() => {
border-collapse: collapse;
padding: 0;
color: #fff;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
}
/*
@@ -291,7 +291,7 @@ onMounted(() => {
.chart-title {
height: 34px;
font-size: 24px;
font-size: var(--sammo-font-size-title);
}
.chart-row {
+4 -4
View File
@@ -369,7 +369,7 @@ onMounted(() => {
color: #fff;
background: transparent;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
line-height: 1.5;
}
@@ -395,7 +395,7 @@ onMounted(() => {
.topBackBar h2 {
margin: 0;
font-size: 24px;
font-size: var(--sammo-font-size-title);
font-weight: 500;
line-height: 32px;
text-align: center;
@@ -455,7 +455,7 @@ onMounted(() => {
.makeTitle {
grid-column: 1/3;
padding: 0.15em;
font-size: 1.2em;
font-size: var(--sammo-font-size-emphasis);
line-height: 1.2;
}
@@ -521,7 +521,7 @@ onMounted(() => {
grid-column: 4/5;
grid-row: 1/3;
overflow: hidden;
font-size: 85%;
font-size: var(--sammo-font-size-small);
text-align: left;
}
+3 -3
View File
@@ -258,7 +258,7 @@ onMounted(async () => {
margin: 0 auto;
color: #fff;
font-family: var(--sammo-font-sans);
font-size: 14px;
font-size: var(--sammo-font-size-normal);
background-color: transparent;
}
@@ -279,7 +279,7 @@ onMounted(async () => {
.yearbook-title strong {
display: inline-block;
line-height: 32px;
font-size: 20px;
font-size: var(--sammo-font-size-title);
letter-spacing: 0.35em;
}
@@ -355,7 +355,7 @@ onMounted(async () => {
.nation-position table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
font-size: var(--sammo-font-size-normal);
text-align: center;
}
+62
View File
@@ -131,3 +131,65 @@ than forcing it into either family.
The CSS variables contain `/image/game/*` URLs but do not import or copy image
files. Caddy continues to own `/image/*`; Vite must not rewrite the image tree
as application assets.
## Game typography tiers
The game UI uses four starting sizes from `assets/styles/tokens.css`:
| Token suffix (`--sammo-font-size-`) | CSS size | Role |
| --- | --- | --- |
| `small` | 12px | Compact controls, metadata, secondary labels |
| `normal` | 14px | Body, tables, ordinary controls |
| `emphasis` | 16px | Emphasized names and section labels |
| `title` | 24px | Page and major section headings |
This is an intentional Core UX policy adopted on 2026-09-15. It is a starting
policy for verified UI, not a claim that every rendered glyph has one of four
computed sizes. Existing geometry and media queries remain owned by each page.
A breakpoint can choose another tier (for example, the personnel nation heading
uses 16px on its narrow layout and 24px on its wide layout).
Use the tokens for new UI. Do not round arbitrary content or all remaining
relative sizes automatically. Before moving an existing label to a tier, compare
actual Chromium screenshots and text geometry at 500px and 1000px, with the
same font, data, DPR and zoom. Also check the real mobile viewport modes: a
390px-wide device scales a 500px layout to approximately 78% and a 1000px layout
to 39%; these do not change the CSS token values. A page with a fixed 1000px
minimum width can still render at about 39% in 500px mode because Chromium
fits the overflowing content. Record `visualViewport.scale` for each route;
do not infer every route's scale from the selected mode alone.
### Preserved exceptions
- Battle log `.small_war_log .name_plate` remains `0.75em`, and `.crew_plate`
remains `90%`. Legacy inline `0.9em` conversions and zero-sized hidden markers
remain unchanged. At a 14px parent these are 10.5px and 12.6px respectively;
nested content must be calculated from its actual parent.
- NPC possession names retain their existing length rule: 4 and 7 characters
render at 16px, and the existing `length >= 9` branch renders at 12px. Do not
remove the smaller branch or enlarge long names to match adjacent labels.
- Player HTML, editor size choices, and personal CSS remain content/user settings.
Scoped `small` rules normalize UI metadata without rewriting injected HTML.
### Deferred conversions
| Area | Preserved size | Reason |
| --- | --- | --- |
| Chief overview compact rows and turn indices | `0.55rem` (8.8px) | 12px text overlaps the existing 11.25px rows |
| Chief compact header/name | `0.65rem` / `0.6rem` | Preserve the same dense card contract |
| Narrow personnel chief name / lock label | 15px / 10px | Enlarging them reduces visible maximum-length names |
| Best generals / hall name and nation cells | 11px; inner `small` 95% | 12px worsens overflow in the fixed name cells |
| General selection and main nation basic card | Existing local/inherited sizes | Maximum-width names already overflow; conversion is deferred |
User-created general names and founded/renamed nation names are limited by
legacy width 18 (CJK counts 2, ASCII counts 1): test both 9 CJK characters and
18 wide ASCII characters. Selection-pool and scenario names have a different
source contract and are not proven bounded by that user-input guard.
Existing ellipsis, horizontal scrolling, and pre-existing maximum-name overflow
are not fixed by these tokens. The deferred areas need a separate layout decision
before their fonts are enlarged. The `typographyPolicy.spec.ts` fixture tests
protect the safe main labels, personnel breakpoints, dense chief rows, NPC
length branches, and battle log ratios. They use mocked read responses and do
not constitute live game or public deployment verification.